From b8f34eb8ca0019461cd6b8c2f39b704e48b379e2 Mon Sep 17 00:00:00 2001 From: yoho Date: Mon, 19 Dec 2022 18:23:22 +0800 Subject: [PATCH] feat: add html reporter based on Vitest UI (#2444) * feat: vitest html report * chore: copy ui to vitest dist * feat: report copy ui dist * feat: ui report version builder * fix: copy file * chore: remove * feat: html metadata path * feat: add declare * feat: static file client * feat: mock rpc * fix: mock error * chore: update meta * chore: compress json * chore: comment * chore: update merge config * chore: lock * feat: remove all control command from ui * chore: remove * feat: report command * feat: version tag * chore: reset lock file * feat: remove hooks * chore: update * fix: runningPromise * chore: update debug mode and disable click on transform * docs: report * chore: remove the version mark * feat: report * fix: lint * chore: copy ui from @vitest/ui * chore: export report from ui * chore: update * fix: lint * docs: ui html report * feat: ensurePackageInstalled * update * feat: more info * chore: improve documentation and tests * chore: fix UI bundle size * chore: ui tests * perf: remove output report using the global variable to judge report mode * chore: update * fix: build * fix: report * fix: parse * chore: fix html reporters test * chore: don't store config in html reporter test * chore: update ui docs * feat: log * chore: fix tests * test: fix html reporter tests * docs add vitest fo UI reporter Co-authored-by: Vladimir --- docs/config/index.md | 1 + docs/guide/ui.md | 26 +++ packages/ui/client/components/FileDetails.vue | 4 +- packages/ui/client/components/Navigation.vue | 5 +- packages/ui/client/components/Suites.vue | 5 +- .../components/views/ViewModuleGraph.vue | 3 + .../{client.ts => client/index.ts} | 46 ++-- .../ui/client/composables/client/static.ts | 84 ++++++++ packages/ui/client/shim.d.ts | 6 + packages/ui/index.html | 3 +- packages/ui/node/reporter.ts | 77 +++++++ packages/ui/package.json | 7 +- packages/ui/rollup.config.js | 17 +- packages/ui/vite.config.ts | 15 ++ packages/vitest/LICENSE.md | 29 --- packages/vitest/src/api/setup.ts | 36 +--- packages/vitest/src/node/core.ts | 8 +- packages/vitest/src/node/reporters/utils.ts | 8 +- packages/vitest/src/runtime/error.ts | 2 +- packages/vitest/src/types/config.ts | 2 +- packages/vitest/src/types/tasks.ts | 5 +- packages/vitest/src/utils/config-helpers.ts | 2 +- packages/vitest/src/utils/graph.ts | 38 ++++ packages/vitest/src/utils/index.ts | 1 + pnpm-lock.yaml | 75 +++---- test/reporters/fixtures/.gitignore | 1 + test/reporters/fixtures/json-fail.test.ts | 2 + test/reporters/package.json | 1 + .../tests/__snapshots__/html.test.ts.snap | 202 ++++++++++++++++++ .../tests/__snapshots__/json.test.ts.snap | 2 +- test/reporters/tests/html.test.ts | 79 +++++++ 31 files changed, 641 insertions(+), 151 deletions(-) rename packages/ui/client/composables/{client.ts => client/index.ts} (74%) create mode 100644 packages/ui/client/composables/client/static.ts create mode 100644 packages/ui/node/reporter.ts create mode 100644 packages/vitest/src/utils/graph.ts create mode 100644 test/reporters/fixtures/.gitignore create mode 100644 test/reporters/tests/__snapshots__/html.test.ts.snap create mode 100644 test/reporters/tests/html.test.ts diff --git a/docs/config/index.md b/docs/config/index.md index 52ae5620a036..d6596011d1c3 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -341,6 +341,7 @@ Custom reporters for output. Reporters can be [a Reporter instance](https://gith - `'dot'` - show each task as a single dot - `'junit'` - JUnit XML reporter (you can configure `testsuites` tag name with `VITEST_JUNIT_SUITE_NAME` environmental variable) - `'json'` - give a simple JSON summary + - `'html'` - outputs HTML report based on [`@vitest/ui`](/guide/ui) - path of a custom reporter (e.g. `'./path/to/reporter.ts'`, `'@scope/reporter'`) ### outputTruncateLength diff --git a/docs/guide/ui.md b/docs/guide/ui.md index 79be95408933..7b2646868fff 100644 --- a/docs/guide/ui.md +++ b/docs/guide/ui.md @@ -21,3 +21,29 @@ Then you can visit the Vitest UI at Vitest UI + +Since Vitest 0.26.0, UI can also be used as a reporter. Use `'html'` reporter in your Vitest configuration to generate HTML output and preview results of your tests: + +```ts +// vitest.config.ts + +export default { + test: { + reporters: ['html'] + } +} +``` + +::: warning +If you still want to see how your tests are running in real time in the terminal, don't forget to add `default` reporter to `reporters` option: `['default', 'html']`. +::: + +::: tip +To preview your HTML report, you can use [vite preview](https://vitejs.dev/guide/cli.html#vite-preview) command: + +```sh +npx vite preview --base __vitest__ --outDir ./html +``` + +You can configure output with [`outputFile`](/config/#outputfile) config option. You need to specify `.html` path there. For example, `./html/index.html` is the default value. +::: diff --git a/packages/ui/client/components/FileDetails.vue b/packages/ui/client/components/FileDetails.vue index ffb9d9bf0071..30d1a644e11a 100644 --- a/packages/ui/client/components/FileDetails.vue +++ b/packages/ui/client/components/FileDetails.vue @@ -1,5 +1,5 @@ +
diff --git a/packages/ui/node/reporter.ts b/packages/ui/node/reporter.ts new file mode 100644 index 000000000000..79a40a5a5688 --- /dev/null +++ b/packages/ui/node/reporter.ts @@ -0,0 +1,77 @@ +import { existsSync, promises as fs } from 'node:fs' +import { fileURLToPath } from 'node:url' +import { basename, dirname, relative, resolve } from 'pathe' +import c from 'picocolors' +import fg from 'fast-glob' +import { stringify } from 'flatted' +// eslint-disable-next-line no-restricted-imports +import type { File, ModuleGraphData, Reporter, ResolvedConfig, Vitest } from 'vitest' +import { getModuleGraph } from '../../vitest/src/utils/graph' +import { getOutputFile } from '../../vitest/src/utils/config-helpers' + +interface HTMLReportData { + paths: string[] + files: File[] + config: ResolvedConfig + moduleGraph: Record +} + +const distDir = resolve(fileURLToPath(import.meta.url), '../../dist') + +export default class HTMLReporter implements Reporter { + start = 0 + ctx!: Vitest + reportUIPath!: string + + async onInit(ctx: Vitest) { + this.ctx = ctx + this.start = Date.now() + } + + async onFinished() { + const result: HTMLReportData = { + paths: await this.ctx.state.getPaths(), + files: this.ctx.state.getFiles(), + config: this.ctx.config, + moduleGraph: {}, + } + await Promise.all( + result.files.map(async (file) => { + result.moduleGraph[file.filepath] = await getModuleGraph(this.ctx, file.filepath) + }), + ) + await this.writeReport(stringify(result)) + } + + async writeReport(report: string) { + const htmlFile = getOutputFile(this.ctx.config, 'html') || 'html/index.html' + const htmlFileName = basename(htmlFile) + const htmlDir = resolve(this.ctx.config.root, dirname(htmlFile)) + + const metaFile = resolve(htmlDir, 'html.meta.json') + + if (!existsSync(htmlDir)) + await fs.mkdir(resolve(htmlDir, 'assets'), { recursive: true }) + + await fs.writeFile(metaFile, report, 'utf-8') + const ui = resolve(distDir, 'client') + // copy ui + const files = fg.sync('**/*', { cwd: ui }) + await Promise.all(files.map(async (f) => { + if (f === 'index.html') { + const html = await fs.readFile(resolve(ui, f), 'utf-8') + const filePath = relative(htmlDir, metaFile) + await fs.writeFile( + resolve(htmlDir, htmlFileName), + html.replace('', ``), + ) + } + else { + await fs.copyFile(resolve(ui, f), resolve(htmlDir, f)) + } + })) + + this.ctx.logger.log(`${c.bold(c.inverse(c.magenta(' HTML ')))} ${c.magenta('Report is generated')}`) + this.ctx.logger.log(`${c.dim(' You can run ')}${c.bold(`npx vite preview --base __vitest__ --outDir ${relative(this.ctx.config.root, htmlDir)}`)}${c.dim(' to see the test results.')}`) + } +} diff --git a/packages/ui/package.json b/packages/ui/package.json index 3c7bf8adefc4..0600733a6f7a 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -15,6 +15,10 @@ "types": "./dist/index.d.ts", "import": "./dist/index.js" }, + "./reporter": { + "types": "./dist/reporter.d.ts", + "import": "./dist/reporter.js" + }, "./*": "./*" }, "main": "./dist/index.js", @@ -35,6 +39,8 @@ "prepublishOnly": "pnpm build" }, "dependencies": { + "fast-glob": "^3.2.12", + "flatted": "^3.2.7", "sirv": "^2.0.2" }, "devDependencies": { @@ -56,7 +62,6 @@ "cypress": "^11.2.0", "d3-graph-controller": "^2.4.0", "diff": "^5.1.0", - "flatted": "^3.2.7", "floating-vue": "^2.0.0-y.0", "picocolors": "^1.0.0", "rollup": "^2.79.1", diff --git a/packages/ui/rollup.config.js b/packages/ui/rollup.config.js index 8278d5a8e371..d6c01b08196d 100644 --- a/packages/ui/rollup.config.js +++ b/packages/ui/rollup.config.js @@ -7,20 +7,21 @@ import json from '@rollup/plugin-json' import alias from '@rollup/plugin-alias' import pkg from './package.json' -const entry = [ - './node/index.ts', -] - const external = [ ...builtinModules, ...Object.keys(pkg.dependencies), ...Object.keys(pkg.peerDependencies || {}), 'worker_threads', + 'vitest/node', + 'vitest', ] export default () => [ + 'index', + 'reporter', +].map(entry => [ { - input: entry, + input: `./node/${entry}.ts`, output: { dir: 'dist', format: 'esm', @@ -44,9 +45,9 @@ export default () => [ onwarn, }, { - input: entry, + input: `./node/${entry}.ts`, output: { - file: 'dist/index.d.ts', + file: `dist/${entry}.d.ts`, format: 'esm', }, external, @@ -54,7 +55,7 @@ export default () => [ dts(), ], }, -] +]).flat() function onwarn(message) { if (['EMPTY_BUNDLE', 'CIRCULAR_DEPENDENCY'].includes(message.code)) diff --git a/packages/ui/vite.config.ts b/packages/ui/vite.config.ts index 74bb0ac9916c..b4055d541cff 100644 --- a/packages/ui/vite.config.ts +++ b/packages/ui/vite.config.ts @@ -8,6 +8,11 @@ import Unocss from 'unocss/vite' import Pages from 'vite-plugin-pages' import { presetAttributify, presetIcons, presetUno } from 'unocss' +// for debug: +// open a static file serve to share the report json +// and ui using the link to load the report json data +const debugLink = 'http://127.0.0.1:4173' + export const config: UserConfig = { root: __dirname, base: '/__vitest__/', @@ -18,6 +23,9 @@ export const config: UserConfig = { '@vitest/ws-client': `${resolve(__dirname, '../ws-client/src/index.ts')}`, }, }, + define: { + __REPORT__: false, + }, plugins: [ Vue(), Unocss({ @@ -53,6 +61,13 @@ export const config: UserConfig = { '@vueuse/core', ], }), + { + name: 'debug-html-report', + apply: 'serve', + transformIndexHtml(html) { + return html.replace('', ``) + }, + }, ], build: { outDir: './dist/client', diff --git a/packages/vitest/LICENSE.md b/packages/vitest/LICENSE.md index 932d15580a2e..4ceca21a7248 100644 --- a/packages/vitest/LICENSE.md +++ b/packages/vitest/LICENSE.md @@ -2037,35 +2037,6 @@ Repository: chalk/slice-ansi --------------------------------------- -## sourcemap-codec -License: MIT -By: Rich Harris -Repository: https://github.com/Rich-Harris/sourcemap-codec - -> The MIT License -> -> Copyright (c) 2015 Rich Harris -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in -> all copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -> THE SOFTWARE. - ---------------------------------------- - ## string-width License: MIT By: Sindre Sorhus diff --git a/packages/vitest/src/api/setup.ts b/packages/vitest/src/api/setup.ts index 905bbf3c244c..772d8da06b00 100644 --- a/packages/vitest/src/api/setup.ts +++ b/packages/vitest/src/api/setup.ts @@ -4,10 +4,10 @@ import { createBirpc } from 'birpc' import { parse, stringify } from 'flatted' import type { WebSocket } from 'ws' import { WebSocketServer } from 'ws' -import type { ModuleNode } from 'vite' import { API_PATH } from '../constants' import type { Vitest } from '../node' import type { File, ModuleGraphData, Reporter, TaskResultPack, UserConsoleLog } from '../types' +import { getModuleGraph } from '../utils' import type { TransformResultWithSource, WebSocketEvents, WebSocketHandlers } from './types' export function setup(ctx: Vitest) { @@ -75,39 +75,7 @@ export function setup(ctx: Vitest) { } }, async getModuleGraph(id: string): Promise { - const graph: Record = {} - const externalized = new Set() - const inlined = new Set() - - function clearId(id?: string | null) { - return id?.replace(/\?v=\w+$/, '') || '' - } - async function get(mod?: ModuleNode, seen = new Map()) { - if (!mod || !mod.id) - return - if (seen.has(mod)) - return seen.get(mod) - let id = clearId(mod.id) - seen.set(mod, id) - const rewrote = await ctx.vitenode.shouldExternalize(id) - if (rewrote) { - id = rewrote - externalized.add(id) - seen.set(mod, id) - } - else { - inlined.add(id) - } - const mods = Array.from(mod.importedModules).filter(i => i.id && !i.id.includes('/vitest/dist/')) - graph[id] = (await Promise.all(mods.map(m => get(m, seen)))).filter(Boolean) as string[] - return id - } - await get(ctx.server.moduleGraph.getModuleById(id)) - return { - graph, - externalized: Array.from(externalized), - inlined: Array.from(inlined), - } + return getModuleGraph(ctx, id) }, updateSnapshot(file?: File) { if (!file) diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index d96a4898f317..d2e6c88ff96e 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -339,14 +339,14 @@ export class Vitest { if (hasFailed(files)) process.exitCode = 1 - if (!this.config.browser) - await this.report('onFinished', files, this.state.getUnhandledErrors()) this.cache.results.updateResults(files) await this.cache.results.writeToCache() })() - .finally(() => { - this.runningPromise = undefined + .finally(async () => { this.state.finishCollectingPaths() + if (!this.config.browser) + await this.report('onFinished', this.state.getFiles(), this.state.getUnhandledErrors()) + this.runningPromise = undefined }) return await this.runningPromise diff --git a/packages/vitest/src/node/reporters/utils.ts b/packages/vitest/src/node/reporters/utils.ts index aad3aa695c88..0931c481e947 100644 --- a/packages/vitest/src/node/reporters/utils.ts +++ b/packages/vitest/src/node/reporters/utils.ts @@ -1,5 +1,6 @@ import type { ViteNodeRunner } from 'vite-node/client' import type { Reporter } from '../../types' +import { ensurePackageInstalled } from '../../utils' import { BenchmarkReportsMap, ReportersMap } from './index' import type { BenchmarkBuiltinReporters, BuiltinReporters } from './index' @@ -21,7 +22,12 @@ async function loadCustomReporterModule(path: string, runner function createReporters(reporterReferences: Array, runner: ViteNodeRunner) { const promisedReporters = reporterReferences.map(async (referenceOrInstance) => { if (typeof referenceOrInstance === 'string') { - if (referenceOrInstance in ReportersMap) { + if (referenceOrInstance === 'html') { + await ensurePackageInstalled('@vitest/ui', runner.root) + const CustomReporter = await loadCustomReporterModule('@vitest/ui/reporter', runner) + return new CustomReporter() + } + else if (referenceOrInstance in ReportersMap) { const BuiltinReporter = ReportersMap[referenceOrInstance as BuiltinReporters] return new BuiltinReporter() } diff --git a/packages/vitest/src/runtime/error.ts b/packages/vitest/src/runtime/error.ts index 8ea6a4c7f802..da6719fe65f6 100644 --- a/packages/vitest/src/runtime/error.ts +++ b/packages/vitest/src/runtime/error.ts @@ -23,7 +23,7 @@ export function serializeError(val: any, seen = new WeakMap()): any { if (!val || typeof val === 'string') return val if (typeof val === 'function') - return `Function<${val.name}>` + return `Function<${val.name || 'anonymous'}>` if (typeof val === 'symbol') return val.toString() if (typeof val !== 'object') diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index 1b50c805262a..d4f679beaa3d 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -158,7 +158,7 @@ export interface InlineConfig { * Custom reporter for output. Can contain one or more built-in report names, reporter instances, * and/or paths to custom reporters */ - reporters?: Arrayable> + reporters?: Arrayable> /** * diff output length diff --git a/packages/vitest/src/types/tasks.ts b/packages/vitest/src/types/tasks.ts index 21480be3b34c..315e9e22550f 100644 --- a/packages/vitest/src/types/tasks.ts +++ b/packages/vitest/src/types/tasks.ts @@ -1,7 +1,6 @@ import type { ChainableFunction } from '../runtime/chain' -import type { BenchFactory } from './benchmark' -import type { Awaitable, ErrorWithDiff } from './general' -import type { Benchmark, BenchmarkAPI, BenchmarkResult, UserConsoleLog } from '.' +import type { BenchFactory, Benchmark, BenchmarkAPI, BenchmarkResult } from './benchmark' +import type { Awaitable, ErrorWithDiff, UserConsoleLog } from './general' export type RunMode = 'run' | 'skip' | 'only' | 'todo' export type TaskState = RunMode | 'pass' | 'fail' diff --git a/packages/vitest/src/utils/config-helpers.ts b/packages/vitest/src/utils/config-helpers.ts index 0a85c7763028..db075d01d446 100644 --- a/packages/vitest/src/utils/config-helpers.ts +++ b/packages/vitest/src/utils/config-helpers.ts @@ -4,7 +4,7 @@ interface PotentialConfig { outputFile?: string | Partial> } -export const getOutputFile = (config: PotentialConfig | undefined, reporter: BuiltinReporters | BenchmarkBuiltinReporters) => { +export const getOutputFile = (config: PotentialConfig | undefined, reporter: BuiltinReporters | BenchmarkBuiltinReporters | 'html') => { if (!config?.outputFile) return diff --git a/packages/vitest/src/utils/graph.ts b/packages/vitest/src/utils/graph.ts new file mode 100644 index 000000000000..aa8f795c6a8f --- /dev/null +++ b/packages/vitest/src/utils/graph.ts @@ -0,0 +1,38 @@ +import type { ModuleNode } from 'vite' +import type { ModuleGraphData, Vitest } from '../types' + +export async function getModuleGraph(ctx: Vitest, id: string): Promise { + const graph: Record = {} + const externalized = new Set() + const inlined = new Set() + + function clearId(id?: string | null) { + return id?.replace(/\?v=\w+$/, '') || '' + } + async function get(mod?: ModuleNode, seen = new Map()) { + if (!mod || !mod.id) + return + if (seen.has(mod)) + return seen.get(mod) + let id = clearId(mod.id) + seen.set(mod, id) + const rewrote = await ctx.vitenode.shouldExternalize(id) + if (rewrote) { + id = rewrote + externalized.add(id) + seen.set(mod, id) + } + else { + inlined.add(id) + } + const mods = Array.from(mod.importedModules).filter(i => i.id && !i.id.includes('/vitest/dist/')) + graph[id] = (await Promise.all(mods.map(m => get(m, seen)))).filter(Boolean) as string[] + return id + } + await get(ctx.server.moduleGraph.getModuleById(id)) + return { + graph, + externalized: Array.from(externalized), + inlined: Array.from(inlined), + } +} diff --git a/packages/vitest/src/utils/index.ts b/packages/vitest/src/utils/index.ts index e4f8bd3c61c8..21e03df1fe63 100644 --- a/packages/vitest/src/utils/index.ts +++ b/packages/vitest/src/utils/index.ts @@ -10,6 +10,7 @@ import { getWorkerState } from '../utils' import { getNames } from './tasks' import { isBrowser, isNode } from './env' +export * from './graph' export * from './tasks' export * from './base' export * from './global' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5a84a59f8bc1..8d5ad5973a01 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -236,7 +236,7 @@ importers: react-dom: 18.0.0_react@18.0.0 devDependencies: '@testing-library/react': 13.3.0_zpnidt7m3osuk7shl3s4oenomq - '@types/node': 18.11.15 + '@types/node': 18.11.16 '@types/react': 18.0.26 '@vitejs/plugin-react': 3.0.0 jsdom: 20.0.3 @@ -704,6 +704,7 @@ importers: cypress: ^11.2.0 d3-graph-controller: ^2.4.0 diff: ^5.1.0 + fast-glob: ^3.2.12 flatted: ^3.2.7 floating-vue: ^2.0.0-y.0 picocolors: ^1.0.0 @@ -718,6 +719,8 @@ importers: vue: ^3.2.45 vue-router: ^4.1.6 dependencies: + fast-glob: 3.2.12 + flatted: 3.2.7 sirv: 2.0.2 devDependencies: '@faker-js/faker': 7.6.0 @@ -738,7 +741,6 @@ importers: cypress: 11.2.0 d3-graph-controller: 2.4.0 diff: 5.1.0 - flatted: 3.2.7 floating-vue: 2.0.0-y.0_vue@3.2.45 picocolors: 1.0.0 rollup: 2.79.1 @@ -1086,11 +1088,13 @@ importers: test/reporters: specifiers: execa: ^6.1.0 + flatted: ^3.2.7 pkg-reporter: ./reportPkg/ vitest: workspace:* vitest-sonar-reporter: 0.3.3 devDependencies: execa: 6.1.0 + flatted: 3.2.7 pkg-reporter: link:reportPkg vitest: link:../../packages/vitest vitest-sonar-reporter: 0.3.3_vitest@packages+vitest @@ -4841,7 +4845,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.11.15 + '@types/node': 18.11.16 '@types/yargs': 15.0.14 chalk: 4.1.2 dev: true @@ -4853,7 +4857,7 @@ packages: '@jest/schemas': 29.0.0 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.11.15 + '@types/node': 18.11.16 '@types/yargs': 17.0.12 chalk: 4.1.2 dev: true @@ -5322,7 +5326,6 @@ packages: dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 - dev: true /@nodelib/fs.stat/1.1.3: resolution: {integrity: sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==} @@ -5332,7 +5335,6 @@ packages: /@nodelib/fs.stat/2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} - dev: true /@nodelib/fs.walk/1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} @@ -5340,7 +5342,6 @@ packages: dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.13.0 - dev: true /@npmcli/fs/1.1.1: resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==} @@ -5366,7 +5367,7 @@ packages: engines: {node: '>=14'} hasBin: true dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 playwright-core: 1.28.0 dev: true @@ -7192,7 +7193,7 @@ packages: /@types/cheerio/0.22.31: resolution: {integrity: sha512-Kt7Cdjjdi2XWSfrZ53v4Of0wG3ZcmaegFXjMmz9tfNrZSkzzo36G0AL1YqSdcIA78Etjt6E609pt5h1xnQkPUw==} dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 dev: true /@types/codemirror/5.60.5: @@ -7258,27 +7259,27 @@ packages: /@types/fs-extra/9.0.13: resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 dev: true /@types/glob/7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.1 - '@types/node': 18.11.15 + '@types/node': 18.11.16 dev: true /@types/glob/8.0.0: resolution: {integrity: sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==} dependencies: '@types/minimatch': 5.1.1 - '@types/node': 18.11.15 + '@types/node': 18.11.16 dev: true /@types/graceful-fs/4.1.5: resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 dev: true /@types/hast/2.3.4: @@ -7339,7 +7340,7 @@ packages: /@types/jsdom/20.0.1: resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 '@types/tough-cookie': 4.0.2 parse5: 7.1.1 dev: true @@ -7383,7 +7384,7 @@ packages: /@types/node-fetch/2.6.2: resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 form-data: 3.0.1 dev: true @@ -7399,8 +7400,8 @@ packages: resolution: {integrity: sha512-0KXV57tENYmmJMl+FekeW9V3O/rlcqGQQJ/hNh9r8pKIj304pskWuEd8fCyNT86g/TpO0gcOTiLzsHLEURFMIQ==} dev: true - /@types/node/18.11.15: - resolution: {integrity: sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw==} + /@types/node/18.11.16: + resolution: {integrity: sha512-6T7P5bDkRhqRxrQtwj7vru+bWTpelgtcETAZEUSdq0YISKz8WKdoBukQLYQQ6DFHvU9JRsbFq0JH5C51X2ZdnA==} dev: true /@types/node/18.7.13: @@ -7433,7 +7434,7 @@ packages: /@types/prompts/2.4.2: resolution: {integrity: sha512-TwNx7qsjvRIUv/BCx583tqF5IINEVjCNqg9ofKHRlSoUHE62WBHrem4B1HGXcIrG511v29d1kJ9a/t2Esz7MIg==} dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 kleur: 3.0.3 dev: true @@ -7506,7 +7507,7 @@ packages: /@types/resolve/1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 dev: true /@types/resolve/1.20.2: @@ -7523,7 +7524,7 @@ packages: /@types/set-cookie-parser/2.4.2: resolution: {integrity: sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w==} dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 dev: true /@types/sinonjs__fake-timers/8.1.1: @@ -7610,7 +7611,7 @@ packages: /@types/webpack-sources/3.2.0: resolution: {integrity: sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==} dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 '@types/source-list-map': 0.1.2 source-map: 0.7.4 dev: true @@ -7618,7 +7619,7 @@ packages: /@types/webpack/4.41.32: resolution: {integrity: sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==} dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 '@types/tapable': 1.0.8 '@types/uglify-js': 3.17.0 '@types/webpack-sources': 3.2.0 @@ -7629,7 +7630,7 @@ packages: /@types/ws/8.5.3: resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==} dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 dev: true /@types/yargs-parser/21.0.0: @@ -7652,7 +7653,7 @@ packages: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 dev: true optional: true @@ -9758,7 +9759,6 @@ packages: engines: {node: '>=8'} dependencies: fill-range: 7.0.1 - dev: true /broadcast-channel/3.7.0: resolution: {integrity: sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==} @@ -12636,7 +12636,6 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.5 - dev: true /fast-json-parse/1.0.3: resolution: {integrity: sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==} @@ -12699,7 +12698,6 @@ packages: resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} dependencies: reusify: 1.0.4 - dev: true /fb-watchman/2.0.1: resolution: {integrity: sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==} @@ -12780,7 +12778,6 @@ packages: engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 - dev: true /finalhandler/1.2.0: resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} @@ -13252,7 +13249,6 @@ packages: engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 - dev: true /glob-parent/6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} @@ -14192,7 +14188,6 @@ packages: /is-extglob/2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - dev: true /is-finite/1.1.0: resolution: {integrity: sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==} @@ -14225,7 +14220,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 - dev: true /is-hexadecimal/1.0.4: resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} @@ -14278,7 +14272,6 @@ packages: /is-number/7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - dev: true /is-obj/1.0.1: resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} @@ -14584,7 +14577,7 @@ packages: dependencies: '@jest/types': 26.6.2 '@types/graceful-fs': 4.1.5 - '@types/node': 18.11.15 + '@types/node': 18.11.16 anymatch: 3.1.2 fb-watchman: 2.0.1 graceful-fs: 4.2.10 @@ -14652,7 +14645,7 @@ packages: resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==} engines: {node: '>= 10.14.2'} dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 graceful-fs: 4.2.10 dev: true @@ -14661,7 +14654,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 18.11.15 + '@types/node': 18.11.16 chalk: 4.1.2 graceful-fs: 4.2.10 is-ci: 2.0.0 @@ -14673,7 +14666,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.0.1 - '@types/node': 18.11.15 + '@types/node': 18.11.16 chalk: 4.1.2 ci-info: 3.7.0 graceful-fs: 4.2.10 @@ -14684,7 +14677,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 merge-stream: 2.0.0 supports-color: 7.2.0 dev: true @@ -14693,7 +14686,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.11.15 + '@types/node': 18.11.16 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true @@ -15536,7 +15529,6 @@ packages: /merge2/1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - dev: true /methods/1.1.2: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} @@ -15583,7 +15575,6 @@ packages: dependencies: braces: 3.0.2 picomatch: 2.3.1 - dev: true /microseconds/0.2.0: resolution: {integrity: sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==} @@ -17364,7 +17355,6 @@ packages: /queue-microtask/1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true /quick-format-unescaped/4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} @@ -18099,7 +18089,6 @@ packages: /reusify/1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true /rfdc/1.3.0: resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} @@ -18245,7 +18234,6 @@ packages: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 - dev: true /run-queue/1.0.3: resolution: {integrity: sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==} @@ -19593,7 +19581,6 @@ packages: engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 - dev: true /to-regex/3.0.2: resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==} diff --git a/test/reporters/fixtures/.gitignore b/test/reporters/fixtures/.gitignore new file mode 100644 index 000000000000..1936cc1d441e --- /dev/null +++ b/test/reporters/fixtures/.gitignore @@ -0,0 +1 @@ +html diff --git a/test/reporters/fixtures/json-fail.test.ts b/test/reporters/fixtures/json-fail.test.ts index 093f0e1bcfc6..82e4ca6e3633 100644 --- a/test/reporters/fixtures/json-fail.test.ts +++ b/test/reporters/fixtures/json-fail.test.ts @@ -3,5 +3,7 @@ import { expect, test } from 'vitest' // I am comment1 // I am comment2 test('should fail', () => { + // eslint-disable-next-line no-console + console.log('json-fail>should fail') expect(2).toEqual(1) }) diff --git a/test/reporters/package.json b/test/reporters/package.json index 3044c43c1f1e..497f184ffe54 100644 --- a/test/reporters/package.json +++ b/test/reporters/package.json @@ -6,6 +6,7 @@ }, "devDependencies": { "execa": "^6.1.0", + "flatted": "^3.2.7", "pkg-reporter": "./reportPkg/", "vitest": "workspace:*", "vitest-sonar-reporter": "0.3.3" diff --git a/test/reporters/tests/__snapshots__/html.test.ts.snap b/test/reporters/tests/__snapshots__/html.test.ts.snap new file mode 100644 index 000000000000..b243bdff112b --- /dev/null +++ b/test/reporters/tests/__snapshots__/html.test.ts.snap @@ -0,0 +1,202 @@ +// Vitest Snapshot v1 + +exports[`html reporter > resolves to "failing" status for test file "json-fail" > tests are failing 1`] = ` +{ + "config": {}, + "files": [ + { + "collectDuration": 0, + "filepath": "/test/reporters/fixtures/json-fail.test.ts", + "id": 0, + "mode": "run", + "name": "json-fail.test.ts", + "result": { + "duration": 0, + "hooks": { + "afterAll": "pass", + "beforeAll": "pass", + }, + "startTime": 0, + "state": "fail", + }, + "setupDuration": 0, + "tasks": [ + { + "file": [Circular], + "id": 0, + "logs": [ + { + "content": "json-fail>should fail +", + "size": 1, + "taskId": 0, + "time": 0, + "type": "stdout", + }, + ], + "mode": "run", + "name": "should fail", + "result": { + "duration": 0, + "error": { + "actual": "2", + "constructor": "Function", + "expected": "1", + "message": "expected 2 to deeply equal 1", + "name": "AssertionError", + "nameStr": "AssertionError", + "operator": "strictEqual", + "showDiff": true, + "stack": "AssertionError: expected 2 to deeply equal 1", + "stackStr": "AssertionError: expected 2 to deeply equal 1", + "toJSON": "Function", + "toString": "Function", + }, + "hooks": { + "afterEach": "pass", + "beforeEach": "pass", + }, + "retryCount": 0, + "startTime": 0, + "state": "fail", + }, + "suite": { + "file": [Circular], + "id": "", + "mode": "run", + "name": "", + "tasks": [ + [Circular], + ], + "type": "suite", + }, + "type": "test", + }, + ], + "type": "suite", + }, + ], + "moduleGraph": { + "/test/reporters/fixtures/json-fail.test.ts": { + "externalized": [], + "graph": { + "/test/reporters/fixtures/json-fail.test.ts": [], + }, + "inlined": [ + "/test/reporters/fixtures/json-fail.test.ts", + ], + }, + }, + "paths": [], +} +`; + +exports[`html reporter > resolves to "passing" status for test file "all-passing-or-skipped" > tests are passing 1`] = ` +{ + "config": {}, + "files": [ + { + "collectDuration": 0, + "filepath": "/test/reporters/fixtures/all-passing-or-skipped.test.ts", + "id": 0, + "mode": "run", + "name": "all-passing-or-skipped.test.ts", + "result": { + "duration": 0, + "hooks": { + "afterAll": "pass", + "beforeAll": "pass", + }, + "startTime": 0, + "state": "pass", + }, + "setupDuration": 0, + "tasks": [ + { + "file": [Circular], + "id": 0, + "mode": "run", + "name": "2 + 3 = 5", + "result": { + "duration": 0, + "hooks": { + "afterEach": "pass", + "beforeEach": "pass", + }, + "retryCount": 0, + "startTime": 0, + "state": "pass", + }, + "suite": { + "file": [Circular], + "id": "", + "mode": "run", + "name": "", + "tasks": [ + [Circular], + { + "file": [Circular], + "id": "1111755131_1", + "mode": "skip", + "name": "3 + 3 = 6", + "suite": [Circular], + "type": "test", + }, + ], + "type": "suite", + }, + "type": "test", + }, + { + "file": [Circular], + "id": "1111755131_1", + "mode": "skip", + "name": "3 + 3 = 6", + "suite": { + "file": [Circular], + "id": "", + "mode": "run", + "name": "", + "tasks": [ + { + "file": [Circular], + "id": 0, + "mode": "run", + "name": "2 + 3 = 5", + "result": { + "duration": 0, + "hooks": { + "afterEach": "pass", + "beforeEach": "pass", + }, + "retryCount": 0, + "startTime": 0, + "state": "pass", + }, + "suite": [Circular], + "type": "test", + }, + [Circular], + ], + "type": "suite", + }, + "type": "test", + }, + ], + "type": "suite", + }, + ], + "moduleGraph": { + "/test/reporters/fixtures/all-passing-or-skipped.test.ts": { + "externalized": [], + "graph": { + "/test/reporters/fixtures/all-passing-or-skipped.test.ts": [], + }, + "inlined": [ + "/test/reporters/fixtures/all-passing-or-skipped.test.ts", + ], + }, + }, + "paths": [], +} +`; diff --git a/test/reporters/tests/__snapshots__/json.test.ts.snap b/test/reporters/tests/__snapshots__/json.test.ts.snap index 3f930ad33668..565b068bc9b6 100644 --- a/test/reporters/tests/__snapshots__/json.test.ts.snap +++ b/test/reporters/tests/__snapshots__/json.test.ts.snap @@ -11,7 +11,7 @@ exports[`json reporter > generates correct report 1`] = ` "fullName": " should fail", "location": { "column": 13, - "line": 6, + "line": 8, }, "status": "failed", "title": "should fail", diff --git a/test/reporters/tests/html.test.ts b/test/reporters/tests/html.test.ts new file mode 100644 index 000000000000..dbc98ccaa115 --- /dev/null +++ b/test/reporters/tests/html.test.ts @@ -0,0 +1,79 @@ +import fs from 'fs' +import { resolve } from 'pathe' +import { execa } from 'execa' +import { describe, expect, it } from 'vitest' +import { parse } from 'flatted' + +const skip = (process.platform === 'win32' || process.platform === 'darwin') && process.env.CI + +describe.skipIf(skip)('html reporter', async () => { + const vitestRoot = resolve(__dirname, '../../..') + const root = resolve(__dirname, '../fixtures') + + it('resolves to "passing" status for test file "all-passing-or-skipped"', async () => { + const [expected, testFile, basePath] = ['passing', 'all-passing-or-skipped', 'html/all-passing-or-skipped'] + await execa('npx', ['vitest', 'run', testFile, '--reporter=html', `--outputFile=${basePath}/index.html`], { + cwd: root, + env: { + ...process.env, + CI: 'true', + NO_COLOR: 'true', + }, + stdio: 'inherit', + }).catch(e => e) + const metaJson = fs.readFileSync(resolve(root, `${basePath}/html.meta.json`), { encoding: 'utf-8' }) + const indexHtml = fs.readFileSync(resolve(root, `${basePath}/index.html`), { encoding: 'utf-8' }) + const resultJson = parse(metaJson.replace(new RegExp(vitestRoot, 'g'), '')) + resultJson.config = {} // doesn't matter for a test + const file = resultJson.files[0] + file.id = 0 + file.collectDuration = 0 + file.setupDuration = 0 + file.result.duration = 0 + file.result.startTime = 0 + const task = file.tasks[0] + task.id = 0 + task.result.duration = 0 + task.result.startTime = 0 + expect(task.result.error).not.toBeDefined() + expect(task.result.logs).not.toBeDefined() + expect(resultJson).toMatchSnapshot(`tests are ${expected}`) + expect(indexHtml).toMatch('window.METADATA_PATH="html.meta.json"') + }, 120000) + + it('resolves to "failing" status for test file "json-fail"', async () => { + const [expected, testFile, basePath] = ['failing', 'json-fail', 'html/fail'] + await execa('npx', ['vitest', 'run', testFile, '--reporter=html', `--outputFile=${basePath}/index.html`], { + cwd: root, + env: { + ...process.env, + CI: 'true', + NO_COLOR: 'true', + }, + stdio: 'inherit', + }).catch(e => e) + const metaJson = fs.readFileSync(resolve(root, `${basePath}/html.meta.json`), { encoding: 'utf-8' }) + const indexHtml = fs.readFileSync(resolve(root, `${basePath}/index.html`), { encoding: 'utf-8' }) + const resultJson = parse(metaJson.replace(new RegExp(vitestRoot, 'g'), '')) + resultJson.config = {} // doesn't matter for a test + const file = resultJson.files[0] + file.id = 0 + file.collectDuration = 0 + file.setupDuration = 0 + file.result.duration = 0 + file.result.startTime = 0 + const task = file.tasks[0] + task.id = 0 + task.result.duration = 0 + task.result.startTime = 0 + expect(task.result.error).toBeDefined() + task.result.error.stack = task.result.error.stack.split('\n')[0] + task.result.error.stackStr = task.result.error.stackStr.split('\n')[0] + expect(task.logs).toBeDefined() + expect(task.logs).toHaveLength(1) + task.logs[0].taskId = 0 + task.logs[0].time = 0 + expect(resultJson).toMatchSnapshot(`tests are ${expected}`) + expect(indexHtml).toMatch('window.METADATA_PATH="html.meta.json"') + }, 120000) +})