From 0f44d2c81f3486819fd875384f4ef187667ebac5 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 28 Mar 2023 11:13:48 +0200 Subject: [PATCH] feat: implement `istanbul` coverage support for browser testing (#3040) Co-authored-by: AriPerkkio --- packages/browser/src/client/main.ts | 18 +- packages/browser/src/client/runner.ts | 12 +- packages/browser/src/node/index.ts | 5 + packages/coverage-c8/rollup.config.js | 1 + packages/coverage-c8/src/index.ts | 13 +- packages/coverage-istanbul/rollup.config.js | 1 + packages/coverage-istanbul/src/index.ts | 9 +- packages/vitest/src/api/setup.ts | 3 + packages/vitest/src/api/types.ts | 3 +- packages/vitest/src/browser.ts | 1 + .../vitest/src/integrations/browser/server.ts | 9 +- packages/vitest/src/integrations/coverage.ts | 7 +- packages/vitest/src/node/config.ts | 10 + packages/vitest/src/node/core.ts | 6 +- .../src/node/plugins/coverageTransform.ts | 3 +- pnpm-lock.yaml | 272 +++++++++++++++--- test/browser/package.json | 2 +- test/browser/specs/runner.test.mjs | 12 +- test/browser/src/createNode.ts | 6 + test/browser/test/dom.test.ts | 7 +- test/browser/tsconfig.json | 7 +- .../{vitest.config.ts => vitest.config.mts} | 9 +- test/config/test/failures.test.ts | 6 + test/coverage-test/package.json | 7 +- test/coverage-test/test/coverage.test.ts | 10 +- test/coverage-test/testing.mjs | 18 +- test/watch/package.json | 4 +- test/watch/test/file-watching.test.ts | 58 ++-- test/watch/test/stdin.test.ts | 24 +- test/watch/test/stdout.test.ts | 14 +- test/watch/test/utils.ts | 49 ++-- tsconfig.json | 3 +- 32 files changed, 458 insertions(+), 151 deletions(-) create mode 100644 test/browser/src/createNode.ts rename test/browser/{vitest.config.ts => vitest.config.mts} (75%) diff --git a/packages/browser/src/client/main.ts b/packages/browser/src/client/main.ts index 9633e46b07dc..43fed3c83bc5 100644 --- a/packages/browser/src/client/main.ts +++ b/packages/browser/src/client/main.ts @@ -57,6 +57,8 @@ ws.addEventListener('open', async () => { const { getSafeTimers } = await importId('vitest/utils') as typeof import('vitest/utils') const safeRpc = createSafeRpc(client, getSafeTimers) + // @ts-expect-error untyped global for internal use + globalThis.__vitest_browser__ = true // @ts-expect-error mocking vitest apis globalThis.__vitest_worker__ = { config, @@ -82,11 +84,20 @@ async function runTests(paths: string[], config: any) { const viteClientPath = '/@vite/client' await import(viteClientPath) - const { startTests, setupCommonEnv, setupSnapshotEnvironment } = await importId('vitest/browser') as typeof import('vitest/browser') + const { + startTests, + setupCommonEnv, + setupSnapshotEnvironment, + takeCoverageInsideWorker, + } = await importId('vitest/browser') as typeof import('vitest/browser') + + const executor = { + executeId: (id: string) => importId(id), + } if (!runner) { const { VitestTestRunner } = await importId('vitest/runners') as typeof import('vitest/runners') - const BrowserRunner = createBrowserRunner(VitestTestRunner) + const BrowserRunner = createBrowserRunner(VitestTestRunner, { takeCoverage: () => takeCoverageInsideWorker(config.coverage, executor) }) runner = new BrowserRunner({ config, browserHashMap }) } @@ -104,7 +115,8 @@ async function runTests(paths: string[], config: any) { const now = `${new Date().getTime()}` files.forEach(i => browserHashMap.set(i, now)) - await startTests(files, runner) + for (const file of files) + await startTests([file], runner) } finally { await rpcDone() diff --git a/packages/browser/src/client/runner.ts b/packages/browser/src/client/runner.ts index 3e3a3a536e76..3bdc40c1f0d2 100644 --- a/packages/browser/src/client/runner.ts +++ b/packages/browser/src/client/runner.ts @@ -7,7 +7,11 @@ interface BrowserRunnerOptions { browserHashMap: Map } -export function createBrowserRunner(original: any) { +interface CoverageHandler { + takeCoverage: () => Promise +} + +export function createBrowserRunner(original: any, coverageModule: CoverageHandler | null) { return class BrowserTestRunner extends original { public config: ResolvedConfig hashMap = new Map() @@ -25,6 +29,12 @@ export function createBrowserRunner(original: any) { }) } + async onAfterRunSuite() { + await super.onAfterRunSuite?.() + const coverage = await coverageModule?.takeCoverage?.() + await rpc().onAfterSuiteRun({ coverage }) + } + onCollected(files: File[]): unknown { return rpc().onCollected(files) } diff --git a/packages/browser/src/node/index.ts b/packages/browser/src/node/index.ts index 7b089537f8c4..97dc7d9027e2 100644 --- a/packages/browser/src/node/index.ts +++ b/packages/browser/src/node/index.ts @@ -18,6 +18,11 @@ export default (base = '/'): Plugin[] => { { enforce: 'pre', name: 'vitest:browser', + async config(viteConfig) { + // Enables using ignore hint for coverage providers with @preserve keyword + viteConfig.esbuild ||= {} + viteConfig.esbuild.legalComments = 'inline' + }, async configureServer(server) { server.middlewares.use( base, diff --git a/packages/coverage-c8/rollup.config.js b/packages/coverage-c8/rollup.config.js index cbee35392bc8..d8c1160ca43c 100644 --- a/packages/coverage-c8/rollup.config.js +++ b/packages/coverage-c8/rollup.config.js @@ -10,6 +10,7 @@ import pkg from './package.json' const entries = { index: 'src/index.ts', + provider: 'src/provider.ts', } const external = [ diff --git a/packages/coverage-c8/src/index.ts b/packages/coverage-c8/src/index.ts index ddc09359a394..ea6998eef5c1 100644 --- a/packages/coverage-c8/src/index.ts +++ b/packages/coverage-c8/src/index.ts @@ -1,6 +1,11 @@ -export * from './takeCoverage' +import * as coverage from './takeCoverage' -export async function getProvider() { - const { C8CoverageProvider } = await import('./provider') - return new C8CoverageProvider() +export default { + ...coverage, + async getProvider() { + // to not bundle the provider + const name = './provider.js' + const { C8CoverageProvider } = await import(name) as typeof import('./provider') + return new C8CoverageProvider() + }, } diff --git a/packages/coverage-istanbul/rollup.config.js b/packages/coverage-istanbul/rollup.config.js index 0372998f1117..480e4cb4132a 100644 --- a/packages/coverage-istanbul/rollup.config.js +++ b/packages/coverage-istanbul/rollup.config.js @@ -10,6 +10,7 @@ import pkg from './package.json' const entries = { index: 'src/index.ts', + provider: 'src/provider.ts', } const external = [ diff --git a/packages/coverage-istanbul/src/index.ts b/packages/coverage-istanbul/src/index.ts index de791bc69b33..9cf5c9e28650 100644 --- a/packages/coverage-istanbul/src/index.ts +++ b/packages/coverage-istanbul/src/index.ts @@ -1,7 +1,9 @@ import { COVERAGE_STORE_KEY } from './constants' export async function getProvider() { - const { IstanbulCoverageProvider } = await import('./provider') + // to not bundle the provider + const providerPath = './provider.js' + const { IstanbulCoverageProvider } = await import(providerPath) as typeof import('./provider') return new IstanbulCoverageProvider() } @@ -15,3 +17,8 @@ export function takeCoverage() { return coverage } + +export default { + getProvider, + takeCoverage, +} diff --git a/packages/vitest/src/api/setup.ts b/packages/vitest/src/api/setup.ts index 614c13899407..665c8af26e4b 100644 --- a/packages/vitest/src/api/setup.ts +++ b/packages/vitest/src/api/setup.ts @@ -46,6 +46,9 @@ export function setup(ctx: Vitest, server?: ViteDevServer) { ctx.state.updateTasks(packs) await ctx.report('onTaskUpdate', packs) }, + onAfterSuiteRun(meta) { + ctx.coverageProvider?.onAfterSuiteRun(meta) + }, getFiles() { return ctx.state.getFiles() }, diff --git a/packages/vitest/src/api/types.ts b/packages/vitest/src/api/types.ts index aa7e1f09cb45..b50a30470eb9 100644 --- a/packages/vitest/src/api/types.ts +++ b/packages/vitest/src/api/types.ts @@ -1,5 +1,5 @@ import type { TransformResult } from 'vite' -import type { File, ModuleGraphData, Reporter, ResolvedConfig, SnapshotResult, TaskResultPack, UserConsoleLog } from '../types' +import type { AfterSuiteRunMeta, File, ModuleGraphData, Reporter, ResolvedConfig, SnapshotResult, TaskResultPack, UserConsoleLog } from '../types' export interface TransformResultWithSource extends TransformResult { source?: string @@ -8,6 +8,7 @@ export interface TransformResultWithSource extends TransformResult { export interface WebSocketHandlers { onCollected(files?: File[]): Promise onTaskUpdate(packs: TaskResultPack[]): void + onAfterSuiteRun(meta: AfterSuiteRunMeta): void onDone(name: string): void sendLog(log: UserConsoleLog): void getFiles(): File[] diff --git a/packages/vitest/src/browser.ts b/packages/vitest/src/browser.ts index 27eabf5e6121..954310e8d65b 100644 --- a/packages/vitest/src/browser.ts +++ b/packages/vitest/src/browser.ts @@ -1,3 +1,4 @@ export { startTests } from '@vitest/runner' export { setupCommonEnv } from './runtime/setup.common' export { setupSnapshotEnvironment } from './integrations/snapshot/env' +export { takeCoverageInsideWorker, stopCoverageInsideWorker, getCoverageProvider, startCoverageInsideWorker } from './integrations/coverage' diff --git a/packages/vitest/src/integrations/browser/server.ts b/packages/vitest/src/integrations/browser/server.ts index 61c471ee1a02..61ef7bad1967 100644 --- a/packages/vitest/src/integrations/browser/server.ts +++ b/packages/vitest/src/integrations/browser/server.ts @@ -6,6 +6,7 @@ import type { Vitest } from '../../node' import type { UserConfig } from '../../types/config' import { ensurePackageInstalled } from '../../node/pkg' import { resolveApiServerConfig } from '../../node/config' +import { CoverageTransform } from '../../node/plugins/coverageTransform' export async function createBrowserServer(ctx: Vitest, options: UserConfig) { const root = ctx.config.root @@ -31,6 +32,7 @@ export async function createBrowserServer(ctx: Vitest, options: UserConfig) { }, plugins: [ (await import('@vitest/browser')).default('/'), + CoverageTransform(ctx), { enforce: 'post', name: 'vitest:browser:config', @@ -44,7 +46,6 @@ export async function createBrowserServer(ctx: Vitest, options: UserConfig) { config.optimizeDeps ??= {} config.optimizeDeps.entries ??= [] - const root = config.root || process.cwd() const [...entries] = await ctx.globAllTestFiles(ctx.config, ctx.config.dir || root) entries.push(...ctx.config.setupFiles) @@ -52,6 +53,12 @@ export async function createBrowserServer(ctx: Vitest, options: UserConfig) { config.optimizeDeps.entries = [config.optimizeDeps.entries] config.optimizeDeps.entries.push(...entries) + + return { + resolve: { + alias: config.test?.alias, + }, + } }, }, ], diff --git a/packages/vitest/src/integrations/coverage.ts b/packages/vitest/src/integrations/coverage.ts index 914cbdcf6054..95b81af656ad 100644 --- a/packages/vitest/src/integrations/coverage.ts +++ b/packages/vitest/src/integrations/coverage.ts @@ -1,4 +1,3 @@ -import { importModule } from 'local-pkg' import type { CoverageOptions, CoverageProvider, CoverageProviderModule } from '../types' interface Loader { @@ -16,8 +15,10 @@ async function resolveCoverageProviderModule(options: CoverageOptions | undefine const provider = options.provider - if (provider === 'c8' || provider === 'istanbul') - return await importModule(CoverageProviderMap[provider]) + if (provider === 'c8' || provider === 'istanbul') { + const { default: coverageModule } = await loader.executeId(CoverageProviderMap[provider]) + return coverageModule + } let customProviderModule diff --git a/packages/vitest/src/node/config.ts b/packages/vitest/src/node/config.ts index 1f4e81f554dd..0e37c092b635 100644 --- a/packages/vitest/src/node/config.ts +++ b/packages/vitest/src/node/config.ts @@ -113,6 +113,9 @@ export function resolveConfig( } } + if (resolved.coverage.provider === 'c8' && resolved.coverage.enabled && isBrowserEnabled(resolved)) + throw new Error('@vitest/coverage-c8 does not work with --browser. Use @vitest/coverage-istanbul instead') + resolved.deps = resolved.deps || {} // vitenode will try to import such file with native node, // but then our mocker will not work properly @@ -263,3 +266,10 @@ export function resolveConfig( return resolved } + +export function isBrowserEnabled(config: ResolvedConfig) { + if (config.browser.enabled) + return true + + return config.poolMatchGlobs?.length && config.poolMatchGlobs.some(([, pool]) => pool === 'browser') +} diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index 28fd156c6b25..607ee65d050f 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -18,7 +18,7 @@ import { createPool } from './pool' import type { ProcessPool } from './pool' import { createBenchmarkReporters, createReporters } from './reporters/utils' import { StateManager } from './state' -import { resolveConfig } from './config' +import { isBrowserEnabled, resolveConfig } from './config' import { Logger } from './logger' import { VitestCache } from './cache' import { VitestServer } from './server' @@ -725,9 +725,7 @@ export class Vitest { } isBrowserEnabled() { - if (this.config.browser.enabled) - return true - return this.config.poolMatchGlobs?.length && this.config.poolMatchGlobs.some(([, pool]) => pool === 'browser') + return isBrowserEnabled(this.config) } // The server needs to be running for communication diff --git a/packages/vitest/src/node/plugins/coverageTransform.ts b/packages/vitest/src/node/plugins/coverageTransform.ts index f31d19762e87..eb368f570471 100644 --- a/packages/vitest/src/node/plugins/coverageTransform.ts +++ b/packages/vitest/src/node/plugins/coverageTransform.ts @@ -1,4 +1,5 @@ import type { Plugin as VitePlugin } from 'vite' +import { normalizeRequestId } from 'vite-node/utils' import type { Vitest } from '../core' @@ -6,7 +7,7 @@ export function CoverageTransform(ctx: Vitest): VitePlugin | null { return { name: 'vitest:coverage-transform', transform(srcCode, id) { - return ctx.coverageProvider?.onFileTransform?.(srcCode, id, this) + return ctx.coverageProvider?.onFileTransform?.(srcCode, normalizeRequestId(id), this) }, } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fc078fcee01d..cc1705d1c0b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -240,8 +240,8 @@ importers: react-dom: 18.0.0_react@18.0.0 devDependencies: '@testing-library/react': 13.3.0_zpnidt7m3osuk7shl3s4oenomq - '@types/node': 18.15.7 - '@types/react': 18.0.28 + '@types/node': 18.15.10 + '@types/react': 18.0.29 '@vitejs/plugin-react': 3.1.0 jsdom: 21.1.1 typescript: 4.8.4 @@ -1088,18 +1088,22 @@ importers: test/coverage-test: specifiers: '@vitejs/plugin-vue': latest + '@vitest/browser': workspace:* '@vue/test-utils': latest happy-dom: latest vite: ^4.0.0 vitest: workspace:* vue: latest + webdriverio: latest devDependencies: '@vitejs/plugin-vue': 4.1.0_vite@4.0.0+vue@3.2.47 + '@vitest/browser': link:../../packages/browser '@vue/test-utils': 2.3.2_vue@3.2.47 happy-dom: 8.9.0 vite: 4.0.0 vitest: link:../../packages/vitest vue: 3.2.47 + webdriverio: 8.6.8 test/css: specifiers: @@ -1310,15 +1314,19 @@ importers: test/watch: specifiers: + '@vitest/browser': workspace:* execa: ^7.0.0 strip-ansi: ^7.0.1 vite: ^4.0.0 vitest: workspace:* + webdriverio: latest devDependencies: + '@vitest/browser': link:../../packages/browser execa: 7.0.0 strip-ansi: 7.0.1 vite: 4.0.0 vitest: link:../../packages/vitest + webdriverio: 8.6.8 test/web-worker: specifiers: @@ -5057,7 +5065,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.15.7 + '@types/node': 18.15.10 '@types/yargs': 15.0.14 chalk: 4.1.2 dev: true @@ -5069,7 +5077,7 @@ packages: '@jest/schemas': 29.0.0 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.15.7 + '@types/node': 18.15.10 '@types/yargs': 17.0.12 chalk: 4.1.2 dev: true @@ -5581,7 +5589,7 @@ packages: engines: {node: '>=14'} hasBin: true dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 playwright-core: 1.28.0 dev: true @@ -7419,7 +7427,7 @@ packages: /@types/cheerio/0.22.31: resolution: {integrity: sha512-Kt7Cdjjdi2XWSfrZ53v4Of0wG3ZcmaegFXjMmz9tfNrZSkzzo36G0AL1YqSdcIA78Etjt6E609pt5h1xnQkPUw==} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 dev: true /@types/codemirror/5.60.6: @@ -7454,7 +7462,7 @@ packages: resolution: {integrity: sha512-xryQlOEIe1TduDWAOphR0ihfebKFSWOXpIsk+70JskCfRfW+xALdnJ0r1ZOTo85F9Qsjk6vtlU7edTYHbls9tA==} dependencies: '@types/cheerio': 0.22.31 - '@types/react': 18.0.28 + '@types/react': 18.0.29 dev: true /@types/eslint-scope/3.7.4: @@ -7486,33 +7494,33 @@ packages: resolution: {integrity: sha512-zdV5odfHf95B4qr6bdpshG4VMm/3xgnPhSJLa3xh75CYr35e34k+4FQli82Q48sPqwHazJGy+6+jl4T+Vw1AMg==} dependencies: '@types/jsonfile': 6.1.1 - '@types/node': 18.15.7 + '@types/node': 18.15.10 dev: true /@types/fs-extra/9.0.13: resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 dev: true /@types/glob/7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.1 - '@types/node': 18.15.7 + '@types/node': 18.15.10 dev: true /@types/glob/8.0.0: resolution: {integrity: sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==} dependencies: '@types/minimatch': 5.1.1 - '@types/node': 18.15.7 + '@types/node': 18.15.10 dev: true /@types/graceful-fs/4.1.5: resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 dev: true /@types/hast/2.3.4: @@ -7577,7 +7585,7 @@ packages: /@types/jsdom/21.1.0: resolution: {integrity: sha512-leWreJOdnuIxq9Y70tBVm/bvTuh31DSlF/r4l7Cfi4uhVQqLHD0Q4v301GMisEMwwbMgF7ZKxuZ+Jbd4NcdmRw==} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 '@types/tough-cookie': 4.0.2 parse5: 7.1.1 dev: true @@ -7593,7 +7601,7 @@ packages: /@types/jsonfile/6.1.1: resolution: {integrity: sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 dev: true /@types/lodash/4.14.191: @@ -7627,7 +7635,7 @@ packages: /@types/node-fetch/2.6.2: resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 form-data: 3.0.1 dev: true @@ -7643,8 +7651,8 @@ packages: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} dev: true - /@types/node/18.15.7: - resolution: {integrity: sha512-LFmUbFunqmBn26wJZgZPYZPrDR1RwGOu2v79Mgcka1ndO6V0/cwjivPTc4yoK6n9kmw4/ls1r8cLrvh2iMibFA==} + /@types/node/18.15.10: + resolution: {integrity: sha512-9avDaQJczATcXgfmMAW3MIWArOO7A+m90vuCFLr8AotWf8igO/mRoYukrk2cqZVtv38tHs33retzHEilM7FpeQ==} dev: true /@types/node/18.7.13: @@ -7677,7 +7685,7 @@ packages: /@types/prompts/2.4.2: resolution: {integrity: sha512-TwNx7qsjvRIUv/BCx583tqF5IINEVjCNqg9ofKHRlSoUHE62WBHrem4B1HGXcIrG511v29d1kJ9a/t2Esz7MIg==} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 kleur: 3.0.3 dev: true @@ -7697,19 +7705,19 @@ packages: /@types/react-dom/18.0.6: resolution: {integrity: sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==} dependencies: - '@types/react': 18.0.28 + '@types/react': 18.0.29 dev: true /@types/react-dom/18.0.8: resolution: {integrity: sha512-C3GYO0HLaOkk9dDAz3Dl4sbe4AKUGTCfFIZsz3n/82dPNN8Du533HzKatDxeUYWu24wJgMP1xICqkWk1YOLOIw==} dependencies: - '@types/react': 18.0.28 + '@types/react': 18.0.29 dev: true /@types/react-is/17.0.3: resolution: {integrity: sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==} dependencies: - '@types/react': 18.0.28 + '@types/react': 18.0.29 dev: false /@types/react-test-renderer/17.0.2: @@ -7721,7 +7729,7 @@ packages: /@types/react-transition-group/4.4.5: resolution: {integrity: sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==} dependencies: - '@types/react': 18.0.28 + '@types/react': 18.0.29 dev: false /@types/react/17.0.49: @@ -7740,8 +7748,8 @@ packages: csstype: 3.1.0 dev: true - /@types/react/18.0.28: - resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==} + /@types/react/18.0.29: + resolution: {integrity: sha512-wXHktgUABxplw1+UnljseDq4+uztQyp2tlWZRIxHlpchsCFqiYkvaDS8JR7eKOQm8wziTH/el5qL7D6gYNkYcw==} dependencies: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.2 @@ -7750,7 +7758,7 @@ packages: /@types/resolve/1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 dev: true /@types/resolve/1.20.2: @@ -7767,7 +7775,7 @@ packages: /@types/set-cookie-parser/2.4.2: resolution: {integrity: sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w==} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 dev: true /@types/sinonjs__fake-timers/8.1.1: @@ -7841,7 +7849,7 @@ packages: /@types/webpack-sources/3.2.0: resolution: {integrity: sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 '@types/source-list-map': 0.1.2 source-map: 0.7.4 dev: true @@ -7849,7 +7857,7 @@ packages: /@types/webpack/4.41.32: resolution: {integrity: sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 '@types/tapable': 1.0.8 '@types/uglify-js': 3.17.0 '@types/webpack-sources': 3.2.0 @@ -7864,7 +7872,7 @@ packages: /@types/ws/8.5.4: resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 dev: true /@types/yargs-parser/21.0.0: @@ -7887,7 +7895,7 @@ packages: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 dev: true optional: true @@ -8871,6 +8879,20 @@ packages: read-pkg-up: 9.1.0 dev: true + /@wdio/config/8.6.8: + resolution: {integrity: sha512-2gNL5AtJgKnZ3o4mM9KJpKP0fL2QX5J+Iepn7TSI5QEzC/c9f6UgjR14hoRLE0rnzQm6ZvXHj22d0hvT3ilafg==} + engines: {node: ^16.13 || >=18} + dependencies: + '@wdio/logger': 8.6.6 + '@wdio/types': 8.6.8 + '@wdio/utils': 8.6.8 + decamelize: 6.0.0 + deepmerge-ts: 5.0.0 + glob: 9.3.0 + import-meta-resolve: 2.2.2 + read-pkg-up: 9.1.0 + dev: true + /@wdio/logger/8.1.0: resolution: {integrity: sha512-QRC5b7FF4JIYUCqggnVA0sZ80TwIUFN9JyBSbuGuMxaSLSLujSo7WfuSrnQXVvsRbnJ16wWwJWYigfLkxOW86Q==} engines: {node: ^16.13 || >=18} @@ -8881,22 +8903,50 @@ packages: strip-ansi: 6.0.1 dev: true + /@wdio/logger/8.6.6: + resolution: {integrity: sha512-MS+Y5yqFGx2zVXMOfuBQAVdFsP4DuYz+/hM552xwiDWjGg6EZHoccqUYgH3J5zpu3JFpYV3R/a5jExFiGGck6g==} + engines: {node: ^16.13 || >=18} + dependencies: + chalk: 5.2.0 + loglevel: 1.8.1 + loglevel-plugin-prefix: 0.8.4 + strip-ansi: 6.0.1 + dev: true + /@wdio/protocols/8.5.7: resolution: {integrity: sha512-ymdXSRqHugEptLdjLnvX7m7TY6cvae1B1yiFJVpaKwj88s4PaCUs7aISexC0ES9s6z8ZWRjZo3mrKPlwZ/IKCw==} dev: true + /@wdio/protocols/8.6.6: + resolution: {integrity: sha512-RC7cwvLvtxaQGNC+m5YwwPO/p+NxM0HVoJ5DC/udoJgNiEZay75rtFqpKsUxHq7DU5IKYAZV1FzVQJ776EZUfg==} + dev: true + /@wdio/repl/8.1.0: resolution: {integrity: sha512-96G4TzbYnRf95+GURo15FYt6iTYq85nbWU6YQedLRAV15RfSp4foKTbAnq++bKKMALNL6gdzTc+HGhQr3Q0sQg==} engines: {node: ^16.13 || >=18} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 + dev: true + + /@wdio/repl/8.6.6: + resolution: {integrity: sha512-vyJzqHJ5yOmfVyk5WWo6pRsJ2xhgWl3DVIBdDNR0wKrtFcm/g1jnB+pNf6Eb7NhCDh3oGul25bmhAwWDoxcFYA==} + engines: {node: ^16.13 || >=18} + dependencies: + '@types/node': 18.15.10 dev: true /@wdio/types/8.4.0: resolution: {integrity: sha512-1eA0D0jS8Ttg67zB2gsZJFUcHcRz4VRjLTjxdLKh70+ZfB1+YZr9tScLgQjc+qsjsK1wKSzOz03uZiidwNnN9g==} engines: {node: ^16.13 || >=18} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 + dev: true + + /@wdio/types/8.6.8: + resolution: {integrity: sha512-hwlkQ6E8DNIFL/l8vHve3Zpl1t6Hqle7vtatEkAlrmbnExc7qI6Yw6SI5T/KiBSAi0ez1OypbGhdrbXFfywxng==} + engines: {node: ^16.13 || >=18} + dependencies: + '@types/node': 18.15.10 dev: true /@wdio/utils/8.6.1: @@ -8909,6 +8959,16 @@ packages: p-iteration: 1.1.8 dev: true + /@wdio/utils/8.6.8: + resolution: {integrity: sha512-ibhuEGBwkRoy3hqKb7pXGqJkH8vSqSsTyYdxQ8cGJYFfOIvHJR1aTLz+az5zJMLNUUT6WItRBwa5Rad0hlWbFw==} + engines: {node: ^16.13 || >=18} + dependencies: + '@wdio/logger': 8.6.6 + '@wdio/types': 8.6.8 + import-meta-resolve: 2.2.2 + p-iteration: 1.1.8 + dev: true + /@webassemblyjs/ast/1.11.1: resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==} dependencies: @@ -10775,7 +10835,7 @@ packages: engines: {node: '>=12.13.0'} hasBin: true dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.3.0 @@ -10797,6 +10857,15 @@ packages: mitt: 3.0.0 dev: true + /chromium-bidi/0.4.5_6o5gdkn34s2j2m26x63ssheuqa: + resolution: {integrity: sha512-rkav9YzRfAshSTG3wNXF7P7yNiI29QAo1xBXElPoCoSQR5n20q3cOyVhDv6S7+GlF/CJ/emUxlQiR0xOPurkGg==} + peerDependencies: + devtools-protocol: '*' + dependencies: + devtools-protocol: 0.0.1094867 + mitt: 3.0.0 + dev: true + /ci-info/2.0.0: resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} dev: true @@ -11817,6 +11886,11 @@ packages: engines: {node: '>=12.4.0'} dev: true + /deepmerge-ts/5.0.0: + resolution: {integrity: sha512-esq9xUO8+CQCG63IlpkoOBNlpm1m4WBm0NRLFrGL/dcgzqWi1tmTLfG7QTvffqYt6T+dS+xaxrHxdexqGWkV1g==} + engines: {node: '>=16.0.0'} + dev: true + /deepmerge/4.2.2: resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} engines: {node: '>=0.10.0'} @@ -11960,6 +12034,10 @@ packages: resolution: {integrity: sha512-jjWlaRN+ntWqV4bYlkg7lzIAXTnzFlznLiSj2kTPZVOHX+t0FW6gCPsgjjQA/4wray7PdbWZ09lMlkCuVWvtZA==} dev: true + /devtools-protocol/0.0.1119014: + resolution: {integrity: sha512-ewXoFOkLIpKqsiZ19Rm8l46FqDlj+yACbysDIKlUWqTbZ4BBovWmoCegkLHbfTSjGrrLdaXkD1EKA4VF0O9q3g==} + dev: true + /devtools-protocol/0.0.981744: resolution: {integrity: sha512-0cuGS8+jhR67Fy7qG3i3Pc7Aw494sb9yG9QgpG97SFVWwolgYjlhJg7n+UaHxOQT30d1TYu/EYe9k01ivLErIg==} dev: true @@ -11968,7 +12046,7 @@ packages: resolution: {integrity: sha512-dqR5SUsqpJBkY+SpzSPyRwAdwn3PdoCFjgaRqO2jkF0IXgvBkKsXSepAN2QV4/nbcFXB6mokJZ0rlHSzVl7GkQ==} engines: {node: ^16.13 || >=18} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 '@wdio/config': 8.6.2 '@wdio/logger': 8.1.0 '@wdio/protocols': 8.5.7 @@ -11990,6 +12068,32 @@ packages: - utf-8-validate dev: true + /devtools/8.6.8: + resolution: {integrity: sha512-4TzEf6jbURKlCX6zwvYgkGInGTnPgKvd3OyHrBDI8pQWdzdvyL8gBO90UY+zjpFXANT+xZMYDX7aq+txCOeGqA==} + engines: {node: ^16.13 || >=18} + dependencies: + '@types/node': 18.15.10 + '@wdio/config': 8.6.8 + '@wdio/logger': 8.6.6 + '@wdio/protocols': 8.6.6 + '@wdio/types': 8.6.8 + '@wdio/utils': 8.6.8 + chrome-launcher: 0.15.1 + edge-paths: 3.0.5 + import-meta-resolve: 2.2.2 + puppeteer-core: 19.7.5 + query-selector-shadow-dom: 1.0.1 + ua-parser-js: 1.0.34 + uuid: 9.0.0 + which: 3.0.0 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - typescript + - utf-8-validate + dev: true + /dezalgo/1.0.3: resolution: {integrity: sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==} dependencies: @@ -15333,7 +15437,7 @@ packages: dependencies: '@jest/types': 26.6.2 '@types/graceful-fs': 4.1.5 - '@types/node': 18.15.7 + '@types/node': 18.15.10 anymatch: 3.1.2 fb-watchman: 2.0.1 graceful-fs: 4.2.10 @@ -15401,7 +15505,7 @@ packages: resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==} engines: {node: '>= 10.14.2'} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 graceful-fs: 4.2.10 dev: true @@ -15410,7 +15514,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 18.15.7 + '@types/node': 18.15.10 chalk: 4.1.2 graceful-fs: 4.2.10 is-ci: 2.0.0 @@ -15422,7 +15526,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.0.1 - '@types/node': 18.15.7 + '@types/node': 18.15.10 chalk: 4.1.2 ci-info: 3.7.0 graceful-fs: 4.2.10 @@ -15433,7 +15537,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 merge-stream: 2.0.0 supports-color: 7.2.0 dev: true @@ -15442,7 +15546,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true @@ -18398,6 +18502,33 @@ packages: - utf-8-validate dev: true + /puppeteer-core/19.7.5: + resolution: {integrity: sha512-EJuNha+SxPfaYFbkoWU80H3Wb1SiQH5fFyb2xdbWda0ziax5mhV63UMlqNfPeTDIWarwtR4OIcq/9VqY8HPOsg==} + engines: {node: '>=14.14.0'} + peerDependencies: + typescript: '>= 4.7.4' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + chromium-bidi: 0.4.5_6o5gdkn34s2j2m26x63ssheuqa + cross-fetch: 3.1.5 + debug: 4.3.4 + devtools-protocol: 0.0.1094867 + extract-zip: 2.0.1 + https-proxy-agent: 5.0.1 + proxy-from-env: 1.1.0 + rimraf: 4.4.0 + tar-fs: 2.1.1 + unbzip2-stream: 1.4.3 + ws: 8.12.1 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: true + /puppeteer/13.7.0: resolution: {integrity: sha512-U1uufzBjz3+PkpCxFrWzh4OrMIdIb2ztzCu0YEPfRHjHswcSwHZswnK+WdsOQJsRV8WeTg3jLhJR4D867+fjsA==} engines: {node: '>=10.18.1'} @@ -22228,7 +22359,7 @@ packages: resolution: {integrity: sha512-jgnu/hB7O8hrjCZm6zW77QhBEdwteQF+liWvjZcflQXUmS/YLdS/UAfCBQzt1CIUHfbGrlxHshwFm+ywjT9XJw==} engines: {node: ^16.13 || >=18} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 '@types/ws': 8.5.4 '@wdio/config': 8.6.2 '@wdio/logger': 8.1.0 @@ -22244,11 +22375,31 @@ packages: - utf-8-validate dev: true + /webdriver/8.6.8: + resolution: {integrity: sha512-v+43Z4miGKa1JaFAIxgK5AedBgHV/7MI+jd3fJao24R/KWYNgC9GD4BUD6LxC7AChuyRdA1/cN3NjwrPTg0ujg==} + engines: {node: ^16.13 || >=18} + dependencies: + '@types/node': 18.15.10 + '@types/ws': 8.5.4 + '@wdio/config': 8.6.8 + '@wdio/logger': 8.6.6 + '@wdio/protocols': 8.6.6 + '@wdio/types': 8.6.8 + '@wdio/utils': 8.6.8 + deepmerge-ts: 5.0.0 + got: 12.6.0 + ky: 0.33.3 + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + /webdriverio/8.6.3_typescript@4.9.4: resolution: {integrity: sha512-XhS6lydSyCLnJcOBhLbSK6J0MQdfnSXaFcSvSEL10TMBnWcmETCfYIj7OX41weDI8F4YAasYyMH+mJUkWwC1Ww==} engines: {node: ^16.13 || >=18} dependencies: - '@types/node': 18.15.7 + '@types/node': 18.15.10 '@wdio/config': 8.6.2 '@wdio/logger': 8.1.0 '@wdio/protocols': 8.5.7 @@ -22281,6 +22432,43 @@ packages: - utf-8-validate dev: true + /webdriverio/8.6.8: + resolution: {integrity: sha512-U1+O8zGx3yaGGf3RMGIaYlApdlmDX3AQ1qF8JX3mFqq2pvAxGSB6h6yDST8aJajzQv0sp+eXLER+O/rlEVwiOQ==} + engines: {node: ^16.13 || >=18} + dependencies: + '@types/node': 18.15.10 + '@wdio/config': 8.6.8 + '@wdio/logger': 8.6.6 + '@wdio/protocols': 8.6.6 + '@wdio/repl': 8.6.6 + '@wdio/types': 8.6.8 + '@wdio/utils': 8.6.8 + archiver: 5.3.1 + aria-query: 5.0.2 + css-shorthand-properties: 1.1.1 + css-value: 0.0.1 + devtools: 8.6.8 + devtools-protocol: 0.0.1119014 + grapheme-splitter: 1.0.4 + import-meta-resolve: 2.2.2 + is-plain-obj: 4.1.0 + lodash.clonedeep: 4.5.0 + lodash.zip: 4.2.0 + minimatch: 7.4.2 + puppeteer-core: 19.7.5 + query-selector-shadow-dom: 1.0.1 + resq: 1.11.0 + rgb2hex: 0.2.5 + serialize-error: 8.1.0 + webdriver: 8.6.8 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - typescript + - utf-8-validate + dev: true + /webidl-conversions/3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} diff --git a/test/browser/package.json b/test/browser/package.json index 7ea9aa8f090b..c2477b82c80f 100644 --- a/test/browser/package.json +++ b/test/browser/package.json @@ -6,7 +6,7 @@ "test": "pnpm run test:webdriverio && pnpm run test:playwright", "test:webdriverio": "PROVIDER=webdriverio node --test specs/", "test:playwright": "PROVIDER=playwright node --test specs/", - "coverage": "vitest run --coverage" + "coverage": "vitest --coverage.enabled --coverage.provider=istanbul" }, "devDependencies": { "@vitest/browser": "workspace:*", diff --git a/test/browser/specs/runner.test.mjs b/test/browser/specs/runner.test.mjs index 52597aae3b26..b6a0d460a02d 100644 --- a/test/browser/specs/runner.test.mjs +++ b/test/browser/specs/runner.test.mjs @@ -3,9 +3,9 @@ import { readFile } from 'node:fs/promises' import test from 'node:test' import { execa } from 'execa' -const browser = process.env.BROWSER || 'chrome' +const browser = process.env.BROWSER || (process.env.PROVIDER === 'playwright' ? 'chromium' : 'chrome') -const { stderr, stdout } = await execa('npx', ['vitest', `--browser=${browser}`], { +const { stderr, stdout } = await execa('npx', ['vitest', '--run', `--browser.name=${browser}`, '--browser.headless'], { env: { ...process.env, CI: 'true', @@ -13,7 +13,7 @@ const { stderr, stdout } = await execa('npx', ['vitest', `--browser=${browser}`] }, }) -test('tests are actually running', async () => { +await test('tests are actually running', async () => { const browserResult = await readFile('./browser.json', 'utf-8') const browserResultJson = JSON.parse(browserResult) @@ -23,7 +23,7 @@ test('tests are actually running', async () => { assert.ok(result.status === 'passed', `${result.name} has failed`) }) -test('logs are redirected to stdout', async () => { +await test('logs are redirected to stdout', async () => { assert.match(stdout, /stdout | test\/logs.test.ts > logging to stdout/) assert.match(stdout, /hello from console.log/, 'prints console.log') assert.match(stdout, /hello from console.info/, 'prints console.info') @@ -43,7 +43,7 @@ test('logs are redirected to stdout', async () => { assert.match(stdout, /time: [\d.]+ ms/, 'prints custom time') }) -test('logs are redirected to stderr', async () => { +await test('logs are redirected to stderr', async () => { assert.match(stderr, /stderr | test\/logs.test.ts > logging to stderr/) assert.match(stderr, /hello from console.error/, 'prints console.log') assert.match(stderr, /hello from console.warn/, 'prints console.info') @@ -51,7 +51,7 @@ test('logs are redirected to stderr', async () => { assert.match(stderr, /Timer "invalid timeEnd" does not exist/, 'prints errored timeEnd') }) -test('popup apis should log a warning', () => { +await test('popup apis should log a warning', () => { assert.ok(stderr.includes('Vitest encountered a \`alert\("test"\)\`'), 'prints warning for alert') assert.ok(stderr.includes('Vitest encountered a \`confirm\("test"\)\`'), 'prints warning for confirm') assert.ok(stderr.includes('Vitest encountered a \`prompt\("test"\)\`'), 'prints warning for prompt') diff --git a/test/browser/src/createNode.ts b/test/browser/src/createNode.ts new file mode 100644 index 000000000000..29f3a48891b2 --- /dev/null +++ b/test/browser/src/createNode.ts @@ -0,0 +1,6 @@ +export function createNode() { + const div = document.createElement('div') + div.textContent = 'Hello World' + document.body.appendChild(div) + return div +} diff --git a/test/browser/test/dom.test.ts b/test/browser/test/dom.test.ts index 12fd4e0fbc33..ec1825824c52 100644 --- a/test/browser/test/dom.test.ts +++ b/test/browser/test/dom.test.ts @@ -1,8 +1,7 @@ import { expect, test } from 'vitest' +import { createNode } from '#src/createNode' -test('render div', async () => { - const div = document.createElement('div') - div.textContent = 'Hello World' - document.body.appendChild(div) +test('renders div', () => { + const div = createNode() expect(div.textContent).toBe('Hello World') }) diff --git a/test/browser/tsconfig.json b/test/browser/tsconfig.json index 2f98042715ab..0810dce682a5 100644 --- a/test/browser/tsconfig.json +++ b/test/browser/tsconfig.json @@ -1,5 +1,10 @@ { "compilerOptions": { - "esModuleInterop": true + "module": "node16", + "esModuleInterop": true, + "rootDir": ".", + "paths": { + "#src/*": ["./src/*"] + } } } diff --git a/test/browser/vitest.config.ts b/test/browser/vitest.config.mts similarity index 75% rename from test/browser/vitest.config.ts rename to test/browser/vitest.config.mts index 2fbd47c6ab76..5cccd1160b7c 100644 --- a/test/browser/vitest.config.ts +++ b/test/browser/vitest.config.mts @@ -1,5 +1,9 @@ +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitest/config' +const dir = dirname(fileURLToPath(import.meta.url)) + const noop = () => {} export default defineConfig({ @@ -7,10 +11,13 @@ export default defineConfig({ include: ['test/**.test.{ts,js}'], browser: { enabled: true, - headless: true, name: 'chrome', + headless: false, provider: process.env.PROVIDER || 'webdriverio', }, + alias: { + '#src': resolve(dir, './src'), + }, open: false, isolate: false, outputFile: './browser.json', diff --git a/test/config/test/failures.test.ts b/test/config/test/failures.test.ts index 7931e167e738..f72b49ac87c3 100644 --- a/test/config/test/failures.test.ts +++ b/test/config/test/failures.test.ts @@ -37,3 +37,9 @@ test('inspect-brk cannot be used with threads', async () => { expect(error).toMatch('Error: You cannot use --inspect-brk without "threads: false" or "singleThread: true"') }) + +test('c8 coverage provider cannot be used with browser', async () => { + const { error } = await runVitest('run', ['--coverage.enabled', '--browser']) + + expect(error).toMatch('Error: @vitest/coverage-c8 does not work with --browser. Use @vitest/coverage-istanbul instead') +}) diff --git a/test/coverage-test/package.json b/test/coverage-test/package.json index 46d49c7a1a11..88d4c7cc0358 100644 --- a/test/coverage-test/package.json +++ b/test/coverage-test/package.json @@ -2,18 +2,21 @@ "name": "@vitest/test-coverage", "private": true, "scripts": { - "test": "pnpm test:c8 && pnpm test:istanbul && pnpm test:custom && pnpm test:types", + "test": "pnpm test:c8 && pnpm test:istanbul && pnpm test:custom && pnpm test:browser && pnpm test:types", "test:c8": "node ./testing.mjs --provider c8", "test:custom": "node ./testing.mjs --provider custom", "test:istanbul": "node ./testing.mjs --provider istanbul", + "test:browser": "node ./testing.mjs --browser --provider istanbul", "test:types": "vitest typecheck --run --reporter verbose" }, "devDependencies": { "@vitejs/plugin-vue": "latest", + "@vitest/browser": "workspace:*", "@vue/test-utils": "latest", "happy-dom": "latest", "vite": "latest", "vitest": "workspace:*", - "vue": "latest" + "vue": "latest", + "webdriverio": "latest" } } diff --git a/test/coverage-test/test/coverage.test.ts b/test/coverage-test/test/coverage.test.ts index 218547bf77ef..389a57ae83c5 100644 --- a/test/coverage-test/test/coverage.test.ts +++ b/test/coverage-test/test/coverage.test.ts @@ -1,8 +1,16 @@ import { expect, test } from 'vitest' -import { pythagoras } from '../src/index.mjs' import { implicitElse } from '../src/implicitElse' import { useImportEnv } from '../src/importEnv' +const { pythagoras } = await (() => { + if ('__vitest_browser__' in globalThis) + // TODO: remove workaround after vite 4.3.2 + // @ts-expect-error extension is not specified + return import('../src/index') + const dynamicImport = '../src/index.mjs' + return import(dynamicImport) +})() + test('Math.sqrt()', async () => { expect(pythagoras(3, 4)).toBe(5) }) diff --git a/test/coverage-test/testing.mjs b/test/coverage-test/testing.mjs index 5db6649aea51..9fb28beed6cb 100644 --- a/test/coverage-test/testing.mjs +++ b/test/coverage-test/testing.mjs @@ -4,13 +4,19 @@ import { startVitest } from 'vitest/node' const UPDATE_SNAPSHOTS = false const provider = process.argv[1 + process.argv.indexOf('--provider')] +const isBrowser = process.argv.includes('--browser') const configs = [ // Run test cases. Generates coverage report. ['test/', { include: ['test/*.test.*'], - exclude: ['coverage-report-tests/**/*'], + exclude: [ + 'coverage-report-tests/**/*', + // TODO: Include once mocking is supported in browser + isBrowser && '**/no-esbuild-transform.test.js', + ].filter(Boolean), coverage: { enabled: true }, + browser: { enabled: isBrowser, name: 'chrome', headless: true }, }], // Run tests for checking coverage report contents. @@ -23,11 +29,15 @@ const configs = [ }], ] +// Prevent the "vitest/src/node/browser/webdriver.ts" from calling process.exit +const exit = process.exit +process.exit = () => !isBrowser && exit() + for (const threads of [{ threads: true }, { threads: false }, { singleThread: true }]) { for (const isolate of [true, false]) { for (const [directory, config] of configs) { await startVitest('test', [directory], { - name: `With settings: ${JSON.stringify({ ...threads, isolate, directory })}`, + name: `With settings: ${JSON.stringify({ ...threads, isolate, directory, browser: config.browser?.enabled })}`, ...config, update: UPDATE_SNAPSHOTS, ...threads, @@ -35,7 +45,9 @@ for (const threads of [{ threads: true }, { threads: false }, { singleThread: tr }) if (process.exitCode) - process.exit() + exit() } } } + +exit() diff --git a/test/watch/package.json b/test/watch/package.json index 7a005dc398f8..4c676c03a9e3 100644 --- a/test/watch/package.json +++ b/test/watch/package.json @@ -5,9 +5,11 @@ "test": "vitest" }, "devDependencies": { + "@vitest/browser": "workspace:*", "execa": "^7.0.0", "strip-ansi": "^7.0.1", "vite": "latest", - "vitest": "workspace:*" + "vitest": "workspace:*", + "webdriverio": "latest" } } diff --git a/test/watch/test/file-watching.test.ts b/test/watch/test/file-watching.test.ts index 7e3c82c5e7f0..496699cd52cd 100644 --- a/test/watch/test/file-watching.test.ts +++ b/test/watch/test/file-watching.test.ts @@ -1,9 +1,7 @@ import { readFileSync, writeFileSync } from 'fs' -import { afterEach, expect, test } from 'vitest' +import { afterEach, describe, test } from 'vitest' -import { startWatchMode, waitFor } from './utils' - -const EDIT_COMMENT = '// Modified by file-watching.test.ts\n\n' +import { startWatchMode } from './utils' const sourceFile = 'fixtures/math.ts' const sourceFileContent = readFileSync(sourceFile, 'utf-8') @@ -14,6 +12,13 @@ const testFileContent = readFileSync(testFile, 'utf-8') const configFile = 'fixtures/vitest.config.ts' const configFileContent = readFileSync(configFile, 'utf-8') +function editFile(fileContent: string) { + return `// Modified by file-watching.test.ts +${fileContent} +console.log("New code running"); // This is used to check that edited changes are actually run, and cached files are not run instead + ` +} + afterEach(() => { writeFileSync(sourceFile, sourceFileContent, 'utf8') writeFileSync(testFile, testFileContent, 'utf8') @@ -23,34 +28,31 @@ afterEach(() => { test('editing source file triggers re-run', async () => { const vitest = await startWatchMode() - writeFileSync(sourceFile, `${EDIT_COMMENT}${sourceFileContent}`, 'utf8') + writeFileSync(sourceFile, editFile(sourceFileContent), 'utf8') - await waitFor(() => { - expect(vitest.getOutput()).toContain('RERUN math.ts') - expect(vitest.getOutput()).toContain('1 passed') - }) + await vitest.waitForOutput('New code running') + await vitest.waitForOutput('RERUN math.ts') + await vitest.waitForOutput('1 passed') }) test('editing test file triggers re-run', async () => { const vitest = await startWatchMode() - writeFileSync(testFile, `${EDIT_COMMENT}${testFileContent}`, 'utf8') + writeFileSync(testFile, editFile(testFileContent), 'utf8') - await waitFor(() => { - expect(vitest.getOutput()).toMatch('RERUN math.test.ts') - expect(vitest.getOutput()).toMatch('1 passed') - }) + await vitest.waitForOutput('New code running') + await vitest.waitForOutput('RERUN math.test.ts') + await vitest.waitForOutput('1 passed') }) test('editing config file triggers re-run', async () => { const vitest = await startWatchMode() - writeFileSync(configFile, `${EDIT_COMMENT}${configFileContent}`, 'utf8') + writeFileSync(configFile, editFile(configFileContent), 'utf8') - await waitFor(() => { - expect(vitest.getOutput()).toMatch('Restarting due to config changes') - expect(vitest.getOutput()).toMatch('2 passed') - }) + await vitest.waitForOutput('New code running') + await vitest.waitForOutput('Restarting due to config changes') + await vitest.waitForOutput('2 passed') }) test('editing config file reloads new changes', async () => { @@ -58,8 +60,20 @@ test('editing config file reloads new changes', async () => { writeFileSync(configFile, configFileContent.replace('reporters: \'verbose\'', 'reporters: \'tap\''), 'utf8') - await waitFor(() => { - expect(vitest.getOutput()).toMatch('TAP version') - expect(vitest.getOutput()).toMatch('ok 2') + await vitest.waitForOutput('TAP version') + await vitest.waitForOutput('ok 2') +}) + +describe('browser', () => { + test.runIf((process.platform !== 'win32'))('editing source file triggers re-run', async () => { + const vitest = await startWatchMode('--browser.enabled', '--browser.headless', '--browser.name=chrome') + + writeFileSync(sourceFile, editFile(sourceFileContent), 'utf8') + + await vitest.waitForOutput('New code running') + await vitest.waitForOutput('RERUN math.ts') + await vitest.waitForOutput('1 passed') + + vitest.write('q') }) }) diff --git a/test/watch/test/stdin.test.ts b/test/watch/test/stdin.test.ts index 3ec282c2c1c0..f39e8c41d234 100644 --- a/test/watch/test/stdin.test.ts +++ b/test/watch/test/stdin.test.ts @@ -1,6 +1,6 @@ -import { expect, test } from 'vitest' +import { test } from 'vitest' -import { startWatchMode, waitFor } from './utils' +import { startWatchMode } from './utils' test('quit watch mode', async () => { const vitest = await startWatchMode() @@ -15,16 +15,12 @@ test('filter by filename', async () => { vitest.write('p') - await waitFor(() => { - expect(vitest.getOutput()).toMatch('Input filename pattern') - }) + await vitest.waitForOutput('Input filename pattern') vitest.write('math\n') - await waitFor(() => { - expect(vitest.getOutput()).toMatch('Filename pattern: math') - expect(vitest.getOutput()).toMatch('1 passed') - }) + await vitest.waitForOutput('Filename pattern: math') + await vitest.waitForOutput('1 passed') }) test('filter by test name', async () => { @@ -32,14 +28,10 @@ test('filter by test name', async () => { vitest.write('t') - await waitFor(() => { - expect(vitest.getOutput()).toMatch('Input test name pattern') - }) + await vitest.waitForOutput('Input test name pattern') vitest.write('sum\n') - await waitFor(() => { - expect(vitest.getOutput()).toMatch('Test name pattern: /sum/') - expect(vitest.getOutput()).toMatch('1 passed') - }) + await vitest.waitForOutput('Test name pattern: /sum/') + await vitest.waitForOutput('1 passed') }) diff --git a/test/watch/test/stdout.test.ts b/test/watch/test/stdout.test.ts index 528d81195f95..88b78ba9a8be 100644 --- a/test/watch/test/stdout.test.ts +++ b/test/watch/test/stdout.test.ts @@ -1,7 +1,7 @@ import { readFileSync, writeFileSync } from 'fs' -import { afterEach, expect, test } from 'vitest' +import { afterEach, test } from 'vitest' -import { startWatchMode, waitFor } from './utils' +import { startWatchMode } from './utils' const testFile = 'fixtures/math.test.ts' const testFileContent = readFileSync(testFile, 'utf-8') @@ -23,10 +23,8 @@ test('test with logging', () => { writeFileSync(testFile, `${testFileContent}${testCase}`, 'utf8') - await waitFor(() => { - expect(vitest.getOutput()).toMatch('stdout | math.test.ts > test with logging') - expect(vitest.getOutput()).toMatch('First') - expect(vitest.getOutput()).toMatch('Second') - expect(vitest.getOutput()).toMatch('Third') - }) + await vitest.waitForOutput('stdout | math.test.ts > test with logging') + await vitest.waitForOutput('First') + await vitest.waitForOutput('Second') + await vitest.waitForOutput('Third') }) diff --git a/test/watch/test/utils.ts b/test/watch/test/utils.ts index 4d89702769be..e3cdb0724337 100644 --- a/test/watch/test/utils.ts +++ b/test/watch/test/utils.ts @@ -1,22 +1,41 @@ -import { afterEach, expect } from 'vitest' +import { afterEach } from 'vitest' import { execa } from 'execa' import stripAnsi from 'strip-ansi' -export async function startWatchMode() { - const subprocess = execa('vitest', ['--root', 'fixtures']) +export async function startWatchMode(...args: string[]) { + const subprocess = execa('vitest', ['--root', 'fixtures', ...args]) let setDone: (value?: unknown) => void const isDone = new Promise(resolve => (setDone = resolve)) const vitest = { output: '', + listeners: [] as (() => void)[], isDone, write(text: string) { this.resetOutput() subprocess.stdin!.write(text) }, - getOutput() { - return this.output + waitForOutput(expected: string) { + return new Promise((resolve, reject) => { + if (this.output.includes(expected)) + return resolve() + + const timeout = setTimeout(() => { + reject(new Error(`Timeout when waiting for output "${expected}".\nReceived:\n${this.output}`)) + }, 20_000) + + const listener = () => { + if (this.output.includes(expected)) { + if (timeout) + clearTimeout(timeout) + + resolve() + } + } + + this.listeners.push(listener) + }) }, resetOutput() { this.output = '' @@ -25,6 +44,7 @@ export async function startWatchMode() { subprocess.stdout!.on('data', (data) => { vitest.output += stripAnsi(data.toString()) + vitest.listeners.forEach(fn => fn()) }) subprocess.on('exit', () => setDone()) @@ -38,25 +58,8 @@ export async function startWatchMode() { }) // Wait for initial test run to complete - await waitFor(() => { - expect(vitest.getOutput()).toMatch('Waiting for file changes') - }) + await vitest.waitForOutput('Waiting for file changes') vitest.resetOutput() return vitest } - -export async function waitFor(method: () => unknown, retries = 100): Promise { - try { - method() - } - catch (error) { - if (retries === 0) { - console.error(error) - throw error - } - - await new Promise(resolve => setTimeout(resolve, 250)) - return waitFor(method, retries - 1) - } -} diff --git a/tsconfig.json b/tsconfig.json index 09da25c19f99..486a0de96473 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -51,6 +51,7 @@ "./packages/ui/cypress/**", "./examples/**/*.*", "./bench/**", - "./test/typescript/**" + "./test/typescript/**", + "./test/browser/**" ] }