Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585import type { TestBenchmark } from 'vitest'import type { JsonTestResult, JsonTestResults, Vitest } from 'vitest/node'import { readdirSync } from 'node:fs'import { readFile } from 'node:fs/promises'import { beforeAll, describe, expect, onTestFailed, test } from 'vitest'import { buildTestTree } from '../../test-utils'import { instances, provider, runBrowserTests } from './utils'
function noop() {}
describe('running browser tests', async () => { let stderr: string let stdout: string let browserResultJson: JsonTestResults let passedTests: JsonTestResult[] let failedTests: JsonTestResult[] let vitest: Vitest const events: string[] = [] const emittedBenchmarks: Array<{ projectName: string testName: string benchmark: TestBenchmark }> = []
beforeAll(async () => { ;({ stderr, stdout, ctx: vitest, } = await runBrowserTests({ allowOnly: true, reporters: [ { onBrowserInit(project) { events.push(`onBrowserInit ${project.name}`) }, onTestCaseBenchmark(testCase, benchmark) { emittedBenchmarks.push({ projectName: testCase.project.name || '', testName: testCase.fullName, benchmark, }) }, }, 'json', { onInit: noop, onTestRunStart: noop, onTestModuleCollected: noop, onTestRunEnd: noop, onTestRemoved: noop, onWatcherStart: noop, onWatcherRerun: noop, onServerRestart: noop, onUserConsoleLog: noop, }, 'default', ], }))
const browserResult = await readFile('./browser.json', 'utf-8') browserResultJson = JSON.parse(browserResult) as JsonTestResults const getPassed = (results: JsonTestResult[]) => results.filter((result) => result.status === 'passed' && !result.message) const getFailed = (results: JsonTestResult[]) => results.filter((result) => result.status === 'failed') passedTests = getPassed(browserResultJson.testResults) failedTests = getFailed(browserResultJson.testResults) })
test('tests are actually running', () => { expect(stderr).toBe('')
const testFiles = Array.from(new Set(browserResultJson.testResults.map((t) => t.name)))
vitest.projects.forEach((project) => { // the order is non-deterministic expect(events).toContain(`onBrowserInit ${project.name}`) })
// test files are optimized automatically (type-check-only files are excluded) const runtimeTestFiles = testFiles.filter((f) => !f.endsWith('.test-d.ts')) expect(vitest.projects.map((p) => p.vite.config.optimizeDeps.entries)).toEqual( vitest.projects.map(() => expect.arrayContaining(runtimeTestFiles)), )
const testFilesCount = readdirSync('./test').filter( (n) => n.includes('.test.') || n.includes('.test-d.') || n.includes('.bench.'), ).length + 1 // 1 is in-source-test
expect(browserResultJson.testResults).toHaveLength(testFilesCount * instances.length) expect(passedTests).toHaveLength(browserResultJson.testResults.length) expect(failedTests).toHaveLength(0) })
test('benchmarks run in a dedicated `(bench)` project per browser instance', () => { const benchProjects = vitest.projects.filter((p) => p.name.endsWith('(bench)')) expect(benchProjects.map((p) => p.name).sort()).toEqual( instances.map(({ browser }) => `${browser} (bench)`).sort(), ) })
test('perProject benchmarks emit tasks with the perProject flag in every browser', () => { const records = emittedBenchmarks.filter( (e) => e.testName === 'perProject registrations flow through the browser RPC (onTestBenchmark)', ) // the test calls `.run()` twice, so each browser produces 2 benchmark records expect(records.length, `perProject emitted: ${records.length}`).toBe(2 * instances.length) for (const record of records) { expect(record.benchmark.tasks, `empty tasks for ${record.projectName}`).toHaveLength(1) const [task] = record.benchmark.tasks expect(task.perProject, `missing perProject flag on ${record.projectName}/${task.name}`).toBe( true, ) expect(task.fromStore).toBeUndefined() } })
test('bench.compare emits one benchmark with both registrations ranked', () => { const records = emittedBenchmarks.filter( (e) => e.testName === 'bench.compare resolves a BenchStorage in the browser', ) expect(records.length).toBe(instances.length) for (const record of records) { expect( record.benchmark.tasks.map((t) => t.name).sort(), `unexpected tasks for ${record.projectName}`, ).toEqual(['a', 'b']) expect(record.benchmark.tasks.map((t) => t.rank).sort()).toEqual([1, 2]) } })
test('writeResult flows through the write-artifact RPC in every browser', () => { const records = emittedBenchmarks.filter( (e) => e.testName === 'writeResult exercises the writeBenchmarkResult RPC round-trip', ) expect(records.length).toBe(instances.length) for (const record of records) { expect(record.benchmark.tasks, `empty tasks for ${record.projectName}`).toHaveLength(1) const [task] = record.benchmark.tasks expect(task.name).toBe('with-write') } })
test('tags are collected', () => { expect(vitest.config.tags).toEqual([ { name: 'e2e', priority: 10 }, { name: 'test', priority: 5 }, { name: 'browser', priority: 1 }, ])
const testModule = vitest.state .getTestModules() .find((m) => m.moduleId.includes('tags.test.ts')) expect.assert(testModule) expect(buildTestTree([testModule], (t) => t.tags)).toMatchInlineSnapshot(` { "test/tags.test.ts": { "suite 1": { "suite 2": { "test 2": [ "browser", "e2e", ], }, "test 1": [ "browser", "test", ], }, }, } `) })
test('runs in-source tests', () => { expect(stdout).toContain('src/actions.ts') const actionsTest = passedTests.find((t) => t.name.includes('/actions.ts')) expect.assert(actionsTest) expect(actionsTest.assertionResults).toHaveLength(1) })
test('unsubscribes cancel listeners after run', async () => { // should not throw birpc closing errors await expect(vitest.cancelCurrentRun('keyboard-input')).resolves.not.toThrow() })})
describe('console logging tests', async () => { let stderr: string let stdout: string beforeAll(async () => { ;({ stderr, stdout } = await runBrowserTests({ root: './fixtures/print-logs', // assert on Vitest's own console forwarding rather than Vite's client // relay, which does not run when the tester loads the stubbed @vite/client reporters: ['default'], })) })
test('logs are redirected to stdout', () => { expect(stdout).toContain('stdout | test/logs.test.ts > logging to stdout') expect(stdout).toContain('hello from console.log') expect(stdout).toContain('hello from console.info') expect(stdout).toContain('hello from console.debug') expect(stdout).toContain( `{ hello: 'from dir',} `.trim(), ) expect(stdout).toContain( `{ hello: 'from dirxml',} `.trim(), ) expect(stdout).toContain('dom <div />') expect(stdout).toContain('default: 1') expect(stdout).toContain('default: 2') expect(stdout).toContain('default: 3') expect(stdout).toContain('count: 1') expect(stdout).toContain('count: 2') expect(stdout).toContain('count: 3') expect(stdout).toMatch(/default: [\d.]+ ms/) expect(stdout).toMatch(/time: [\d.]+ ms/) expect(stdout).toMatch(/\[console-time-fake\]: [\d.]+ ms/) expect(stdout).not.toContain('[console-time-fake]: 0 ms') expect(stdout).toContain('hello from one') expect(stdout).toContain(`hello from two { hello: 'object',}`) })
test('logs are redirected to stderr', () => { expect(stderr).toContain('stderr | test/logs.test.ts > logging to stderr') expect(stderr).toContain('hello from console.error') expect(stderr).toContain('hello from console.warn') expect(stderr).toContain('Timer "invalid timeLog" does not exist') expect(stderr).toContain('Timer "invalid timeEnd" does not exist') // safari logs the stack files with @https://... expect(stderr).toMatch(/hello from console.trace\s+(\w+|@)/) })
test(`logs have stack traces`, () => { expect(stdout).toMatch( `log with a stack ❯ test/logs.test.ts:58:11 `.trim(), ) expect(stderr).toMatch( `error with a stack ❯ test/logs.test.ts:59:11 `.trim(), ) // console.trace processes the stack trace correctly expect(stderr).toMatch('test/logs.test.ts:60:11') })
test('popup apis should log a warning', () => { expect(stderr).toContain('Vitest encountered a `alert("test")`') expect(stderr).toContain('Vitest encountered a `confirm("test")`') expect(stderr).toContain('Vitest encountered a `print()`') expect(stderr).toContain('Vitest encountered a `prompt("test")`') })})
test('disableConsoleIntercept', async () => { const result = await runBrowserTests({ root: './fixtures/print-logs', project: [instances[0].browser], disableConsoleIntercept: true, }) expect(result.stderr).toBe('') expect(result.stdout).not.toContain('logging to stdout') expect(result.stdout).not.toContain('hello from console.log')})
test(`stack trace points to correct file in every browser when failed`, async () => { expect.assertions(30) const { stderr } = await runBrowserTests({ root: './fixtures/failing', reporters: [ 'default', { onTestCaseReady(testCase) { if (testCase.name !== 'correctly fails and prints a diff') { return } if (testCase.project.name === 'chromium' || testCase.project.name === 'chrome') { expect(testCase.result().errors?.[0].stacks).toEqual([ { line: 11, column: 13, file: testCase.module.moduleId, method: '', }, ]) } }, }, ], })
expect(stderr).toContain('expected 1 to be 2') expect(stderr).toMatch(/- 2\s+\+ 1/) // expect(stderr).toContain('Expected to be') // expect(stderr).toContain('But got') expect(stderr).toContain('Failure screenshot') expect(stderr).toContain('.vitest/attachments/failure-screenshots/failing')
expect(stderr).toContain('Access denied to "/inaccessible/path".')
expect(stderr).toMatch(/failing.test.ts:11:13/)
// column is 13 in safari, 9 in others expect(stderr).toMatch(/throwError src\/error.ts:8:(13|9)/)
expect(stderr).toContain( 'The call was not awaited. This method is asynchronous and must be awaited; otherwise, the call will not start to avoid unhandled rejections.', ) expect(stderr).toMatch(/failing.test.ts:19:28/) expect(stderr).toMatch(/failing.test.ts:20:28/) expect(stderr).toMatch(/failing.test.ts:21:28/) expect(stderr).toMatch(/failing.test.ts:22:13/) expect(stderr).toMatch(/failing.test.ts:23:13/) expect(stderr).toMatch(/failing.test.ts:24:13/) expect(stderr).toMatch(/failing.test.ts:25:13/) expect(stderr).toMatch(/failing.test.ts:26:13/) expect(stderr).toMatch(/failing.test.ts:27:13/) expect(stderr).toMatch(/failing.test.ts:28:13/) expect(stderr).toMatch(/failing.test.ts:29:13/) expect(stderr).toMatch(/failing.test.ts:30:13/) expect(stderr).toMatch(/failing.test.ts:31:13/) expect(stderr).toMatch(/failing.test.ts:32:13/) expect(stderr).toMatch(/failing.test.ts:33:13/) expect(stderr).toMatch(/failing.test.ts:34:13/) expect(stderr).toMatch(/failing.test.ts:35:13/)
expect(stderr).toMatch(/bundled-lib\/src\/b.js:2:(9|18)/) expect(stderr).toMatch(/bundled-lib\/src\/index.js:5:(16|17)/)
// index() is called from a bundled file expect(stderr).toMatch(/failing.test.ts:39:3/)
// "not awaited but with then/catch/finally" test should not produce warnings expect(stderr).not.toMatch(/failing.test.ts:4[3-8]/)})
test('user-event', async () => { const { stdout, stderr } = await runBrowserTests({ root: './fixtures/user-event', }) expect(stderr).toBe('') onTestFailed(() => console.error(stderr)) instances.forEach(({ browser }) => { expect(stdout).toReportPassedTest('cleanup-retry.test.ts', browser) expect(stdout).toReportPassedTest('cleanup1.test.ts', browser) expect(stdout).toReportPassedTest('cleanup2.test.ts', browser) expect(stdout).toReportPassedTest('keyboard.test.ts', browser) expect(stdout).toReportPassedTest('clipboard.test.ts', browser) })})
test('timeout settings', async () => { const { stderr } = await runBrowserTests({ root: './fixtures/timeout', }) expect(stderr).toContain('Matcher did not succeed in time.') if (provider.name === 'playwright') { expect(stderr).toContain('locator.click: Timeout 500ms exceeded.') expect(stderr).toContain('locator.click: Timeout 345ms exceeded.') }})
test('viewport', async () => { const { stdout, stderr } = await runBrowserTests({ root: './fixtures/viewport', }) expect(stderr).toBe('') instances.forEach(({ browser }) => { expect(stdout).toReportPassedTest('basic.test.ts', browser) })})
test("in-source tests don't run when the module is imported by the test", async () => { const { stderr, stdout } = await runBrowserTests({}, ['mocking.test.ts']) expect(stderr).toBe('')
instances.forEach(({ browser }) => { expect(stdout).toReportPassedTest('test/mocking.test.ts', browser) })
// there is only one file with one test inside // if this stops working, it will report twice as much tests expect(stdout).toContain(`Test Files ${instances.length} passed`) expect(stdout).toContain(`Tests ${instances.length} passed`)})
test('in-source tests run correctly when filtered', async () => { const { stderr, stdout } = await runBrowserTests({}, ['actions.ts']) expect(stderr).toBe('')
instances.forEach(({ browser }) => { expect(stdout).toReportPassedTest('src/actions.ts', browser) })
// there is only one file with one test inside expect(stdout).toContain(`Test Files ${instances.length} passed`) expect(stdout).toContain(`Tests ${instances.length} passed`)})
test('re-evaluate setupFiles on each test run even when isolate is false', async () => { const { exitCode, stderr, stdout } = await runBrowserTests({ root: './fixtures/isolate-and-setup-file', })
expect(stderr).toBe('') expect(exitCode).toBe(0) instances.forEach(({ browser }) => { expect(stdout).toReportPassedTest('a.test.ts', browser) expect(stdout).toReportPassedTest('b.test.ts', browser) })})
test.runIf(provider.name === 'playwright')( 'timeout hooks', async ({ onTestFailed }) => { const { stderr } = await runBrowserTests({ root: './fixtures/timeout-hooks', })
onTestFailed(() => { console.error(stderr) })
const lines = stderr.split('\n') const timeoutErrorsIndexes: number[] = [] lines.forEach((line, index) => { if (line.includes('TimeoutError:')) { timeoutErrorsIndexes.push(index) } })
const snapshot = timeoutErrorsIndexes .map((index) => { return [ lines[index - 1], lines[index].replace(/Timeout \d+ms exceeded/, 'Timeout <ms> exceeded'), lines[index + 4], ].join('\n') }) .sort() .join('\n\n')
expect(snapshot).toMatchInlineSnapshot(` " FAIL |chromium| hooks-timeout.test.ts > timeouts are failing correctly > afterAll TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:39:46
FAIL |chromium| hooks-timeout.test.ts > timeouts are failing correctly > afterEach > skipped TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:23:46
FAIL |chromium| hooks-timeout.test.ts > timeouts are failing correctly > beforeAll TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:31:46
FAIL |chromium| hooks-timeout.test.ts > timeouts are failing correctly > beforeEach > skipped TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:15:46
FAIL |chromium| hooks-timeout.test.ts > timeouts are failing correctly > click on non-existing element fails TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:6:34
FAIL |chromium| hooks-timeout.test.ts > timeouts are failing correctly > onTestFailed > fails TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:62:48
FAIL |chromium| hooks-timeout.test.ts > timeouts are failing correctly > onTestFailed > fails global TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:70:48
FAIL |chromium| hooks-timeout.test.ts > timeouts are failing correctly > onTestFinished > fails TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:48:48
FAIL |chromium| hooks-timeout.test.ts > timeouts are failing correctly > onTestFinished > fails global TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:54:48
FAIL |firefox| hooks-timeout.test.ts > timeouts are failing correctly > afterAll TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:39:46
FAIL |firefox| hooks-timeout.test.ts > timeouts are failing correctly > afterEach > skipped TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:23:46
FAIL |firefox| hooks-timeout.test.ts > timeouts are failing correctly > beforeAll TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:31:46
FAIL |firefox| hooks-timeout.test.ts > timeouts are failing correctly > beforeEach > skipped TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:15:46
FAIL |firefox| hooks-timeout.test.ts > timeouts are failing correctly > click on non-existing element fails TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:6:34
FAIL |firefox| hooks-timeout.test.ts > timeouts are failing correctly > onTestFailed > fails TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:62:48
FAIL |firefox| hooks-timeout.test.ts > timeouts are failing correctly > onTestFailed > fails global TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:70:48
FAIL |firefox| hooks-timeout.test.ts > timeouts are failing correctly > onTestFinished > fails TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:48:48
FAIL |firefox| hooks-timeout.test.ts > timeouts are failing correctly > onTestFinished > fails global TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:54:48
FAIL |webkit| hooks-timeout.test.ts > timeouts are failing correctly > afterAll TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:39:46
FAIL |webkit| hooks-timeout.test.ts > timeouts are failing correctly > afterEach > skipped TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:23:46
FAIL |webkit| hooks-timeout.test.ts > timeouts are failing correctly > beforeAll TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:31:46
FAIL |webkit| hooks-timeout.test.ts > timeouts are failing correctly > beforeEach > skipped TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:15:46
FAIL |webkit| hooks-timeout.test.ts > timeouts are failing correctly > click on non-existing element fails TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:6:34
FAIL |webkit| hooks-timeout.test.ts > timeouts are failing correctly > onTestFailed > fails TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:62:48
FAIL |webkit| hooks-timeout.test.ts > timeouts are failing correctly > onTestFailed > fails global TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:70:48
FAIL |webkit| hooks-timeout.test.ts > timeouts are failing correctly > onTestFinished > fails TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:48:48
FAIL |webkit| hooks-timeout.test.ts > timeouts are failing correctly > onTestFinished > fails global TimeoutError: locator.click: Timeout <ms> exceeded. ❯ hooks-timeout.test.ts:54:48" `)
// page.getByRole('code').click() expect(stderr).toContain('locator.click: Timeout') // playwright error is proxied from the server to the client and back correctly expect(stderr).toContain( "waiting for locator('[data-vitest=\"true\"]').contentFrame().getByRole('code')", ) expect(stderr).toMatch(/hooks-timeout.test.ts:6:34/) // await expect.element().toBeVisible() expect(stderr).toContain("Cannot find element with locator: getByRole('code')") expect(stderr).toMatch(/hooks-timeout.test.ts:10:50/) }, 120_000 * 3,)