diff --git a/docs/guide/learn/writing-tests.md b/docs/guide/learn/writing-tests.md index a047001bd2f3..35be6bb31633 100644 --- a/docs/guide/learn/writing-tests.md +++ b/docs/guide/learn/writing-tests.md @@ -176,7 +176,7 @@ test.for([ }) ``` -The placeholders `%i`, `%s`, and `%f` in the test name are replaced with the corresponding values from each row, so the output shows `add(1, 1) -> 2`, `add(1, 2) -> 3`, and so on. +In the example above, the %i placeholders are replaced with the integer values from each data row. Vitest also supports other placeholder types, such as %s for strings and %f for floating-point numbers. As a result, the test runner generates test names such as add(1, 1) -> 2, add(1, 2) -> 3, and add(2, 1) -> 3. If your cases have more than two or three values, passing objects is more readable. Use `$property` in the name to interpolate fields: diff --git a/packages/ui/node/index.ts b/packages/ui/node/index.ts index ac849e50a232..7b877d960a51 100644 --- a/packages/ui/node/index.ts +++ b/packages/ui/node/index.ts @@ -1,13 +1,13 @@ import type { Vite, Vitest } from 'vitest/node' import fs from 'node:fs' -import { fileURLToPath } from 'node:url' import { join, resolve } from 'pathe' import sirv from 'sirv' import c from 'tinyrainbow' import { isFileServingAllowed, isValidApiRequest } from 'vitest/node' import { version } from '../package.json' +import { distClientRoot } from './paths' -export const distClientRoot: string = resolve(fileURLToPath(import.meta.url), '../client') +export { distClientRoot } export default (ctx: Vitest): Vite.Plugin => { if (ctx.version !== version) { diff --git a/packages/ui/node/paths.ts b/packages/ui/node/paths.ts new file mode 100644 index 000000000000..32cbad0ac418 --- /dev/null +++ b/packages/ui/node/paths.ts @@ -0,0 +1,4 @@ +import { fileURLToPath } from 'node:url' +import { resolve } from 'pathe' + +export const distClientRoot: string = resolve(fileURLToPath(import.meta.url), '../client') diff --git a/packages/ui/node/reporter.ts b/packages/ui/node/reporter.ts index 0e9419b341bb..f5ec42ca5387 100644 --- a/packages/ui/node/reporter.ts +++ b/packages/ui/node/reporter.ts @@ -1,23 +1,20 @@ import type { TestAttachment } from '@vitest/runner' import type { SerializedError } from 'vitest' -import type { HTMLOptions, Reporter, RunnerTask, RunnerTestFile, TestModule, Vitest } from 'vitest/node' +import type { HTMLOptions, Reporter, ResolvedConfig, RunnerTask, RunnerTestFile, TestModule, Vitest } from 'vitest/node' import type { HTMLReportMetadata } from '../client/composables/client/static' import { existsSync, promises as fs, readFileSync } from 'node:fs' -import { fileURLToPath } from 'node:url' import { promisify } from 'node:util' import { gzip, constants as zlibConstants } from 'node:zlib' import { stringify } from 'flatted' import { dirname, relative, resolve } from 'pathe' -import { globSync } from 'tinyglobby' import c from 'tinyrainbow' import { getModuleGraph } from '../../vitest/src/utils/graph' +import { distClientRoot } from './paths' -interface PotentialConfig { - outputFile?: string | Partial> -} +const gzipAsync = promisify(gzip) -function getOutputFile(config: PotentialConfig | undefined) { - if (!config?.outputFile) { +function getOutputFile(config: ResolvedConfig) { + if (!config.outputFile) { return } @@ -28,15 +25,11 @@ function getOutputFile(config: PotentialConfig | undefined) { return config.outputFile.html } -const distDir = resolve(fileURLToPath(import.meta.url), '../../dist') - export default class HTMLReporter implements Reporter { - start = 0 ctx!: Vitest options: HTMLOptions private reporterDir!: string - private htmlFilePath!: string constructor(options: HTMLOptions) { this.options = options @@ -44,16 +37,12 @@ export default class HTMLReporter implements Reporter { async onInit(ctx: Vitest): Promise { this.ctx = ctx - this.start = Date.now() const htmlFile = this.options.outputFile || getOutputFile(this.ctx.config) || 'html/index.html' const htmlFilePath = resolve(this.ctx.config.root, htmlFile) this.reporterDir = dirname(htmlFilePath) - this.htmlFilePath = htmlFilePath - - await fs.mkdir(resolve(this.reporterDir, 'assets'), { recursive: true }) } async onTestRunEnd( @@ -69,29 +58,20 @@ export default class HTMLReporter implements Reporter { await inlineAttachments(result.files) } - const report = stringify(result) - const promiseGzip = promisify(gzip) - const data = await promiseGzip(report, { + // copy ui assets + await fs.cp(distClientRoot, this.reporterDir, { recursive: true }) + + // create index.html and metadata + const rawData = stringify(result) + const data = await gzipAsync(rawData, { level: zlibConstants.Z_BEST_COMPRESSION, }) - const ui = resolve(distDir, 'client') - // copy ui - const files = globSync(['**/*'], { cwd: ui, expandDirectories: false }) - await Promise.all( - files.map(async (f) => { - if (f === 'index.html') { - await handleIndexHtml({ - srcDir: ui, - dstDir: this.reporterDir, - data, - singleFile: this.options.singleFile, - }) - } - else { - await fs.copyFile(resolve(ui, f), resolve(this.reporterDir, f)) - } - }), - ) + await handleIndexHtml({ + srcDir: distClientRoot, + dstDir: this.reporterDir, + data, + singleFile: this.options.singleFile, + }) // copy attachments // TODO: unify attachmentsDir and html outputFile, so both live together without extra copy @@ -220,12 +200,12 @@ async function handleIndexHtml(options: { if (options.singleFile) { html = await inlineHtmlAssets(indexHtmlFilePath, html) - const base64 = Buffer.from(options.data).toString('base64') + const base64 = options.data.toString('base64') metadataCode = `Promise.resolve((${uint8ArrayFromBase64.toString()})("${base64}"))` } else { - const dataFile = `html.meta.json.gz` - await fs.writeFile(resolve(options.dstDir, dataFile), options.data, 'base64') + const dataFile = 'html.meta.json.gz' + await fs.writeFile(resolve(options.dstDir, dataFile), options.data) metadataCode = `fetch(new URL("./${dataFile}", window.location.href)).then(async res => new Uint8Array(await res.arrayBuffer()))` }