diff --git a/packages/browser/src/node/commands/upload.ts b/packages/browser/src/node/commands/upload.ts index 60024abf7..0d9195e70 100644 --- a/packages/browser/src/node/commands/upload.ts +++ b/packages/browser/src/node/commands/upload.ts @@ -1,3 +1,4 @@ +import type { UserEventUploadOptions } from '@vitest/browser/context' import type { UserEventCommand } from './utils' import { dirname, resolve } from 'pathe' import { PlaywrightBrowserProvider } from '../providers/playwright' @@ -7,10 +8,11 @@ export const upload: UserEventCommand<(element: string, files: Array) => void> = async ( +}>, options: UserEventUploadOptions) => void> = async ( context, selector, files, + options, ) => { const testPath = context.testPath if (!testPath) { @@ -30,7 +32,7 @@ export const upload: UserEventCommand<(element: string, files: Array provider.close())) + vitest._browserSessions.sessionIds.clear() providers.clear() vitest.projects.forEach((project) => { project.browser?.state.orchestrators.forEach((orchestrator) => { orchestrator.$close() }) }) + debug?.('browser pool closed all providers') }, runTests: files => runWorkspaceTests('run', files), collectTests: files => runWorkspaceTests('collect', files), @@ -161,6 +165,7 @@ class BrowserPool { this._promise ??= createDefer() if (!files.length) { + debug?.('no tests found, finishing test run immediately') this._promise.resolve() return this._promise } @@ -177,6 +182,7 @@ class BrowserPool { }) if (this.orchestrators.size >= this.options.maxWorkers) { + debug?.('all orchestrators are ready, not creating more') return this._promise } @@ -190,6 +196,9 @@ class BrowserPool { const promises: Promise[] = [] for (let i = 0; i < workerCount; i++) { const sessionId = crypto.randomUUID() + this.project.vitest._browserSessions.sessionIds.add(sessionId) + const project = this.project.name + debug?.('[%s] creating session for %s', sessionId, project) const page = this.openPage(sessionId).then(() => { // start running tests on the page when it's ready this.runNextTest(method, sessionId) @@ -197,6 +206,7 @@ class BrowserPool { promises.push(page) } await Promise.all(promises) + debug?.('all sessions are created') return this._promise } @@ -230,7 +240,14 @@ class BrowserPool { if (this.readySessions.size === this.orchestrators.size) { this._promise?.resolve() this._promise = undefined - debug?.('all tests finished running') + debug?.('[%s] all tests finished running', sessionId) + } + else { + debug?.( + `did not finish sessions for ${sessionId}: |ready - %s| |overall - %s|`, + [...this.readySessions].join(', '), + [...this.orchestrators.keys()].join(', '), + ) } } @@ -288,6 +305,7 @@ class BrowserPool { this.cancel() this._promise?.resolve() this._promise = undefined + debug?.('[%s] browser connection was closed', sessionId) return } debug?.('[%s] error during %s test run: %s', sessionId, file, error) diff --git a/packages/browser/src/node/projectParent.ts b/packages/browser/src/node/projectParent.ts index 7b4e3a085..ff6f2373a 100644 --- a/packages/browser/src/node/projectParent.ts +++ b/packages/browser/src/node/projectParent.ts @@ -79,7 +79,7 @@ export class ParentBrowserProject { if (mod) { return id } - const resolvedPath = resolve(project.config.root, id.slice(1)) + const resolvedPath = resolve(this.vite.config.root, id.slice(1)) const modUrl = this.vite.moduleGraph.getModuleById(resolvedPath) if (modUrl) { return resolvedPath diff --git a/packages/browser/src/node/providers/playwright.ts b/packages/browser/src/node/providers/playwright.ts index 8b2943582..3045d959d 100644 --- a/packages/browser/src/node/providers/playwright.ts +++ b/packages/browser/src/node/providers/playwright.ts @@ -18,6 +18,9 @@ import type { TestProject, } from 'vitest/node' import { createManualModuleSource } from '@vitest/mocker/node' +import { createDebugger } from 'vitest/node' + +const debug = createDebugger('vitest:browser:playwright') export const playwrightBrowsers = ['firefox', 'webkit', 'chromium'] as const export type PlaywrightBrowser = (typeof playwrightBrowsers)[number] @@ -48,6 +51,8 @@ export class PlaywrightBrowserProvider implements BrowserProvider { public mocker: BrowserModuleMocker | undefined + private closing = false + getSupportedBrowsers(): readonly string[] { return playwrightBrowsers } @@ -56,6 +61,7 @@ export class PlaywrightBrowserProvider implements BrowserProvider { project: TestProject, { browser, options }: PlaywrightProviderOptions, ): void { + this.closing = false this.project = project this.browserName = browser this.options = options as any @@ -63,11 +69,15 @@ export class PlaywrightBrowserProvider implements BrowserProvider { } private async openBrowser() { + await this._throwIfClosing() + if (this.browserPromise) { + debug?.('[%s] the browser is resolving, reusing the promise', this.browserName) return this.browserPromise } if (this.browser) { + debug?.('[%s] the browser is resolved, reusing it', this.browserName) return this.browser } @@ -103,8 +113,8 @@ export class PlaywrightBrowserProvider implements BrowserProvider { } } - const browser = await playwright[this.browserName].launch(launchOptions) - this.browser = browser + debug?.('[%s] initializing the browser with launch options: %O', this.browserName, launchOptions) + this.browser = await playwright[this.browserName].launch(launchOptions) this.browserPromise = null return this.browser })() @@ -243,11 +253,15 @@ export class PlaywrightBrowserProvider implements BrowserProvider { } private async createContext(sessionId: string) { + await this._throwIfClosing() + if (this.contexts.has(sessionId)) { + debug?.('[%s][%s] the context already exists, reusing it', sessionId, this.browserName) return this.contexts.get(sessionId)! } const browser = await this.openBrowser() + await this._throwIfClosing(browser) const { actionTimeout, ...contextOptions } = this.options?.context ?? {} const options = { ...contextOptions, @@ -257,9 +271,11 @@ export class PlaywrightBrowserProvider implements BrowserProvider { options.viewport = null } const context = await browser.newContext(options) + await this._throwIfClosing(context) if (actionTimeout) { context.setDefaultTimeout(actionTimeout) } + debug?.('[%s][%s] the context is ready', sessionId, this.browserName) this.contexts.set(sessionId, context) return context } @@ -306,7 +322,10 @@ export class PlaywrightBrowserProvider implements BrowserProvider { } private async openBrowserPage(sessionId: string) { + await this._throwIfClosing() + if (this.pages.has(sessionId)) { + debug?.('[%s][%s] the page already exists, closing the old one', sessionId, this.browserName) const page = this.pages.get(sessionId)! await page.close() this.pages.delete(sessionId) @@ -314,6 +333,8 @@ export class PlaywrightBrowserProvider implements BrowserProvider { const context = await this.createContext(sessionId) const page = await context.newPage() + debug?.('[%s][%s] the page is ready', sessionId, this.browserName) + await this._throwIfClosing(page) this.pages.set(sessionId, page) if (process.env.VITEST_PW_DEBUG) { @@ -333,9 +354,24 @@ export class PlaywrightBrowserProvider implements BrowserProvider { } async openPage(sessionId: string, url: string, beforeNavigate?: () => Promise): Promise { + debug?.('[%s][%s] creating the browser page for %s', sessionId, this.browserName, url) const browserPage = await this.openBrowserPage(sessionId) await beforeNavigate?.() + debug?.('[%s][%s] browser page is created, opening %s', sessionId, this.browserName, url) await browserPage.goto(url, { timeout: 0 }) + await this._throwIfClosing(browserPage) + } + + private async _throwIfClosing(disposable?: { close: () => Promise }) { + if (this.closing) { + debug?.('[%s] provider was closed, cannot perform the action on %s', this.browserName, String(disposable)) + await disposable?.close() + this.pages.clear() + this.contexts.clear() + this.browser = null + this.browserPromise = null + throw new Error(`[vitest] The provider was closed.`) + } } async getCDPSession(sessionid: string): Promise { @@ -359,13 +395,20 @@ export class PlaywrightBrowserProvider implements BrowserProvider { } async close(): Promise { + debug?.('[%s] closing provider', this.browserName) + this.closing = true const browser = this.browser this.browser = null + if (this.browserPromise) { + await this.browserPromise + this.browserPromise = null + } await Promise.all([...this.pages.values()].map(p => p.close())) this.pages.clear() await Promise.all([...this.contexts.values()].map(c => c.close())) this.contexts.clear() await browser?.close() + debug?.('[%s] provider is closed', this.browserName) } } diff --git a/packages/browser/src/node/providers/webdriver.ts b/packages/browser/src/node/providers/webdriver.ts index c13440577..6d8287dbd 100644 --- a/packages/browser/src/node/providers/webdriver.ts +++ b/packages/browser/src/node/providers/webdriver.ts @@ -4,6 +4,9 @@ import type { BrowserProviderInitializationOptions, TestProject, } from 'vitest/node' +import { createDebugger } from 'vitest/node' + +const debug = createDebugger('vitest:browser:wdio') const webdriverBrowsers = ['firefox', 'chrome', 'edge', 'safari'] as const type WebdriverBrowser = (typeof webdriverBrowsers)[number] @@ -24,6 +27,8 @@ export class WebdriverBrowserProvider implements BrowserProvider { private options?: Capabilities.WebdriverIOConfig + private closing = false + getSupportedBrowsers(): readonly string[] { return webdriverBrowsers } @@ -32,6 +37,7 @@ export class WebdriverBrowserProvider implements BrowserProvider { ctx: TestProject, { browser, options }: WebdriverProviderOptions, ): Promise { + this.closing = false this.project = ctx this.browserName = browser this.options = options as Capabilities.WebdriverIOConfig @@ -71,7 +77,10 @@ export class WebdriverBrowserProvider implements BrowserProvider { } async openBrowser(): Promise { + await this._throwIfClosing('opening the browser') + if (this.browser) { + debug?.('[%s] the browser is already opened, reusing it', this.browserName) return this.browser } @@ -87,12 +96,16 @@ export class WebdriverBrowserProvider implements BrowserProvider { const { remote } = await import('webdriverio') - // TODO: close everything, if browser is closed from the outside - this.browser = await remote({ + const remoteOptions: Capabilities.WebdriverIOConfig = { ...this.options, logLevel: 'error', capabilities: this.buildCapabilities(), - }) + } + + debug?.('[%s] opening the browser with options: %O', this.browserName, remoteOptions) + // TODO: close everything, if browser is closed from the outside + this.browser = await remote(remoteOptions) + await this._throwIfClosing() return this.browser } @@ -134,12 +147,26 @@ export class WebdriverBrowserProvider implements BrowserProvider { return capabilities } - async openPage(_sessionId: string, url: string): Promise { + async openPage(sessionId: string, url: string): Promise { + await this._throwIfClosing('creating the browser') + debug?.('[%s][%s] creating the browser page for %s', sessionId, this.browserName, url) const browserInstance = await this.openBrowser() + debug?.('[%s][%s] browser page is created, opening %s', sessionId, this.browserName, url) await browserInstance.url(url) + await this._throwIfClosing('opening the url') + } + + private async _throwIfClosing(action?: string) { + if (this.closing) { + debug?.(`[%s] provider was closed, cannot perform the action${action ? ` ${action}` : ''}`, this.browserName) + await (this.browser?.sessionId ? this.browser?.deleteSession?.() : null) + throw new Error(`[vitest] The provider was closed.`) + } } async close(): Promise { + debug?.('[%s] closing provider', this.browserName) + this.closing = true await Promise.all([ this.browser?.sessionId ? this.browser?.deleteSession?.() : null, ]) diff --git a/packages/browser/src/node/rpc.ts b/packages/browser/src/node/rpc.ts index 01ae82d4e..ec856c7b9 100644 --- a/packages/browser/src/node/rpc.ts +++ b/packages/browser/src/node/rpc.ts @@ -58,6 +58,13 @@ export function setupBrowserRpc(globalServer: ParentBrowserProject, defaultMocke ) } + if (!vitest._browserSessions.sessionIds.has(sessionId)) { + const ids = [...vitest._browserSessions.sessionIds].join(', ') + return error( + new Error(`[vitest] Unknown session id "${sessionId}". Expected one of ${ids}.`), + ) + } + if (type === 'orchestrator') { const session = vitest._browserSessions.getSession(sessionId) // it's possible the session was already resolved by the preview provider diff --git a/packages/browser/src/node/serverOrchestrator.ts b/packages/browser/src/node/serverOrchestrator.ts index 3290f0c85..6dc899e59 100644 --- a/packages/browser/src/node/serverOrchestrator.ts +++ b/packages/browser/src/node/serverOrchestrator.ts @@ -26,6 +26,11 @@ export async function resolveOrchestrator( return } + // ignore uknown pages + if (sessionId && sessionId !== 'none' && !globalServer.vitest._browserSessions.sessionIds.has(sessionId)) { + return + } + const injectorJs = typeof globalServer.injectorJs === 'string' ? globalServer.injectorJs : await globalServer.injectorJs diff --git a/packages/vitest/src/node/browser/sessions.ts b/packages/vitest/src/node/browser/sessions.ts index c197a56c9..7ac214e71 100644 --- a/packages/vitest/src/node/browser/sessions.ts +++ b/packages/vitest/src/node/browser/sessions.ts @@ -5,6 +5,8 @@ import { createDefer } from '@vitest/utils' export class BrowserSessions { private sessions = new Map() + public sessionIds: Set = new Set() + getSession(sessionId: string): BrowserServerStateSession | undefined { return this.sessions.get(sessionId) } diff --git a/test/browser/fixtures/browser-crash/vitest.config.ts b/test/browser/fixtures/browser-crash/vitest.config.ts index f6fec5a38..099e62941 100644 --- a/test/browser/fixtures/browser-crash/vitest.config.ts +++ b/test/browser/fixtures/browser-crash/vitest.config.ts @@ -37,7 +37,7 @@ export default defineConfig({ commands: { forceCrash }, enabled: true, provider, - instances: instances.map(instance => ({ + instances: instances.filter(i => i.browser !== 'webkit').map(instance => ({ ...instance, context: { actionTimeout: 500, diff --git a/test/browser/test/failing.snaphot.test.ts b/test/browser/fixtures/failing/failing.snaphot.test.ts similarity index 100% rename from test/browser/test/failing.snaphot.test.ts rename to test/browser/fixtures/failing/failing.snaphot.test.ts diff --git a/test/browser/test/failing.test.ts b/test/browser/fixtures/failing/failing.test.ts similarity index 93% rename from test/browser/test/failing.test.ts rename to test/browser/fixtures/failing/failing.test.ts index 2f88b0d1f..7dc7689a8 100644 --- a/test/browser/test/failing.test.ts +++ b/test/browser/fixtures/failing/failing.test.ts @@ -1,7 +1,7 @@ import { page } from '@vitest/browser/context' import { index } from '@vitest/bundled-lib' import { expect, it } from 'vitest' -import { throwError } from '../src/error' +import { throwError } from './src/error' document.body.innerHTML = ` diff --git a/test/browser/src/error.ts b/test/browser/fixtures/failing/src/error.ts similarity index 100% rename from test/browser/src/error.ts rename to test/browser/fixtures/failing/src/error.ts diff --git a/test/browser/fixtures/failing/vitest.config.ts b/test/browser/fixtures/failing/vitest.config.ts new file mode 100644 index 000000000..93c18a527 --- /dev/null +++ b/test/browser/fixtures/failing/vitest.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vitest/config' +import { fileURLToPath } from 'node:url' +import { instances, provider } from '../../settings' + +export default defineConfig({ + cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)), + test: { + browser: { + enabled: true, + provider, + instances, + isolate: false, + }, + }, +}) diff --git a/test/browser/fixtures/mocking-out-of-root/project1/vitest.config.ts b/test/browser/fixtures/mocking-out-of-root/project1/vitest.config.ts index 15b50b920..87ea3c9c9 100644 --- a/test/browser/fixtures/mocking-out-of-root/project1/vitest.config.ts +++ b/test/browser/fixtures/mocking-out-of-root/project1/vitest.config.ts @@ -14,7 +14,6 @@ export default defineConfig({ screenshotFailures: false, headless: true, instances, - headless: true, }, }, }) diff --git a/test/browser/test/dialog.test.ts b/test/browser/fixtures/print-logs/test/dialog.test.ts similarity index 100% rename from test/browser/test/dialog.test.ts rename to test/browser/fixtures/print-logs/test/dialog.test.ts diff --git a/test/browser/test/logs.test.ts b/test/browser/fixtures/print-logs/test/logs.test.ts similarity index 86% rename from test/browser/test/logs.test.ts rename to test/browser/fixtures/print-logs/test/logs.test.ts index 6bd985ade..036373aff 100644 --- a/test/browser/test/logs.test.ts +++ b/test/browser/fixtures/print-logs/test/logs.test.ts @@ -59,3 +59,12 @@ test('logging the stack', () => { console.error('error with a stack') console.trace('trace with a stack') }) + +test('console.time', async () => { + vi.useFakeTimers({ + toFake: ['Date', 'performance'], + }) + console.time('[console-time-fake]') + await new Promise(r => setTimeout(r, 500)) + console.timeEnd('[console-time-fake]') +}) diff --git a/test/browser/fixtures/print-logs/vitest.config.ts b/test/browser/fixtures/print-logs/vitest.config.ts new file mode 100644 index 000000000..93c18a527 --- /dev/null +++ b/test/browser/fixtures/print-logs/vitest.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vitest/config' +import { fileURLToPath } from 'node:url' +import { instances, provider } from '../../settings' + +export default defineConfig({ + cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)), + test: { + browser: { + enabled: true, + provider, + instances, + isolate: false, + }, + }, +}) diff --git a/test/browser/specs/browser-crash.test.ts b/test/browser/specs/browser-crash.test.ts index daaa1865b..a7410855a 100644 --- a/test/browser/specs/browser-crash.test.ts +++ b/test/browser/specs/browser-crash.test.ts @@ -1,14 +1,10 @@ import { expect, test } from 'vitest' -import { instances, runBrowserTests } from './utils' +import { runBrowserTests } from './utils' test('fails gracefully when browser crashes', async () => { const { stderr } = await runBrowserTests({ root: './fixtures/browser-crash', reporters: [['verbose', { isTTY: false }]], - browser: { - // webkit has no support for simulating browser crash - instances: instances.filter(item => item.name !== 'webkit'), - }, }) expect(stderr).toContain('Browser connection was closed while running tests. Was the page closed unexpectedly?') diff --git a/test/browser/specs/fix-4686.test.ts b/test/browser/specs/fix-4686.test.ts index bbc1846d9..56eb30b35 100644 --- a/test/browser/specs/fix-4686.test.ts +++ b/test/browser/specs/fix-4686.test.ts @@ -12,8 +12,5 @@ test('tests run in presence of config.base', async () => { ) expect(stderr).toBe('') - - instances.forEach(({ browser }) => { - expect(stdout).toContain(`✓ |${browser}| test/basic.test.ts`) - }) + expect(stdout).toReportPassedTest('test/basic.test.ts', instances) }) diff --git a/test/browser/specs/runner.test.ts b/test/browser/specs/runner.test.ts index cfcb9e5d7..f2d091edc 100644 --- a/test/browser/specs/runner.test.ts +++ b/test/browser/specs/runner.test.ts @@ -21,6 +21,7 @@ describe('running browser tests', async () => { stdout, ctx: vitest, } = await runBrowserTests({ + allowOnly: true, reporters: [ { onBrowserInit(project) { @@ -53,9 +54,7 @@ describe('running browser tests', async () => { }) test('tests are actually running', () => { - onTestFailed(() => { - console.error(stderr) - }) + expect(stderr).toBe('') const testFiles = browserResultJson.testResults.map(t => t.name) @@ -71,13 +70,9 @@ describe('running browser tests', async () => { // This should match the number of actual tests from browser.json // if you added new tests, these assertion will fail and you should // update the numbers - expect(browserResultJson.testResults).toHaveLength(20 * instances.length) - expect(passedTests).toHaveLength(18 * instances.length) - expect(failedTests).toHaveLength(2 * instances.length) - - expect(stderr).not.toContain('optimized dependencies changed') - expect(stderr).not.toContain('has been externalized for browser compatibility') - expect(stderr).not.toContain('Unhandled Error') + expect(browserResultJson.testResults).toHaveLength(16 * instances.length) + expect(passedTests).toHaveLength(browserResultJson.testResults.length) + expect(failedTests).toHaveLength(0) }) test('runs in-source tests', () => { @@ -86,14 +81,18 @@ describe('running browser tests', async () => { expect(actionsTest).toBeDefined() expect(actionsTest.assertionResults).toHaveLength(1) }) +}) - test('correctly prints error', () => { - 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('__screenshots__/failing') +describe('console logging tests', async () => { + let stderr: string + let stdout: string + beforeAll(async () => { + ({ + stderr, + stdout, + } = await runBrowserTests({ + root: './fixtures/print-logs', + })) }) test('logs are redirected to stdout', () => { @@ -161,34 +160,43 @@ error with a stack } }) - test(`stack trace points to correct file in every browser`, () => { - // depending on the browser it references either `.toBe()` or `expect()` - expect(stderr).toMatch(/test\/failing.test.ts:11:(12|17)/) - - // column is 18 in safari, 8 in others - expect(stderr).toMatch(/throwError src\/error.ts:8:(18|8)/) - - 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(/test\/failing.test.ts:19:(27|36)/) - expect(stderr).toMatch(/test\/failing.test.ts:20:(27|33)/) - expect(stderr).toMatch(/test\/failing.test.ts:21:(27|39)/) - - expect(stderr).toMatch(/bundled-lib\/src\/b.js:2:(8|18)/) - expect(stderr).toMatch(/bundled-lib\/src\/index.js:5:(15|17)/) - - // index() is called from a bundled file - expect(stderr).toMatch(/test\/failing.test.ts:25:(2|8)/) - }) - 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 `prompt("test")`') }) +}) - test('snapshot inaccessible file debuggability', () => { - expect(stderr).toContain('Access denied to "/inaccesible/path".') +test(`stack trace points to correct file in every browser when failed`, async () => { + const { stderr } = await runBrowserTests({ + root: './fixtures/failing', }) + + 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('__screenshots__/failing') + + expect(stderr).toContain('Access denied to "/inaccesible/path".') + + // depending on the browser it references either `.toBe()` or `expect()` + expect(stderr).toMatch(/failing.test.ts:11:(12|17)/) + + // column is 18 in safari, 8 in others + expect(stderr).toMatch(/throwError src\/error.ts:8:(18|8)/) + + 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:(27|36)/) + expect(stderr).toMatch(/failing.test.ts:20:(27|33)/) + expect(stderr).toMatch(/failing.test.ts:21:(27|39)/) + + expect(stderr).toMatch(/bundled-lib\/src\/b.js:2:(9|19)/) + expect(stderr).toMatch(/bundled-lib\/src\/index.js:5:(16|18)/) + + // index() is called from a bundled file + expect(stderr).toMatch(/failing.test.ts:25:(2|8)/) }) test('user-event', async () => { diff --git a/test/browser/specs/setup-file.test.ts b/test/browser/specs/setup-file.test.ts index 4542555a8..0d17e7337 100644 --- a/test/browser/specs/setup-file.test.ts +++ b/test/browser/specs/setup-file.test.ts @@ -8,12 +8,6 @@ test('setup file imports the same modules', async () => { { root: './fixtures/setup-file', }, - undefined, - {}, - { - // TODO 2025-03-26 remove after debugging - std: 'inherit', - }, ) expect(stderr).toReportNoErrors() diff --git a/test/browser/test/commands.test.ts b/test/browser/test/commands.test.ts index 7ac2c57f4..246bf0ae1 100644 --- a/test/browser/test/commands.test.ts +++ b/test/browser/test/commands.test.ts @@ -60,5 +60,7 @@ declare module '@vitest/browser/context' { }> stripVTControlCharacters: (text: string) => Promise + startTrace: () => Promise + stopTrace: () => Promise } } diff --git a/test/browser/test/timers.test.ts b/test/browser/test/timers.test.ts index eff593353..c2e30b7d9 100644 --- a/test/browser/test/timers.test.ts +++ b/test/browser/test/timers.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import { afterEach, expect, it, vi } from 'vitest' afterEach(() => { @@ -18,12 +17,3 @@ it('only runs a setTimeout callback once (ever)', () => { vi.runAllTimers() expect(fn).toHaveBeenCalledTimes(1) }) - -it('console.time', async () => { - vi.useFakeTimers({ - toFake: ['Date', 'performance'], - }) - console.time('[console-time-fake]') - await new Promise(r => setTimeout(r, 500)) - console.timeEnd('[console-time-fake]') -}) diff --git a/test/browser/vitest.config.mts b/test/browser/vitest.config.mts index ae2b2ec1a..6353140dd 100644 --- a/test/browser/vitest.config.mts +++ b/test/browser/vitest.config.mts @@ -93,6 +93,12 @@ export default defineConfig({ commands: { myCustomCommand, stripVTControlCharacters, + async startTrace(ctx) { + await ctx.page.context().tracing.start({ screenshots: true, snapshots: true }) + }, + async stopTrace(ctx) { + await ctx.page.context().tracing.stop({ path: 'trace.zip' }) + }, }, }, alias: { @@ -107,6 +113,11 @@ export default defineConfig({ env: { BROWSER: browser, }, + onConsoleLog(log) { + if (log.includes('MESSAGE ADDED')) { + return false + } + }, }, plugins: [ { diff --git a/test/browser/vitest.config.unit.mts b/test/browser/vitest.config.unit.mts index e217ab15c..1affddbd9 100644 --- a/test/browser/vitest.config.unit.mts +++ b/test/browser/vitest.config.unit.mts @@ -1,11 +1,13 @@ +import type { TestSpecification } from 'vitest/node' import { defineConfig } from 'vitest/config' export default defineConfig({ test: { include: ['specs/**/*.{spec,test}.ts'], + pool: 'threads', poolOptions: { - forks: { - singleFork: true, + threads: { + singleThread: true, }, }, reporters: 'verbose', @@ -13,5 +15,19 @@ export default defineConfig({ // 3 is the maximum of browser instances - in a perfect world they will run in parallel hookTimeout: process.env.CI ? 120_000 * 3 : 20_000, testTimeout: process.env.CI ? 120_000 * 3 : 20_000, + sequence: { + sequencer: class Sequencer { + sort(specifications: TestSpecification[]) { + return specifications.sort((spec1, spec2) => { + // just sort by name, ignore the cache optimization + return spec1.moduleId.localeCompare(spec2.moduleId) + }) + } + + shard(): TestSpecification[] { + throw new Error('not supported') + } + }, + }, }, })