Skip to content

Commit

Permalink
Merge branch 'main' into chore/node18-20
Browse files Browse the repository at this point in the history
  • Loading branch information
ghiscoding authored Oct 16, 2023
2 parents a3e6c44 + 2396858 commit 6c4a64b
Show file tree
Hide file tree
Showing 19 changed files with 123 additions and 101 deletions.
9 changes: 4 additions & 5 deletions docs/guide/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,11 @@ Run only [benchmark](https://vitest.dev/guide/features.html#benchmarking-experim
| `--ui` | Enable UI |
| `--open` | Open the UI automatically if enabled (default: `true`) |
| `--api [api]` | Serve API, available options: `--api.port <port>`, `--api.host [host]` and `--api.strictPort` |
| `--threads` | Enable Threads (default: `true`) |
| `--single-thread` | Run tests inside a single thread, requires --threads (default: `false`) |
| `--experimental-vm-threads` | Run tests in a worker pool using VM isolation (default: `false`) |
| `--experimental-vm-worker-memory-limit` | Set the maximum allowed memory for a worker. When reached, a new worker will be created instead |
| `--pool <pool>` | Specify pool, if not running in the browser (default: `threads`) |
| `--poolOptions <options>` | Specify pool options |
| `--poolOptions.threads.isolate` | Isolate tests in threads pool (default: `true`) |
| `--poolOptions.forks.isolate` | Isolate tests in forks pool (default: `true`) |
| `--silent` | Silent console output from tests |
| `--isolate` | Isolate environment for each test file (default: `true`) |
| `--reporter <name>` | Select reporter: `default`, `verbose`, `dot`, `junit`, `json`, or a path to a custom reporter |
| `--outputFile <filename/-s>` | Write test results to a file when the `--reporter=json` or `--reporter=junit` option is also specified <br /> Via [cac's dot notation] you can specify individual outputs for multiple reporters |
| `--coverage` | Enable coverage report |
Expand Down
8 changes: 5 additions & 3 deletions examples/playwright/test/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ import { chromium } from 'playwright'
import type { Browser, Page } from 'playwright'
import { expect } from '@playwright/test'

const PORT = 3001

// unstable in Windows, TODO: investigate
describe.runIf(process.platform !== 'win32')('basic', async () => {
let server: PreviewServer
let browser: Browser
let page: Page

beforeAll(async () => {
server = await preview({ preview: { port: 3000 } })
browser = await chromium.launch()
server = await preview({ preview: { port: PORT } })
browser = await chromium.launch({ headless: true })
page = await browser.newPage()
})

Expand All @@ -25,7 +27,7 @@ describe.runIf(process.platform !== 'win32')('basic', async () => {
})

test('should change count when button clicked', async () => {
await page.goto('http://localhost:3000')
await page.goto(`http://localhost:${PORT}`)
const button = page.getByRole('button', { name: /Clicked/ })
await expect(button).toBeVisible()

Expand Down
32 changes: 14 additions & 18 deletions examples/puppeteer/test/basic.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { afterAll, beforeAll, describe, expect, test } from 'vitest'
import { preview } from 'vite'
import type { PreviewServer } from 'vite'
import puppeteer from 'puppeteer'
import { launch } from 'puppeteer'
import type { Browser, Page } from 'puppeteer'

const PORT = 3000

describe('basic', async () => {
let server: PreviewServer
let browser: Browser
let page: Page

beforeAll(async () => {
server = await preview({ preview: { port: 3000 } })
browser = await puppeteer.launch()
server = await preview({ preview: { port: PORT } })
browser = await launch({ headless: true })
page = await browser.newPage()
})

Expand All @@ -22,22 +24,16 @@ describe('basic', async () => {
})
})

// TODO make more stable
test.skip('should have the correct title', async () => {
try {
await page.goto('http://localhost:3000')
const button = (await page.$('#btn'))!
expect(button).toBeDefined()
test('should have the correct title', async () => {
await page.goto(`http://localhost:${PORT}`)
const button = (await page.$<HTMLButtonElement>('#btn'))!
expect(button).toBeDefined()

let text = await page.evaluate(btn => btn.textContent, button)
expect(text).toBe('Clicked 0 time(s)')
let text = await page.evaluate(btn => btn.textContent, button)
expect(text).toBe('Clicked 0 time(s)')

await button.click()
text = await page.evaluate(btn => btn.textContent, button)
}
catch (e) {
console.error(e)
expect(e).toBeUndefined()
}
await button.click()
text = await page.evaluate(btn => btn.textContent, button)
expect(text).toBe('Clicked 1 time(s)')
}, 60_000)
})
1 change: 0 additions & 1 deletion packages/runner/src/collect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ export async function collectTests(paths: string[], runner: VitestRunner): Promi
const error = processError(e)
file.result = {
state: 'fail',
error,
errors: [error],
}
}
Expand Down
7 changes: 1 addition & 6 deletions packages/runner/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,10 @@ export async function runTest(test: Test | Custom, runner: VitestRunner) {
if (test.result.state === 'pass') {
const error = processError(new Error('Expect test to fail'))
test.result.state = 'fail'
test.result.error = error
test.result.errors = [error]
}
else {
test.result.state = 'pass'
test.result.error = undefined
test.result.errors = undefined
}
}
Expand All @@ -248,7 +246,6 @@ function failTask(result: TaskResult, err: unknown, diffOptions?: DiffOptions) {
: [err]
for (const e of errors) {
const error = processError(e, diffOptions)
result.error ??= error
result.errors ??= []
result.errors.push(error)
}
Expand Down Expand Up @@ -333,9 +330,8 @@ export async function runSuite(suite: Suite, runner: VitestRunner) {
if (suite.mode === 'run') {
if (!hasTests(suite)) {
suite.result.state = 'fail'
if (!suite.result.error) {
if (!suite.result.errors?.length) {
const error = processError(new Error(`No test found in suite ${suite.name}`))
suite.result.error = error
suite.result.errors = [error]
}
}
Expand Down Expand Up @@ -370,7 +366,6 @@ export async function runFiles(files: File[], runner: VitestRunner) {
const error = processError(new Error(`No test suite found in file ${file.filepath}`))
file.result = {
state: 'fail',
error,
errors: [error],
}
}
Expand Down
4 changes: 4 additions & 0 deletions packages/runner/src/suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import { collectTask, collectorContext, createTestContext, runWithSuite, withTim
import { getHooks, setFixture, setFn, setHooks } from './map'
import type { FixtureItem } from './fixture'
import { mergeContextFixtures, withFixtures } from './fixture'
import { getCurrentTest } from './test-state'

// apis
export const suite = createSuite()
export const test = createTest(
function (name: string | Function, fn?: TestFunction, options?: number | TestOptions) {
if (getCurrentTest())
throw new Error('Nested tests are not allowed')

getCurrentSuite().test.fn.call(this, formatName(name), fn, options)
},
)
Expand Down
4 changes: 0 additions & 4 deletions packages/runner/src/types/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ export interface TaskResult {
duration?: number
startTime?: number
heap?: number
/**
* @deprecated Use "errors" instead
*/
error?: ErrorWithDiff
errors?: ErrorWithDiff[]
htmlError?: string
hooks?: Partial<Record<keyof SuiteHooks, TaskState>>
Expand Down
1 change: 0 additions & 1 deletion packages/runner/src/utils/collect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ function checkAllowOnly(task: TaskBase, allowOnly?: boolean) {
const error = processError(new Error('[Vitest] Unexpected .only modifier. Remove it or pass --allowOnly argument to bypass this error'))
task.result = {
state: 'fail',
error,
errors: [error],
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/utils/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export function clone<T>(
if (Array.isArray(val)) {
out = Array((k = val.length))
seen.set(val, out)
while (k--) out[k] = clone(val[k], seen)
while (k--) out[k] = clone(val[k], seen, options)
return out as any
}

Expand All @@ -110,7 +110,7 @@ export function clone<T>(
const descriptor = Object.getOwnPropertyDescriptor(val, k)
if (!descriptor)
continue
const cloned = clone((val as any)[k], seen)
const cloned = clone((val as any)[k], seen, options)
if ('get' in descriptor) {
Object.defineProperty(out, k, {
...descriptor,
Expand Down
22 changes: 22 additions & 0 deletions packages/vite-node/src/hmr/hmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,28 @@ export function createHotContext(
addToMap(newListeners)
},

// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
// @ts-ignore added in vite 5
off<T extends string>(
event: T,
cb: (payload: InferCustomEventPayload<T>) => void,
) {
const removeFromMap = (map: Map<string, any[]>) => {
const existing = map.get(event)
if (existing === undefined)
return

const pruned = existing.filter(l => l !== cb)
if (pruned.length === 0) {
map.delete(event)
return
}
map.set(event, pruned)
}
removeFromMap(maps.customListenersMap)
removeFromMap(newListeners)
},

send<T extends string>(event: T, data?: InferCustomEventPayload<T>): void {
maps.messageBuffer.push(JSON.stringify({ type: 'custom', event, data }))
sendMessageBuffer(runner, emitter)
Expand Down
3 changes: 0 additions & 3 deletions packages/vitest/src/api/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,6 @@ class WebSocketReporter implements Reporter {
getSourceMap: file => project.getBrowserSourceMapModuleById(file),
}

// TODO remove after "error" deprecation is removed
if (result?.error && !isPrimitive(result.error))
result.error.stacks = parseErrorStacktrace(result.error, parserOptions)
result?.errors?.forEach((error) => {
if (!isPrimitive(error))
error.stacks = parseErrorStacktrace(error, parserOptions)
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/node/reporters/junit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export class JUnitReporter implements Reporter {
await this.logger.log('<skipped/>')

if (task.result?.state === 'fail') {
const errors = task.result.errors?.length ? task.result.errors : [task.result.error]
const errors = task.result.errors || []
for (const error of errors) {
await this.writeElement('failure', {
message: error?.message,
Expand Down
64 changes: 32 additions & 32 deletions packages/vitest/src/runtime/runners/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,32 @@ async function runBenchmarkSuite(suite: Suite, runner: VitestRunner) {

if (benchmarkGroup.length) {
const defer = createDefer()
const benchmarkMap: Record<string, Benchmark> = {}
suite.result = {
state: 'run',
startTime: start,
benchmark: createBenchmarkResult(suite.name),
}
updateTask(suite)
benchmarkGroup.forEach((benchmark, idx) => {

const addBenchTaskListener = (task: InstanceType<typeof Task>, benchmark: Benchmark) => {
task.addEventListener('complete', (e) => {
const task = e.task
const taskRes = task.result!
const result = benchmark.result!.benchmark!
Object.assign(result, taskRes)
updateTask(benchmark)
}, {
once: true,
})
task.addEventListener('error', (e) => {
const task = e.task
defer.reject(benchmark ? task.result!.error : e)
}, {
once: true,
})
}

benchmarkGroup.forEach((benchmark) => {
const options = getBenchOptions(benchmark)
const benchmarkInstance = new Bench(options)

Expand All @@ -65,50 +83,32 @@ async function runBenchmarkSuite(suite: Suite, runner: VitestRunner) {
startTime: start,
benchmark: createBenchmarkResult(benchmark.name),
}
const id = idx.toString()
benchmarkMap[id] = benchmark

const task = new Task(benchmarkInstance, id, benchmarkFn)
const task = new Task(benchmarkInstance, benchmark.name, benchmarkFn)
benchmarkTasks.set(benchmark, task)
addBenchTaskListener(task, benchmark)
updateTask(benchmark)
})

benchmarkGroup.forEach((benchmark) => {
const task = benchmarkTasks.get(benchmark)!
task.addEventListener('complete', (e) => {
const task = e.task
const _benchmark = benchmarkMap[task.name || '']
if (_benchmark) {
const taskRes = task.result!
const result = _benchmark.result!.benchmark!
Object.assign(result, taskRes)
updateTask(_benchmark)
}
})
task.addEventListener('error', (e) => {
const task = e.task
const _benchmark = benchmarkMap[task.name || '']
defer.reject(_benchmark ? task.result!.error : e)
})
})

const tasks: BenchTask[] = []
const { setTimeout } = getSafeTimers()
const tasks: [BenchTask, Benchmark][] = []
for (const benchmark of benchmarkGroup) {
const task = benchmarkTasks.get(benchmark)!
await task.warmup()
const { setTimeout } = getSafeTimers()
tasks.push(await new Promise<BenchTask>(resolve => setTimeout(async () => {
resolve(await task.run())
})))
tasks.push([
await new Promise<BenchTask>(resolve => setTimeout(async () => {
resolve(await task.run())
})),
benchmark,
])
}

suite.result!.duration = performance.now() - start
suite.result!.state = 'pass'

tasks
.sort((a, b) => a.result!.mean - b.result!.mean)
.forEach((cycle, idx) => {
const benchmark = benchmarkMap[cycle.name || '']
.sort(([taskA], [taskB]) => taskA.result!.mean - taskB.result!.mean)
.forEach(([, benchmark], idx) => {
benchmark.result!.state = 'pass'
if (benchmark) {
const result = benchmark.result!.benchmark!
Expand Down
7 changes: 7 additions & 0 deletions test/core/test/jest-expect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,13 @@ describe('jest-expect', () => {
expect(() => {
expect(complex).toHaveProperty('a-b', false)
}).toThrowErrorMatchingInlineSnapshot('"expected { \'0\': \'zero\', foo: 1, …(4) } to have property "a-b" with value false"')

expect(() => {
const x = { a: { b: { c: 1 } } }
const y = { a: { b: { c: 2 } } }
Object.freeze(x.a)
expect(x).toEqual(y)
}).toThrowErrorMatchingInlineSnapshot(`"expected { a: { b: { c: 1 } } } to deeply equal { a: { b: { c: 2 } } }"`)
})

it('assertions', () => {
Expand Down
30 changes: 30 additions & 0 deletions test/core/test/nested-test.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { describe, expect, test } from 'vitest'

test('nested test should throw error', () => {
expect(() => {
test('test inside test', () => {})
}).toThrowErrorMatchingInlineSnapshot(`"Nested tests are not allowed"`)

expect(() => {
test.each([1, 2, 3])('test.each inside test %d', () => {})
}).toThrowErrorMatchingInlineSnapshot(`"Nested tests are not allowed"`)

expect(() => {
test.skipIf(false)('test.skipIf inside test', () => {})
}).toThrowErrorMatchingInlineSnapshot(`"Nested tests are not allowed"`)
})

describe('parallel tests', () => {
test.concurrent('parallel test 1 with nested test', () => {
expect(() => {
test('test inside test', () => {})
}).toThrowErrorMatchingInlineSnapshot(`"Nested tests are not allowed"`)
})
test.concurrent('parallel test 2 without nested test', () => {})
test.concurrent('parallel test 3 without nested test', () => {})
test.concurrent('parallel test 4 with nested test', () => {
expect(() => {
test('test inside test', () => {})
}).toThrowErrorMatchingInlineSnapshot(`"Nested tests are not allowed"`)
})
})
Loading

0 comments on commit 6c4a64b

Please sign in to comment.