feat(runner): Add full names to tasks (#9087)

This commit is contained in:
Raul Macarie 2025-11-25 14:50:06 +01:00 committed by GitHub
parent 2cc34e0d4a
commit 821aa20021
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 453 additions and 9 deletions

View File

@ -42,7 +42,7 @@ export async function collectTests(
runner.onCollectStart?.(file) runner.onCollectStart?.(file)
clearCollectorContext(filepath, runner) clearCollectorContext(file, runner)
try { try {
const setupFiles = toArray(config.setupFiles) const setupFiles = toArray(config.setupFiles)

View File

@ -38,6 +38,7 @@ import { getHooks, setFn, setHooks, setTestFixture } from './map'
import { getCurrentTest } from './test-state' import { getCurrentTest } from './test-state'
import { findTestFileStackTrace } from './utils' import { findTestFileStackTrace } from './utils'
import { createChainable } from './utils/chain' import { createChainable } from './utils/chain'
import { createTaskName } from './utils/tasks'
/** /**
* Creates a suite of tests, allowing for grouping and hierarchical organization of tests. * Creates a suite of tests, allowing for grouping and hierarchical organization of tests.
@ -213,14 +214,15 @@ function createDefaultSuite(runner: VitestRunner) {
} }
export function clearCollectorContext( export function clearCollectorContext(
filepath: string, file: File,
currentRunner: VitestRunner, currentRunner: VitestRunner,
): void { ): void {
if (!defaultSuite) { if (!defaultSuite) {
defaultSuite = createDefaultSuite(currentRunner) defaultSuite = createDefaultSuite(currentRunner)
} }
defaultSuite.file = file
runner = currentRunner runner = currentRunner
currentTestFilepath = filepath currentTestFilepath = file.filepath
collectorContext.tasks.length = 0 collectorContext.tasks.length = 0
defaultSuite.clear() defaultSuite.clear()
collectorContext.currentSuite = defaultSuite collectorContext.currentSuite = defaultSuite
@ -297,10 +299,16 @@ function createSuiteCollector(
const task = function (name = '', options: TaskCustomOptions = {}) { const task = function (name = '', options: TaskCustomOptions = {}) {
const timeout = options?.timeout ?? runner.config.testTimeout const timeout = options?.timeout ?? runner.config.testTimeout
const currentSuite = collectorContext.currentSuite?.suite
const task: Test = { const task: Test = {
id: '', id: '',
name, name,
suite: collectorContext.currentSuite?.suite, fullName: createTaskName([
currentSuite?.fullName ?? collectorContext.currentSuite?.file?.fullName,
name,
]),
fullTestName: createTaskName([currentSuite?.fullTestName, name]),
suite: currentSuite,
each: options.each, each: options.each,
fails: options.fails, fails: options.fails,
context: undefined!, context: undefined!,
@ -439,11 +447,18 @@ function createSuiteCollector(
suiteOptions = { timeout: suiteOptions } suiteOptions = { timeout: suiteOptions }
} }
const currentSuite = collectorContext.currentSuite?.suite
suite = { suite = {
id: '', id: '',
type: 'suite', type: 'suite',
name, name,
suite: collectorContext.currentSuite?.suite, fullName: createTaskName([
currentSuite?.fullName ?? collectorContext.currentSuite?.file?.fullName,
name,
]),
fullTestName: createTaskName([currentSuite?.fullTestName, name]),
suite: currentSuite,
mode, mode,
each, each,
file: undefined!, file: undefined!,

View File

@ -18,6 +18,40 @@ export interface TaskBase {
* Task name provided by the user. If no name was provided, it will be an empty string. * Task name provided by the user. If no name was provided, it will be an empty string.
*/ */
name: string name: string
/**
* Full name including the file path, any parent suites, and this task's name.
*
* Uses ` > ` as the separator between levels.
*
* @example
* // file
* 'test/task-names.test.ts'
* @example
* // suite
* 'test/task-names.test.ts > meal planning'
* 'test/task-names.test.ts > meal planning > grocery lists'
* @example
* // test
* 'test/task-names.test.ts > meal planning > grocery lists > calculates ingredients'
*/
fullName: string
/**
* Full name excluding the file path, including any parent suites and this task's name. `undefined` for file tasks.
*
* Uses ` > ` as the separator between levels.
*
* @example
* // file
* undefined
* @example
* // suite
* 'meal planning'
* 'meal planning > grocery lists'
* @example
* // test
* 'meal planning > grocery lists > calculates ingredients'
*/
fullTestName?: string
/** /**
* Task mode. * Task mode.
* - **skip**: task is skipped * - **skip**: task is skipped
@ -291,6 +325,7 @@ export interface Test<ExtraContext = object> extends TaskPopulated {
* @experimental * @experimental
*/ */
artifacts: TestArtifact[] artifacts: TestArtifact[]
fullTestName: string
} }
export type Task = Test | Suite | File export type Task = Test | Suite | File
@ -630,6 +665,7 @@ export interface SuiteCollector<ExtraContext = object> {
)[] )[]
scoped: (fixtures: Fixtures<any, ExtraContext>) => void scoped: (fixtures: Fixtures<any, ExtraContext>) => void
fixtures: () => FixtureItem[] | undefined fixtures: () => FixtureItem[] | undefined
file?: File
suite?: Suite suite?: Suite
task: (name: string, options?: TaskCustomOptions) => Test<ExtraContext> task: (name: string, options?: TaskCustomOptions) => Test<ExtraContext>
collect: (file: File) => Promise<Suite> collect: (file: File) => Promise<Suite>

View File

@ -187,6 +187,7 @@ export function createFileTask(
const file: File = { const file: File = {
id: generateFileHash(path, projectName), id: generateFileHash(path, projectName),
name: path, name: path,
fullName: path,
type: 'suite', type: 'suite',
mode: 'queued', mode: 'queued',
filepath, filepath,

View File

@ -11,6 +11,7 @@ export {
export { limitConcurrency } from './limit-concurrency' export { limitConcurrency } from './limit-concurrency'
export { partitionSuiteChildren } from './suite' export { partitionSuiteChildren } from './suite'
export { export {
createTaskName,
getFullName, getFullName,
getNames, getNames,
getSuites, getSuites,

View File

@ -80,3 +80,7 @@ export function getFullName(task: Task, separator = ' > '): string {
export function getTestName(task: Task, separator = ' > '): string { export function getTestName(task: Task, separator = ' > '): string {
return getNames(task).slice(1).join(separator) return getNames(task).slice(1).join(separator)
} }
export function createTaskName(names: readonly (string | undefined)[], separator = ' > '): string {
return names.filter(name => name !== undefined).join(separator)
}

View File

@ -48,6 +48,7 @@ const fileWithTextStacks: RunnerTestFile = {
type: 'suite', type: 'suite',
mode: 'run', mode: 'run',
filepath: 'test/plain-stack-trace.ts', filepath: 'test/plain-stack-trace.ts',
fullName: 'test/plain-stack-trace.ts',
meta: {}, meta: {},
result: { result: {
state: 'fail', state: 'fail',
@ -98,6 +99,7 @@ describe('ViewReport', () => {
type: 'suite', type: 'suite',
mode: 'run', mode: 'run',
filepath: 'test/plain-stack-trace.ts', filepath: 'test/plain-stack-trace.ts',
fullName: 'test/plain-stack-trace.ts',
meta: {}, meta: {},
result: { result: {
state: 'fail', state: 'fail',
@ -157,6 +159,7 @@ describe('ViewReport', () => {
type: 'suite', type: 'suite',
mode: 'run', mode: 'run',
filepath: 'test/plain-stack-trace.ts', filepath: 'test/plain-stack-trace.ts',
fullName: 'test/plain-stack-trace.ts',
meta: {}, meta: {},
result: { result: {
state: 'fail', state: 'fail',

View File

@ -46,6 +46,7 @@ const failed = computed(() => {
id: file!.id, id: file!.id,
file: file!, file: file!,
name: file!.name, name: file!.name,
fullName: file!.name,
level: 0, level: 0,
type: 'suite', type: 'suite',
mode: 'run', mode: 'run',

View File

@ -4,6 +4,7 @@ import type { TestProject } from './project'
import { originalPositionFor, TraceMap } from '@jridgewell/trace-mapping' import { originalPositionFor, TraceMap } from '@jridgewell/trace-mapping'
import { import {
calculateSuiteHash, calculateSuiteHash,
createTaskName,
generateHash, generateHash,
interpretTaskModes, interpretTaskModes,
someTasksAreOnly, someTasksAreOnly,
@ -193,6 +194,7 @@ export function createFailedFileTask(project: TestProject, filepath: string, err
type: 'suite', type: 'suite',
id: /* @__PURE__ */ generateHash(`${testFilepath}${project.config.name || ''}`), id: /* @__PURE__ */ generateHash(`${testFilepath}${project.config.name || ''}`),
name: testFilepath, name: testFilepath,
fullName: testFilepath,
mode: 'run', mode: 'run',
tasks: [], tasks: [],
start: 0, start: 0,
@ -252,6 +254,7 @@ function createFileTask(
type: 'suite', type: 'suite',
id: /* @__PURE__ */ generateHash(`${testFilepath}${options.name || ''}`), id: /* @__PURE__ */ generateHash(`${testFilepath}${options.name || ''}`),
name: testFilepath, name: testFilepath,
fullName: testFilepath,
mode: 'run', mode: 'run',
tasks: [], tasks: [],
start: ast.start, start: ast.start,
@ -324,6 +327,8 @@ function createFileTask(
tasks: [], tasks: [],
mode, mode,
name: definition.name, name: definition.name,
fullName: createTaskName([latestSuite.fullName, definition.name]),
fullTestName: createTaskName([latestSuite.fullTestName, definition.name]),
end: definition.end, end: definition.end,
start: definition.start, start: definition.start,
location, location,
@ -343,6 +348,8 @@ function createFileTask(
mode, mode,
context: {} as any, // not used on the server context: {} as any, // not used on the server
name: definition.name, name: definition.name,
fullName: createTaskName([latestSuite.fullName, definition.name]),
fullTestName: createTaskName([latestSuite.fullTestName, definition.name]),
end: definition.end, end: definition.end,
start: definition.start, start: definition.start,
location, location,

View File

@ -327,6 +327,8 @@ export class JUnitReporter implements Reporter {
id: file.id, id: file.id,
type: 'test', type: 'test',
name: file.name, name: file.name,
fullName: file.name,
fullTestName: file.name,
mode: 'run', mode: 'run',
result: file.result, result: file.result,
meta: {}, meta: {},

View File

@ -3,6 +3,7 @@ import type { Rollup } from 'vite'
import type { TestProject } from '../node/project' import type { TestProject } from '../node/project'
import { import {
calculateSuiteHash, calculateSuiteHash,
createTaskName,
generateHash, generateHash,
interpretTaskModes, interpretTaskModes,
someTasksAreOnly, someTasksAreOnly,
@ -60,6 +61,7 @@ export async function collectTests(
type: 'suite', type: 'suite',
id: generateHash(`${testFilepath}${typecheckSubprojectName}`), id: generateHash(`${testFilepath}${typecheckSubprojectName}`),
name: testFilepath, name: testFilepath,
fullName: testFilepath,
mode: 'run', mode: 'run',
tasks: [], tasks: [],
start: ast.start, start: ast.start,
@ -185,6 +187,8 @@ export async function collectTests(
tasks: [], tasks: [],
mode, mode,
name: definition.name, name: definition.name,
fullName: createTaskName([lastSuite.fullName, definition.name]),
fullTestName: createTaskName([lastSuite.fullTestName, definition.name]),
end: definition.end, end: definition.end,
start: definition.start, start: definition.start,
meta: { meta: {
@ -205,6 +209,8 @@ export async function collectTests(
timeout: 0, timeout: 0,
context: {} as any, // not used in typecheck context: {} as any, // not used in typecheck
name: definition.name, name: definition.name,
fullName: createTaskName([lastSuite.fullName, definition.name]),
fullTestName: createTaskName([lastSuite.fullTestName, definition.name]),
end: definition.end, end: definition.end,
start: definition.start, start: definition.start,
annotations: [], annotations: [],

View File

@ -83,9 +83,12 @@ async function onMessage(message: WorkerRequest, project: TestProject, options:
) )
taskFile.mode = 'run' taskFile.mode = 'run'
taskFile.result = { state: 'pass' } taskFile.result = { state: 'pass' }
const taskName = 'custom test'
const taskTest: RunnerTestCase = { const taskTest: RunnerTestCase = {
type: 'test', type: 'test',
name: 'custom test', name: taskName,
fullName: `${taskFile.fullName} > ${taskName}`,
fullTestName: `${taskFile.fullTestName} > ${taskName}`,
id: `${taskFile.id}_0`, id: `${taskFile.id}_0`,
context: {} as any, context: {} as any,
suite: taskFile, suite: taskFile,

View File

@ -0,0 +1,325 @@
/**
* This test is self-referential - it validates its own structure and the structure of the tests defined below.
*
* The order and nesting of these test definitions MUST match the assertions, or the test will fail. The assertions use array indices (e.g., `task.file.tasks[0]`) to access specific tests, so reordering will break the test.
*
* If you need to modify this structure, update both the setup below AND the corresponding assertions above to maintain consistency.
*/
import type { RunnerTestSuite } from 'vitest'
import { describe, test } from 'vitest'
test('tasks have correct `fullName` and `fullTestName` properties', ({ expect, task }) => {
// this test validates the structure defined at the bottom of this file.
//
// structure (must match setup at bottom):
//
// task-names.test.ts
// ├─ [0] tasks have correct `fullName` and `fullTestName` properties (this test)
// ├─ [1] creates new recipe
// ├─ [2] searches by ingredient
// ├─ [3] recipe management/
// │ ├─ [0] saves recipe
// │ └─ [1] deletes recipe
// └─ [4] meal planning/
// ├─ [0] generates weekly plan
// ├─ [1] grocery lists/
// │ ├─ [0] calculates ingredients
// │ ├─ [1] combines duplicate items
// │ └─ [2] shopping/
// │ ├─ [0] marks items as purchased
// │ └─ [1] estimates total cost
// ├─ [2] exports calendar
// └─ [3] nutrition tracking/
// ├─ [0] calculates daily calories
// └─ [1] tracks macros
// validate this test itself (task.file.tasks[0])
expect(task.suite).toBe(undefined)
expect(
task.fullName,
).toBe('test/task-names.test.ts > tasks have correct `fullName` and `fullTestName` properties')
expect(
task.fullTestName,
).toBe('tasks have correct `fullName` and `fullTestName` properties')
expect(task.file.fullName).toBe('test/task-names.test.ts')
expect(task.file.fullTestName).toBe(undefined)
const thisTest = task.file.tasks[0]
expect(thisTest.suite).toBe(undefined)
expect(thisTest.fullName).toBe(
'test/task-names.test.ts > tasks have correct `fullName` and `fullTestName` properties',
)
expect(thisTest.fullTestName).toBe(
'tasks have correct `fullName` and `fullTestName` properties',
)
expect(
task.file.tasks,
).toHaveLength(5)
// top-level tests
const createsRecipe = task.file.tasks[1]
expect(createsRecipe.suite).toBe(undefined)
expect(createsRecipe.fullName).toBe(
'test/task-names.test.ts > creates new recipe',
)
expect(createsRecipe.fullTestName).toBe(
'creates new recipe',
)
const searchIngredient = task.file.tasks[2]
expect(searchIngredient.suite).toBe(undefined)
expect(searchIngredient.fullName).toBe(
'test/task-names.test.ts > searches by ingredient',
)
expect(searchIngredient.fullTestName).toBe(
'searches by ingredient',
)
// single-level suite
const recipeManagement = task.file.tasks[3] as RunnerTestSuite
expect(recipeManagement.suite).toBe(undefined)
expect(recipeManagement.fullName).toBe(
'test/task-names.test.ts > recipe management',
)
expect(recipeManagement.fullTestName).toBe(
'recipe management',
)
expect(recipeManagement.tasks).toHaveLength(2)
const savesRecipe = recipeManagement.tasks[0]
expect(savesRecipe.suite?.fullName).toBe(
'test/task-names.test.ts > recipe management',
)
expect(savesRecipe.suite?.fullTestName).toBe(
'recipe management',
)
expect(savesRecipe.fullName).toBe(
'test/task-names.test.ts > recipe management > saves recipe',
)
expect(savesRecipe.fullTestName).toBe(
'recipe management > saves recipe',
)
const deletesRecipe = recipeManagement.tasks[1]
expect(deletesRecipe.suite?.fullName).toBe(
'test/task-names.test.ts > recipe management',
)
expect(deletesRecipe.suite?.fullTestName).toBe(
'recipe management',
)
expect(deletesRecipe.fullName).toBe(
'test/task-names.test.ts > recipe management > deletes recipe',
)
expect(deletesRecipe.fullTestName).toBe(
'recipe management > deletes recipe',
)
// nested suites with mixed patterns
const mealPlanning = task.file.tasks[4] as RunnerTestSuite
expect(mealPlanning.suite).toBe(undefined)
expect(mealPlanning.fullName).toBe(
'test/task-names.test.ts > meal planning',
)
expect(mealPlanning.fullTestName).toBe(
'meal planning',
)
expect(mealPlanning.tasks).toHaveLength(4)
const generatesPlan = mealPlanning.tasks[0]
expect(generatesPlan.suite?.fullName).toBe(
'test/task-names.test.ts > meal planning',
)
expect(generatesPlan.suite?.fullTestName).toBe(
'meal planning',
)
expect(generatesPlan.fullName).toBe(
'test/task-names.test.ts > meal planning > generates weekly plan',
)
expect(generatesPlan.fullTestName).toBe(
'meal planning > generates weekly plan',
)
const groceryList = mealPlanning.tasks[1] as RunnerTestSuite
expect(groceryList.suite?.fullName).toBe(
'test/task-names.test.ts > meal planning',
)
expect(groceryList.suite?.fullTestName).toBe(
'meal planning',
)
expect(groceryList.fullName).toBe(
'test/task-names.test.ts > meal planning > grocery lists',
)
expect(groceryList.fullTestName).toBe(
'meal planning > grocery lists',
)
expect(groceryList.tasks).toHaveLength(3)
const calculatesIngredients = groceryList.tasks[0]
expect(calculatesIngredients.suite?.fullName).toBe(
'test/task-names.test.ts > meal planning > grocery lists',
)
expect(calculatesIngredients.suite?.fullTestName).toBe(
'meal planning > grocery lists',
)
expect(calculatesIngredients.fullName).toBe(
'test/task-names.test.ts > meal planning > grocery lists > calculates ingredients',
)
expect(calculatesIngredients.fullTestName).toBe(
'meal planning > grocery lists > calculates ingredients',
)
const combinesItems = groceryList.tasks[1]
expect(combinesItems.suite?.fullName).toBe(
'test/task-names.test.ts > meal planning > grocery lists',
)
expect(combinesItems.suite?.fullTestName).toBe(
'meal planning > grocery lists',
)
expect(combinesItems.fullName).toBe(
'test/task-names.test.ts > meal planning > grocery lists > combines duplicate items',
)
expect(combinesItems.fullTestName).toBe(
'meal planning > grocery lists > combines duplicate items',
)
const shopping = groceryList.tasks[2] as RunnerTestSuite
expect(shopping.suite?.fullName).toBe(
'test/task-names.test.ts > meal planning > grocery lists',
)
expect(shopping.suite?.fullTestName).toBe(
'meal planning > grocery lists',
)
expect(shopping.fullName).toBe(
'test/task-names.test.ts > meal planning > grocery lists > shopping',
)
expect(shopping.fullTestName).toBe(
'meal planning > grocery lists > shopping',
)
expect(shopping.tasks).toHaveLength(2)
const marksItemsPurchased = shopping.tasks[0]
expect(marksItemsPurchased.suite?.fullName).toBe(
'test/task-names.test.ts > meal planning > grocery lists > shopping',
)
expect(marksItemsPurchased.suite?.fullTestName).toBe(
'meal planning > grocery lists > shopping',
)
expect(marksItemsPurchased.fullName).toBe(
'test/task-names.test.ts > meal planning > grocery lists > shopping > marks items as purchased',
)
expect(marksItemsPurchased.fullTestName).toBe(
'meal planning > grocery lists > shopping > marks items as purchased',
)
const estimatesTotalCost = shopping.tasks[1]
expect(estimatesTotalCost.suite?.fullName).toBe(
'test/task-names.test.ts > meal planning > grocery lists > shopping',
)
expect(estimatesTotalCost.suite?.fullTestName).toBe(
'meal planning > grocery lists > shopping',
)
expect(estimatesTotalCost.fullName).toBe(
'test/task-names.test.ts > meal planning > grocery lists > shopping > estimates total cost',
)
expect(estimatesTotalCost.fullTestName).toBe(
'meal planning > grocery lists > shopping > estimates total cost',
)
const exportsCalendar = mealPlanning.tasks[2]
expect(exportsCalendar.suite?.fullName).toBe(
'test/task-names.test.ts > meal planning',
)
expect(exportsCalendar.suite?.fullTestName).toBe(
'meal planning',
)
expect(exportsCalendar.fullName).toBe(
'test/task-names.test.ts > meal planning > exports calendar',
)
expect(exportsCalendar.fullTestName).toBe(
'meal planning > exports calendar',
)
const nutritionTracking = mealPlanning.tasks[3] as RunnerTestSuite
expect(nutritionTracking.suite?.fullName).toBe(
'test/task-names.test.ts > meal planning',
)
expect(nutritionTracking.suite?.fullTestName).toBe(
'meal planning',
)
expect(nutritionTracking.fullName).toBe(
'test/task-names.test.ts > meal planning > nutrition tracking',
)
expect(nutritionTracking.fullTestName).toBe(
'meal planning > nutrition tracking',
)
expect(nutritionTracking.tasks).toHaveLength(2)
const calculatesCalories = nutritionTracking.tasks[0]
expect(calculatesCalories.suite?.fullName).toBe(
'test/task-names.test.ts > meal planning > nutrition tracking',
)
expect(calculatesCalories.suite?.fullTestName).toBe(
'meal planning > nutrition tracking',
)
expect(calculatesCalories.fullName).toBe(
'test/task-names.test.ts > meal planning > nutrition tracking > calculates daily calories',
)
expect(calculatesCalories.fullTestName).toBe(
'meal planning > nutrition tracking > calculates daily calories',
)
const tracksMacros = nutritionTracking.tasks[1]
expect(tracksMacros.suite?.fullName).toBe(
'test/task-names.test.ts > meal planning > nutrition tracking',
)
expect(tracksMacros.suite?.fullTestName).toBe(
'meal planning > nutrition tracking',
)
expect(tracksMacros.fullName).toBe(
'test/task-names.test.ts > meal planning > nutrition tracking > tracks macros',
)
expect(tracksMacros.fullTestName).toBe(
'meal planning > nutrition tracking > tracks macros',
)
})
// setup
// top-level tests
test('creates new recipe')
test('searches by ingredient')
// single-level suite
describe('recipe management', () => {
test('saves recipe')
test('deletes recipe')
})
// nested suites with mixed patterns
describe('meal planning', () => {
test('generates weekly plan')
describe('grocery lists', () => {
test('calculates ingredients')
test('combines duplicate items')
describe('shopping', () => {
test('marks items as purchased')
test('estimates total cost')
})
})
test('exports calendar')
describe('nutrition tracking', () => {
test('calculates daily calories')
test('tracks macros')
})
})

View File

@ -12,10 +12,13 @@ file.result = {
duration: 145.99284195899963, duration: 145.99284195899963,
} }
const suiteName = 'suite'
const suite: RunnerTestSuite = { const suite: RunnerTestSuite = {
id: `${file.id}_0`, id: `${file.id}_0`,
type: 'suite', type: 'suite',
name: 'suite', name: suiteName,
fullName: `${file.fullName} > ${suiteName}`,
fullTestName: `${file.fullTestName} > ${suiteName}`,
mode: 'run', mode: 'run',
meta: {}, meta: {},
file, file,
@ -34,6 +37,8 @@ passedFile.tasks.push({
id: `${file.id}_1`, id: `${file.id}_1`,
type: 'test', type: 'test',
name: 'Math.sqrt()', name: 'Math.sqrt()',
fullName: `${suite.fullName} > Math.sqrt()`,
fullTestName: `${suite.fullTestName} > Math.sqrt()`,
mode: 'run', mode: 'run',
fails: undefined, fails: undefined,
suite, suite,
@ -75,6 +80,8 @@ const tasks: RunnerTestCase[] = [
id: `${suite.id}_0`, id: `${suite.id}_0`,
type: 'test', type: 'test',
name: 'Math.sqrt()', name: 'Math.sqrt()',
fullName: `${suite.fullName} > Math.sqrt()`,
fullTestName: `${suite.fullTestName} > Math.sqrt()`,
mode: 'run', mode: 'run',
fails: undefined, fails: undefined,
meta: {}, meta: {},
@ -98,6 +105,8 @@ const tasks: RunnerTestCase[] = [
id: `${suite.id}_1`, id: `${suite.id}_1`,
type: 'test', type: 'test',
name: 'JSON', name: 'JSON',
fullName: `${suite.fullName} > JSON`,
fullTestName: `${suite.fullTestName} > JSON`,
mode: 'run', mode: 'run',
annotations: [], annotations: [],
artifacts: [], artifacts: [],
@ -113,6 +122,8 @@ const tasks: RunnerTestCase[] = [
id: `${suite.id}_3`, id: `${suite.id}_3`,
type: 'test', type: 'test',
name: 'async with timeout', name: 'async with timeout',
fullName: `${suite.fullName} > async with timeout`,
fullTestName: `${suite.fullTestName} > async with timeout`,
mode: 'skip', mode: 'skip',
suite, suite,
fails: undefined, fails: undefined,
@ -128,6 +139,8 @@ const tasks: RunnerTestCase[] = [
id: `${suite.id}_4`, id: `${suite.id}_4`,
type: 'test', type: 'test',
name: 'timeout', name: 'timeout',
fullName: `${suite.fullName} > timeout`,
fullTestName: `${suite.fullTestName} > timeout`,
annotations: [], annotations: [],
artifacts: [], artifacts: [],
mode: 'run', mode: 'run',
@ -143,6 +156,8 @@ const tasks: RunnerTestCase[] = [
id: `${suite.id}_5`, id: `${suite.id}_5`,
type: 'test', type: 'test',
name: 'callback setup success ', name: 'callback setup success ',
fullName: `${suite.fullName} > callback setup success `,
fullTestName: `${suite.fullTestName} > callback setup success `,
mode: 'run', mode: 'run',
suite, suite,
fails: undefined, fails: undefined,
@ -158,6 +173,8 @@ const tasks: RunnerTestCase[] = [
id: `${suite.id}_6`, id: `${suite.id}_6`,
type: 'test', type: 'test',
name: 'callback test success ', name: 'callback test success ',
fullName: `${suite.fullName} > callback test success `,
fullTestName: `${suite.fullTestName} > callback test success `,
mode: 'run', mode: 'run',
suite, suite,
fails: undefined, fails: undefined,
@ -173,6 +190,8 @@ const tasks: RunnerTestCase[] = [
id: `${suite.id}_7`, id: `${suite.id}_7`,
type: 'test', type: 'test',
name: 'callback setup success done(false)', name: 'callback setup success done(false)',
fullName: `${suite.fullName} > callback setup success done(false)`,
fullTestName: `${suite.fullTestName} > callback setup success done(false)`,
mode: 'run', mode: 'run',
suite, suite,
fails: undefined, fails: undefined,
@ -188,6 +207,8 @@ const tasks: RunnerTestCase[] = [
id: `${suite.id}_8`, id: `${suite.id}_8`,
type: 'test', type: 'test',
name: 'callback test success done(false)', name: 'callback test success done(false)',
fullName: `${suite.fullName} > callback test success done(false)`,
fullTestName: `${suite.fullTestName} > callback test success done(false)`,
mode: 'run', mode: 'run',
suite, suite,
fails: undefined, fails: undefined,
@ -211,6 +232,8 @@ const tasks: RunnerTestCase[] = [
id: `${suite.id}_9`, id: `${suite.id}_9`,
type: 'test', type: 'test',
name: 'todo test', name: 'todo test',
fullName: `${suite.fullName} > todo test`,
fullTestName: `${suite.fullTestName} > todo test`,
mode: 'todo', mode: 'todo',
suite, suite,
timeout: 0, timeout: 0,

View File

@ -9,6 +9,7 @@ exports[`html reporter > resolves to "failing" status for test file "json-fail"
"environmentLoad": 0, "environmentLoad": 0,
"file": [Circular], "file": [Circular],
"filepath": "<rootDir>/test/reporters/fixtures/json-fail.test.ts", "filepath": "<rootDir>/test/reporters/fixtures/json-fail.test.ts",
"fullName": "json-fail.test.ts",
"id": 0, "id": 0,
"importDurations": {}, "importDurations": {},
"meta": {}, "meta": {},
@ -28,6 +29,8 @@ exports[`html reporter > resolves to "failing" status for test file "json-fail"
"annotations": [], "annotations": [],
"artifacts": [], "artifacts": [],
"file": [Circular], "file": [Circular],
"fullName": "json-fail.test.ts > should fail",
"fullTestName": "should fail",
"id": 0, "id": 0,
"location": { "location": {
"column": 1, "column": 1,
@ -129,6 +132,7 @@ exports[`html reporter > resolves to "passing" status for test file "all-passing
"environmentLoad": 0, "environmentLoad": 0,
"file": [Circular], "file": [Circular],
"filepath": "<rootDir>/test/reporters/fixtures/all-passing-or-skipped.test.ts", "filepath": "<rootDir>/test/reporters/fixtures/all-passing-or-skipped.test.ts",
"fullName": "all-passing-or-skipped.test.ts",
"id": 0, "id": 0,
"importDurations": {}, "importDurations": {},
"meta": {}, "meta": {},
@ -148,6 +152,8 @@ exports[`html reporter > resolves to "passing" status for test file "all-passing
"annotations": [], "annotations": [],
"artifacts": [], "artifacts": [],
"file": [Circular], "file": [Circular],
"fullName": "all-passing-or-skipped.test.ts > 2 + 3 = 5",
"fullTestName": "2 + 3 = 5",
"id": 0, "id": 0,
"location": { "location": {
"column": 1, "column": 1,
@ -170,6 +176,8 @@ exports[`html reporter > resolves to "passing" status for test file "all-passing
"annotations": [], "annotations": [],
"artifacts": [], "artifacts": [],
"file": [Circular], "file": [Circular],
"fullName": "all-passing-or-skipped.test.ts > 3 + 3 = 6",
"fullTestName": "3 + 3 = 6",
"id": "1111755131_1", "id": "1111755131_1",
"location": { "location": {
"column": 6, "column": 6,

View File

@ -10,19 +10,26 @@ const root = resolve(import.meta.dirname, '../fixtures')
test('calc the duration used by junit', () => { test('calc the duration used by junit', () => {
const result: RunnerTaskResult = { state: 'pass', duration: 0 } const result: RunnerTaskResult = { state: 'pass', duration: 0 }
const file: RunnerTestFile = createFileTask('/test.ts', '/', 'test') const file: RunnerTestFile = createFileTask('/test.ts', '/', 'test')
const suiteName
= 'suite'
const suite: RunnerTestSuite = { const suite: RunnerTestSuite = {
id: '1_0', id: '1_0',
type: 'suite', type: 'suite',
name: 'suite', name: suiteName,
fullName: `${file.fullName} > ${suiteName}`,
fullTestName: `${file.fullTestName} > ${suiteName}`,
mode: 'run', mode: 'run',
tasks: [], tasks: [],
file, file,
meta: {}, meta: {},
} }
const taskName = 'timeout'
const task: RunnerTestCase = { const task: RunnerTestCase = {
id: '1_0_0', id: '1_0_0',
type: 'test', type: 'test',
name: 'timeout', name: taskName,
fullName: `${suite.fullName} > ${suiteName}`,
fullTestName: `${suite.fullTestName} > ${suiteName}`,
mode: 'run', mode: 'run',
result, result,
annotations: [], annotations: [],

View File

@ -286,6 +286,8 @@ function createTest(name: string, file: File): Test {
return { return {
type: 'test', type: 'test',
name, name,
fullName: `${file.fullName} > ${name}`,
fullTestName: `${file.fullTestName} > ${name}`,
id: `${file.id}_0`, id: `${file.id}_0`,
mode: 'run', mode: 'run',
file, file,