test: rework tests using @nuxt/test-utils (#3308)

This commit is contained in:
pooya parsa 2022-02-18 19:14:57 +01:00 committed by GitHub
parent 87eb7d0d39
commit 12a95ad86c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 111 additions and 379 deletions

View File

@ -33,7 +33,7 @@ jobs:
- name: Lint (docs)
run: yarn lint:docs
test:
test-fixtures:
runs-on: ${{ matrix.os }}
strategy:
@ -51,19 +51,16 @@ jobs:
- name: Install dependencies
run: yarn --immutable
- name: Build
run: yarn build
- name: Stub
run: yarn stub
- name: Test (unit)
run: yarn test:unit
- name: Test (presets)
run: yarn test:presets
- name: Test (fixtures)
run: yarn test:fixtures
- name: Test (types)
run: yarn test:types
test-bridge:
test-fixtures-webpack:
runs-on: ${{ matrix.os }}
strategy:
@ -77,8 +74,30 @@ jobs:
with:
node-version: ${{ matrix.node }}
cache: 'yarn'
cache-dependency-path: |
yarn.lock
- name: Install dependencies
run: yarn --immutable
- name: Stub
run: yarn stub
- name: Test (fixtures)
run: yarn test:fixtures:webpack
test-types:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
node: [14]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node }}
cache: 'yarn'
- name: Install dependencies
run: yarn --immutable
@ -86,17 +105,15 @@ jobs:
- name: Build
run: yarn build
- name: Test with webpack
run: yarn test:bridge:webpack
- name: Test with vite
run: yarn test:bridge:vite
- name: Test (types)
run: yarn test:types
build-release:
needs:
- test
- test-bridge
- lint
- test-fixtures
- test-fixtures-webpack
- test-types
runs-on: ${{ matrix.os }}
strategy:

View File

@ -14,7 +14,7 @@
"@docus/theme": "1.2.2",
"@nuxt/typescript-build": "^2.1.0",
"fs-extra": "^10.0.0",
"jiti": "^1.12.15",
"jiti": "^1.13.0",
"pathe": "^0.2.0",
"rimraf": "^3.0.2",
"scule": "^0.2.1",

View File

@ -10,30 +10,27 @@
],
"scripts": {
"build": "FORCE_COLOR=1 lerna run prepack --stream --no-prefix",
"stub": "lerna run prepack -- --stub",
"release": "yarn && yarn lint && FORCE_COLOR=1 lerna publish -m \"chore: release\" && yarn stub",
"nuxi": "./node_modules/.bin/nuxi",
"nuxt": "./node_modules/.bin/nuxi",
"play": "yarn run nuxi dev playground",
"example": "yarn workspace example-$0 dev",
"example:build": "yarn workspace example-$0 build",
"lint": "eslint --ext .vue,.ts,.js,.mjs .",
"lint:docs": "./node_modules/.bin/markdownlint ./",
"test": "yarn lint && yarn test:presets",
"test:presets": "vitest test/presets",
"test:bridge:webpack": "TEST_BRIDGE=1 yarn test:presets",
"test:bridge:vite": "TEST_BRIDGE_VITE=1 TEST_BRIDGE=1 yarn test:presets",
"nuxi": "./node_modules/.bin/nuxi",
"nuxt": "./node_modules/.bin/nuxi",
"play": "yarn run nuxi dev playground",
"release": "yarn && yarn lint && FORCE_COLOR=1 lerna publish -m \"chore: release\" && yarn stub",
"stub": "lerna run prepack -- --stub",
"test:fixtures": "JITI_ESM_RESOLVE=1 vitest test",
"test:fixtures:webpack": "TEST_WITH_WEBPACK=1 yarn test:fixtures",
"test:types": "yarn run nuxi prepare test/fixtures/basic && cd test/fixtures/basic && npx vue-tsc --noEmit",
"test:unit": "vitest packages",
"test:utils": "vitest run test/examples",
"test:unit": "JITI_ESM_RESOLVE=1 yarn vitest packages",
"version": "yarn && git add yarn.lock"
},
"resolutions": {
"nuxt3": "workspace:./packages/nuxt3",
"@nuxt/ui": "npm:@nuxt/ui-edge@latest",
"unbuild": "^0.6.9",
"jiti": "^1.13.0",
"nitropack-dev": "link:../nitropack",
"jiti": "^1.12.15"
"nuxt3": "workspace:./packages/nuxt3",
"unbuild": "^0.6.9"
},
"devDependencies": {
"@iconify-json/carbon": "^1.1.1",
@ -50,7 +47,7 @@
"execa": "^6.1.0",
"expect-type": "^0.13.0",
"globby": "^13.1.1",
"jiti": "^1.12.15",
"jiti": "^1.13.0",
"lerna": "^4.0.0",
"markdownlint-cli": "^0.31.1",
"miniflare": "^1.4.1",
@ -58,12 +55,12 @@
"pathe": "^0.2.0",
"typescript": "^4.5.5",
"unbuild": "^0.6.9",
"vitest": "^0.3.6",
"vitest": "^0.4.1",
"vue-router": "next",
"vue-tsc": "^0.31.4"
},
"packageManager": "yarn@3.1.1",
"engines": {
"node": "^14.16.0 || ^16.11.0 || ^17.0.0"
}
},
"packageManager": "yarn@3.1.1"
}

View File

@ -19,7 +19,7 @@
"defu": "^5.0.1",
"globby": "^13.1.1",
"hash-sum": "^2.0.0",
"jiti": "^1.12.15",
"jiti": "^1.13.0",
"knitwork": "^0.1.0",
"lodash.template": "^4.5.0",
"mlly": "^0.4.3",

View File

@ -3,7 +3,7 @@ import { fileURLToPath } from 'url'
import { basename, dirname, resolve, join, normalize, isAbsolute } from 'pathe'
import { globby } from 'globby'
import { useNuxt } from './context'
import { tryResolveModule } from '.'
import { tryResolveModule } from './internal/cjs'
export interface ResolvePathOptions {
/** Base for resolving paths from. Default is Nuxt rootDir. */

View File

@ -47,7 +47,7 @@
"hookable": "^5.1.1",
"http-proxy": "^1.18.1",
"is-primitive": "^3.0.1",
"jiti": "^1.12.15",
"jiti": "^1.13.0",
"knitwork": "^0.1.0",
"listhen": "^0.2.6",
"mime": "^3.0.0",

View File

@ -33,7 +33,7 @@
"destr": "^1.1.0",
"execa": "^6.1.0",
"flat": "^5.0.2",
"jiti": "^1.12.15",
"jiti": "^1.13.0",
"listhen": "^0.2.6",
"mlly": "^0.4.3",
"mri": "^1.2.0",

View File

@ -22,7 +22,7 @@
"c12": "^0.1.3",
"create-require": "^1.1.1",
"defu": "^5.0.1",
"jiti": "^1.12.15",
"jiti": "^1.13.0",
"pathe": "^0.2.0",
"scule": "^0.2.1",
"std-env": "^3.0.1",

View File

@ -18,13 +18,13 @@
"defu": "^5.0.1",
"execa": "^6.1.0",
"get-port-please": "^2.3.0",
"jiti": "^1.12.15",
"jiti": "^1.13.0",
"ohmyfetch": "^0.4.15"
},
"devDependencies": {
"playwright": "^1.19.1",
"unbuild": "latest",
"vitest": "^0.3.6"
"vitest": "^0.4.1"
},
"peerDependencies": {
"vue": "3.2.31"

View File

@ -21,7 +21,7 @@ function checkPort (port: number, host: string): Promise<number|false> {
export async function listen () {
const ctx = useTestContext()
const host = process.env.HOST || '0.0.0.0'
const port = await getPort({ host })
const port = await getPort({ host, random: true })
ctx.url = 'http://localhost:' + port
execa('node', [

13
test/basic.test.ts Normal file
View File

@ -0,0 +1,13 @@
import { fileURLToPath } from 'url'
import { describe, expect, it } from 'vitest'
import { setup, $fetch } from '@nuxt/test-utils'
describe('fixtures:basic', async () => {
await setup({
rootDir: fileURLToPath(new URL('./fixtures/basic', import.meta.url)),
server: true
})
it('Render hello world', async () => {
expect(await $fetch('/')).to.contain('Hello Nuxt 3!')
})
})

14
test/bridge.test.ts Normal file
View File

@ -0,0 +1,14 @@
import { fileURLToPath } from 'url'
import { describe, expect, it } from 'vitest'
import { setup, $fetch } from '@nuxt/test-utils'
describe('fixtures:bridge', async () => {
await setup({
rootDir: fileURLToPath(new URL('./fixtures/bridge', import.meta.url)),
server: true
})
it('Render hello world', async () => {
expect(await $fetch('/')).to.contain('Hello Vue 2!')
})
})

View File

@ -1,17 +0,0 @@
import { fileURLToPath } from 'url'
import { resolve } from 'path'
import { describe, expect, it } from 'vitest'
import { setup, $fetch } from '@nuxt/test-utils'
const examplesDir = fileURLToPath(new URL('../../examples', import.meta.url))
await setup({
rootDir: resolve(examplesDir, 'hello-world'),
server: true
})
describe('examples:hello-world', () => {
it('Render hello world test', async () => {
expect(await $fetch('/')).to.contain('Hello Nuxt 3!')
})
})

View File

@ -3,6 +3,7 @@ import { addComponent } from '@nuxt/kit'
export default defineNuxtConfig({
buildDir: process.env.NITRO_BUILD_DIR,
vite: !process.env.TEST_WITH_WEBPACK,
nitro: {
output: { dir: process.env.NITRO_OUTPUT_DIR }
},

View File

@ -3,7 +3,7 @@
<Head>
<Title>Basic fixture</Title>
</Head>
<h1>Hello Vue 3</h1>
<h1>Hello Nuxt 3!</h1>
<div>Config: {{ $config.testConfig }}</div>
<CustomComponent />
</div>

View File

@ -20,6 +20,6 @@ export default defineNuxtConfig({
},
bridge: {
meta: true,
vite: !!process.env.TEST_BRIDGE_VITE
vite: !process.env.TEST_WITH_WEBPACK
}
})

View File

@ -1,83 +0,0 @@
import { pathToFileURL } from 'url'
import { resolve } from 'pathe'
import destr from 'destr'
import { listen, Listener } from 'listhen'
import { $fetch } from 'ohmyfetch'
import { execa } from 'execa'
import { expect, it, beforeAll, afterAll } from 'vitest'
import { fixtureDir, resolveWorkspace } from '../utils'
const isBridge = Boolean(process.env.TEST_BRIDGE)
interface Context {
rootDir: string
outDir: string
fetch: (url:string) => Promise<any>
server?: Listener
}
export function importModule (path) {
return import(pathToFileURL(path).href)
}
export function setupTest (preset) {
const fixture = isBridge ? 'bridge' : 'basic'
const rootDir = fixtureDir(fixture)
const buildDir = resolve(rootDir, '.nuxt-' + preset)
const ctx: Context = {
rootDir,
outDir: resolve(buildDir, 'output'),
fetch: url => $fetch(url, { baseURL: ctx.server!.url })
}
beforeAll(async () => {
const nuxtCLI = isBridge
? resolve(ctx.rootDir, 'node_modules/nuxt-edge/bin/nuxt.js')
: resolveWorkspace('packages/nuxi/bin/nuxi.mjs')
await execa('node', [nuxtCLI, 'build', ctx.rootDir], {
env: {
NITRO_PRESET: preset,
NITRO_BUILD_DIR: buildDir,
NITRO_OUTPUT_DIR: ctx.outDir,
NODE_ENV: 'production'
}
})
}, (isBridge ? 120 : 60) * 1000)
afterAll(async () => {
if (ctx.server) {
await ctx.server.close()
}
})
return ctx
}
export async function startServer (ctx, handle) {
ctx.server = await listen(handle)
}
export function testNitroBehavior (_ctx, getHandler) {
let handler
it('setup handler', async () => {
handler = await getHandler()
})
it('SSR Works', async () => {
const { data } = await handler({ url: '/' })
expect(data).to.have.string('Hello Vue')
})
it('API Works', async () => {
const { data: helloData } = await handler({ url: '/api/hello' })
const { data: heyData } = await handler({ url: '/api/hey' })
expect(destr(helloData)).to.have.string('Hello API')
expect(destr(heyData)).to.deep.equal({
foo: 'bar',
baz: 'qux'
})
})
}

View File

@ -1,26 +0,0 @@
import { promises as fsp } from 'fs'
import { resolve } from 'pathe'
import { Miniflare } from 'miniflare'
import { describe } from 'vitest'
import { setupTest, testNitroBehavior } from './_tests'
// TODO: fix SyntaxError: Unexpected end of input on script executation
describe('nitro:preset:cloudflare', () => {
const ctx = setupTest('cloudflare')
testNitroBehavior(ctx, async () => {
const script = await fsp.readFile(resolve(ctx.outDir, 'server/index.mjs'), 'utf-8')
const mf = new Miniflare({ script })
return async ({ url, headers, method, body }) => {
const data = await mf.dispatchFetch('http://localhost' + url, {
headers: headers || {},
method: method || 'GET',
redirect: null,
body: body || null
}).then(r => r.text())
return { data }
}
})
})

View File

@ -1,57 +0,0 @@
import { resolve } from 'pathe'
import { describe } from 'vitest'
import type { APIGatewayProxyEvent, APIGatewayProxyEventV2 } from 'aws-lambda'
import { setupTest, testNitroBehavior, importModule } from './_tests'
describe('nitro:preset:lambda', () => {
const ctx = setupTest('lambda')
// Lambda v1 paylod
testNitroBehavior(ctx, async () => {
const { handler } = await importModule(resolve(ctx.outDir, 'server/index.mjs'))
return async ({ url: rawRelativeUrl, headers, method, body }) => {
// creating new URL object to parse query easier
const url = new URL(`https://example.com${rawRelativeUrl}`)
const queryStringParameters = Object.fromEntries(url.searchParams.entries())
const event: Partial<APIGatewayProxyEvent> = {
resource: '/my/path',
path: url.pathname,
headers: headers || {},
httpMethod: method || 'GET',
queryStringParameters,
body: body || ''
}
const res = await handler(event)
return {
data: res.body
}
}
})
// Lambda v2 paylod
testNitroBehavior(ctx, async () => {
const { handler } = await importModule(resolve(ctx.outDir, 'server/index.mjs'))
return async ({ url: rawRelativeUrl, headers, method, body }) => {
// creating new URL object to parse query easier
const url = new URL(`https://example.com${rawRelativeUrl}`)
const queryStringParameters = Object.fromEntries(url.searchParams.entries())
const event: Partial<APIGatewayProxyEventV2> = {
rawPath: url.pathname,
headers: headers || {},
requestContext: {
...Object.fromEntries([['accountId'], ['apiId'], ['domainName'], ['domainPrefix']]),
http: {
path: url.pathname,
protocol: 'http',
...Object.fromEntries([['userAgent'], ['sourceIp']]),
method: method || 'GET'
}
},
queryStringParameters,
body: body || ''
}
const res = await handler(event)
return {
data: res.body
}
}
})
})

View File

@ -1,17 +0,0 @@
import { resolve } from 'pathe'
import { describe } from 'vitest'
import { startServer, setupTest, testNitroBehavior, importModule } from './_tests.js'
describe('nitro:preset:node', () => {
const ctx = setupTest('node')
testNitroBehavior(ctx, async () => {
const { handle } = await importModule(resolve(ctx.outDir, 'server/index.mjs'))
await startServer(ctx, handle)
return async ({ url }) => {
const data = await ctx.fetch(url)
return {
data
}
}
})
})

View File

@ -1,18 +0,0 @@
import { resolve } from 'pathe'
import { describe } from 'vitest'
import { setupTest, startServer, testNitroBehavior, importModule } from './_tests'
describe('nitro:preset:vercel', () => {
const ctx = setupTest('vercel')
testNitroBehavior(ctx, async () => {
const handle = await importModule(resolve(ctx.outDir, 'functions/node/server/index.mjs'))
.then(r => r.default || r)
await startServer(ctx, handle)
return async ({ url }) => {
const data = await ctx.fetch(url)
return {
data
}
}
})
})

View File

@ -3,7 +3,7 @@ import { describe, it } from 'vitest'
import type { Ref } from 'vue'
import { useRouter as vueUseRouter } from 'vue-router'
import { defineNuxtConfig } from '~~/../../../packages/nuxt3/src'
import { defineNuxtConfig } from '~~/../../packages/nuxt3/src'
import { useRouter } from '#imports'
import { isVue3 } from '#app'

View File

@ -1,92 +0,0 @@
import { existsSync, readFileSync, writeFileSync, rmSync, mkdirSync } from 'fs'
import { execSync } from 'child_process'
import { resolve, dirname } from 'pathe'
import defu from 'defu'
import hash from 'object-hash'
import { execa, Options as ExecaOptions } from 'execa'
import { createCommonJS } from 'mlly'
const cjs = createCommonJS(import.meta.url)
export function resolveWorkspace (name: string) {
return resolve(cjs.__dirname, '../', name)
}
export function fixtureDir (name: string) {
return resolve(cjs.__dirname, 'fixtures', name)
}
export async function execNuxtCLI (args: string[], opts: ExecaOptions) {
const nuxtCLI = resolveWorkspace('packages/nuxi/bin/nuxi.mjs')
await execa('node', [nuxtCLI, ...args], opts)
}
export async function loadFixture (opts: any, unhashedConfig?: any) {
const buildId = hash(opts)
const buildDir = resolve(opts.rootDir, '.nuxt', buildId)
const { loadNuxt } = await import('@nuxt/kit')
const nuxt = await loadNuxt(defu(opts, { config: { buildDir, ...unhashedConfig } }))
return nuxt
}
export async function buildFixture (opts) {
const buildId = hash(opts)
const buildDir = resolve(opts.rootDir, '.nuxt', buildId)
const lockFile = resolve(opts.rootDir, `.build-${buildId}.lock`)
mkdirpSync(dirname(lockFile))
await waitWhile(() => isAlive(readSync(lockFile)))
writeFileSync(lockFile, process.pid + '', 'utf8')
try {
const integrity = gitHead() // TODO: Calculate hash from project source
const integrityFile = resolve(buildDir, '.integrity')
const lastBuildIntegrity = readSync(integrityFile)
if (integrity !== lastBuildIntegrity) {
const nuxt = await loadFixture(opts)
const { buildNuxt } = await import('@nuxt/kit')
await buildNuxt(nuxt)
await nuxt.close()
await writeFileSync(integrityFile, integrity)
}
} finally {
existsSync(lockFile) && rmSync(lockFile)
}
}
function mkdirpSync (dir: string) {
if (!existsSync(dir)) {
mkdirpSync(dirname(dir))
mkdirSync(dir)
}
}
function readSync (file) {
return existsSync(file) ? readFileSync(file, 'utf8') : null
}
function isAlive (pid) {
try {
process.kill(pid, 0)
return true
} catch (e) {
return e.code === 'EPERM'
}
}
function waitWhile (check, interval = 100, timeout = 30000) {
return new Promise((resolve, reject) => {
const t = setTimeout(() => reject(new Error('Timeout')), timeout)
const i = setInterval(() => {
if (!check()) {
clearTimeout(t)
clearInterval(i)
resolve(true)
}
}, interval)
})
}
function gitHead () {
return execSync('git rev-parse HEAD').toString('utf8').trim()
}

View File

@ -2997,7 +2997,7 @@ __metadata:
defu: ^5.0.1
globby: ^13.1.1
hash-sum: ^2.0.0
jiti: ^1.12.15
jiti: ^1.13.0
knitwork: ^0.1.0
lodash.template: ^4.5.0
mlly: ^0.4.3
@ -3094,7 +3094,7 @@ __metadata:
hookable: ^5.1.1
http-proxy: ^1.18.1
is-primitive: ^3.0.1
jiti: ^1.12.15
jiti: ^1.13.0
knitwork: ^0.1.0
listhen: ^0.2.6
mime: ^3.0.0
@ -3161,7 +3161,7 @@ __metadata:
c12: ^0.1.3
create-require: ^1.1.1
defu: ^5.0.1
jiti: ^1.12.15
jiti: ^1.13.0
pathe: ^0.2.0
scule: ^0.2.1
std-env: ^3.0.1
@ -3273,11 +3273,11 @@ __metadata:
defu: ^5.0.1
execa: ^6.1.0
get-port-please: ^2.3.0
jiti: ^1.12.15
jiti: ^1.13.0
ohmyfetch: ^0.4.15
playwright: ^1.19.1
unbuild: latest
vitest: ^0.3.6
vitest: ^0.4.1
peerDependencies:
vue: 3.2.31
languageName: unknown
@ -13245,12 +13245,12 @@ __metadata:
languageName: node
linkType: hard
"jiti@npm:^1.12.15":
version: 1.12.15
resolution: "jiti@npm:1.12.15"
"jiti@npm:^1.13.0":
version: 1.13.0
resolution: "jiti@npm:1.13.0"
bin:
jiti: bin/jiti.js
checksum: 8e53259f427eb7a7c0cd834c284e18cd05760cb7f0f15b4971ab11874e8b0e7b15fc1f13000eb3a8c7f804ff15637a283d6595858811d61effe8c5aeaacb02ac
checksum: 1e74d99b9a551df952057b8bce56f744caccca0044aa05021643e6ce0837ed47bc2d5b290fecb462e32988c6bf318afe77e341310c5ecf6bb63c19c80c929cb6
languageName: node
linkType: hard
@ -15513,7 +15513,7 @@ __metadata:
execa: ^6.1.0
flat: ^5.0.2
fsevents: ~2.3.2
jiti: ^1.12.15
jiti: ^1.13.0
listhen: ^0.2.6
mlly: ^0.4.3
mri: ^1.2.0
@ -15577,7 +15577,7 @@ __metadata:
execa: ^6.1.0
expect-type: ^0.13.0
globby: ^13.1.1
jiti: ^1.12.15
jiti: ^1.13.0
lerna: ^4.0.0
markdownlint-cli: ^0.31.1
miniflare: ^1.4.1
@ -15585,7 +15585,7 @@ __metadata:
pathe: ^0.2.0
typescript: ^4.5.5
unbuild: ^0.6.9
vitest: ^0.3.6
vitest: ^0.4.1
vue-router: next
vue-tsc: ^0.31.4
languageName: unknown
@ -20774,10 +20774,10 @@ __metadata:
languageName: node
linkType: hard
"tinyspy@npm:^0.2.10":
version: 0.2.10
resolution: "tinyspy@npm:0.2.10"
checksum: 7c4d89f603f5e12b335f5a97811b11712a6cc44565c2d1c1d76ccf6a0cb9c2b0ca8b7eb84f6cdabd0a49149fb9dababaefadab43bec6d8252dfc797a205f48bd
"tinyspy@npm:^0.3.0":
version: 0.3.0
resolution: "tinyspy@npm:0.3.0"
checksum: 8776bc6b29d190195c12a0769f8e9f0ae7756ff4a8de070b181a5962fc17fdffb1b95043501f7970cea007d4ddf41559c5b376576c8d244ccf35d04a49e664fb
languageName: node
linkType: hard
@ -21756,16 +21756,16 @@ __metadata:
languageName: node
linkType: hard
"vitest@npm:^0.3.6":
version: 0.3.6
resolution: "vitest@npm:0.3.6"
"vitest@npm:^0.4.1":
version: 0.4.1
resolution: "vitest@npm:0.4.1"
dependencies:
"@types/chai": ^4.3.0
"@types/chai-subset": ^1.3.3
chai: ^4.3.6
local-pkg: ^0.4.1
tinypool: ^0.1.2
tinyspy: ^0.2.10
tinyspy: ^0.3.0
vite: ^2.8.2
peerDependencies:
"@vitest/ui": "*"
@ -21783,7 +21783,7 @@ __metadata:
optional: true
bin:
vitest: vitest.mjs
checksum: 3079f19837384b96c74825fbd4e7dc48b018f48d9317665aad193c7339a438d7e5c47e4f7f1b121de162a9dbbb80a40df5ec3fc9f17321560de1c667b056e74e
checksum: f350464cd674612151ea6e63fd37401156ff9d3fafd3f663595beb1dbe19658fb1f7f345253250f3a9c5dff7febf8fc3284f94d46ed167f37141bd609d424e05
languageName: node
linkType: hard