Skip to content

Commit

Permalink
Rebase and changing repeated log in terminal
Browse files Browse the repository at this point in the history
  • Loading branch information
samkevin1 committed Feb 27, 2023
1 parent f15f6cb commit 5836160
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 66 deletions.
131 changes: 76 additions & 55 deletions packages/runner/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const callCleanupHooks = async (cleanups: HookCleanupCallback[]) => {
export async function runTest(test: Test, runner: VitestRunner) {
await runner.onBeforeRunTest?.(test)

if (test.mode !== 'run')
if (test.mode !== 'run' && test.mode !== 'repeats')
return

if (test.result?.state === 'fail') {
Expand All @@ -125,7 +125,7 @@ export async function runTest(test: Test, runner: VitestRunner) {

setCurrentTest(test)

const retry = test.retry || 1
const retry = test.mode === 'repeats' ? test.repeats! : test.retry || 1
for (let retryCount = 0; retryCount < retry; retryCount++) {
let beforeEachCleanups: HookCleanupCallback[] = []
try {
Expand All @@ -147,7 +147,10 @@ export async function runTest(test: Test, runner: VitestRunner) {

await runner.onAfterTryTest?.(test, retryCount)

test.result.state = 'pass'
if (test.mode === 'run')
test.result.state = 'pass'
else if (test.mode === 'repeats' && retry === retryCount)
test.result.state = 'pass'
}
catch (e) {
failTask(test.result, e)
Expand All @@ -164,6 +167,9 @@ export async function runTest(test: Test, runner: VitestRunner) {
if (test.result.state === 'pass')
break

if (test.mode === 'repeats' && test.result.state === 'fail')
break

// update retry info
updateTask(test, runner)
}
Expand Down Expand Up @@ -240,68 +246,83 @@ export async function runSuite(suite: Suite, runner: VitestRunner) {
suite.result.state = 'todo'
}
else {
try {
beforeAllCleanups = await callSuiteHook(suite, suite, 'beforeAll', runner, [suite])
let retry = suite.repeats

if (runner.runSuite) {
await runner.runSuite(suite)
}
else {
for (let tasksGroup of partitionSuiteChildren(suite)) {
if (tasksGroup[0].concurrent === true) {
const mutex = limit(runner.config.maxConcurrency)
await Promise.all(tasksGroup.map(c => mutex(() => runSuiteChild(c, runner))))
}
else {
const { sequence } = runner.config
if (sequence.shuffle || suite.shuffle) {
// run describe block independently from tests
const suites = tasksGroup.filter(group => group.type === 'suite')
const tests = tasksGroup.filter(group => group.type === 'test')
const groups = shuffle([suites, tests], sequence.seed)
tasksGroup = groups.flatMap(group => shuffle(group, sequence.seed))
for (let retryCount = 0; retryCount < retry!; retryCount++) {
if (suite.mode !== 'repeats')
retry = 1
try {
beforeAllCleanups = await callSuiteHook(suite, suite, 'beforeAll', runner, [suite])

if (runner.runSuite) {
await runner.runSuite(suite)
}
else {
for (let tasksGroup of partitionSuiteChildren(suite)) {
if (tasksGroup[0].concurrent === true) {
const mutex = limit(runner.config.maxConcurrency)
await Promise.all(tasksGroup.map(c => mutex(() => runSuiteChild(c, runner))))
}
else {
const { sequence } = runner.config
if (sequence.shuffle || suite.shuffle) {
// run describe block independently from tests
const suites = tasksGroup.filter(group => group.type === 'suite')
const tests = tasksGroup.filter(group => group.type === 'test')
const groups = shuffle([suites, tests], sequence.seed)
tasksGroup = groups.flatMap(group => shuffle(group, sequence.seed))
}
for (const c of tasksGroup)
await runSuiteChild(c, runner)
}
for (const c of tasksGroup)
await runSuiteChild(c, runner)
}
}
}
}
catch (e) {
failTask(suite.result, e)
}
}

try {
await callSuiteHook(suite, suite, 'afterAll', runner, [suite])
await callCleanupHooks(beforeAllCleanups)
}
catch (e) {
failTask(suite.result, e)
}
catch (e) {
failTask(suite.result, e)
}

suite.result.duration = now() - start
try {
if (suite.mode !== 'repeats')
await callSuiteHook(suite, suite, 'afterAll', runner, [suite])
else if (suite.mode === 'repeats' && retry === retryCount)
await callSuiteHook(suite, suite, 'afterAll', runner, [suite])
await callCleanupHooks(beforeAllCleanups)
}
catch (e) {
failTask(suite.result, e)
}

if (suite.mode === 'run') {
if (!hasTests(suite)) {
suite.result.state = 'fail'
if (!suite.result.error) {
const error = processError(new Error(`No test found in suite ${suite.name}`))
suite.result.error = error
suite.result.errors = [error]
if (suite.mode === 'run') {
if (!hasTests(suite)) {
suite.result.state = 'fail'
if (!suite.result.error) {
const error = processError(new Error(`No test found in suite ${suite.name}`))
suite.result.error = error
suite.result.errors = [error]
}
}
else if (hasFailed(suite)) {
suite.result.state = 'fail'
}
else {
suite.result.state = 'pass'
}
}
}
else if (hasFailed(suite)) {
suite.result.state = 'fail'
}
else {
suite.result.state = 'pass'
}
}

await runner.onAfterRunSuite?.(suite)
updateTask(suite, runner)

updateTask(suite, runner)
suite.result.duration = now() - start

await runner.onAfterRunSuite?.(suite)

if (suite.result.state === 'pass')
break

if (suite.mode === 'repeats' && suite.result.state === 'fail')
break
}
}
}

async function runSuiteChild(c: Task, runner: VitestRunner) {
Expand Down
2 changes: 1 addition & 1 deletion packages/runner/src/suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m
id: '',
name,
type: 'custom',
mode: self.only ? 'only' : self.skip ? 'skip' : self.todo ? 'todo' : 'run',
mode: self.only ? 'only' : self.skip ? 'skip' : self.todo ? 'todo' : self.repeats ? 'repeats' : 'run',
}
tasks.push(task)
return task
Expand Down
10 changes: 8 additions & 2 deletions packages/runner/src/types/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface TaskBase {
result?: TaskResult
retry?: number
meta?: any
repeats?: number
}

export interface TaskCustom extends TaskBase {
Expand All @@ -35,6 +36,7 @@ export interface TaskResult {
htmlError?: string
hooks?: Partial<Record<keyof SuiteHooks, TaskState>>
retryCount?: number
repeatCount?: number
}

export type TaskResultPack = [id: string, result: TaskResult | undefined]
Expand Down Expand Up @@ -161,7 +163,11 @@ export interface TestOptions {
* @default 1
*/
retry?: number

/**
* How many times the test will repeat.
*
* @default 5
*/
repeats?: number
}

Expand All @@ -172,7 +178,7 @@ export type TestAPI<ExtraContext = {}> = ChainableTestAPI<ExtraContext> & {
}

type ChainableSuiteAPI<ExtraContext = {}> = ChainableFunction<
'concurrent' | 'only' | 'skip' | 'todo' | 'shuffle',
'concurrent' | 'only' | 'skip' | 'todo' | 'shuffle' | 'repeats',
[name: string, factory?: SuiteFactory<ExtraContext>, options?: number | TestOptions],
SuiteCollector<ExtraContext>,
{
Expand Down
4 changes: 2 additions & 2 deletions packages/vitest/src/node/reporters/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { parseStacktrace } from '../../utils/source-map'
// the following types are extracted from the Jest repository (and simplified)
// the commented-out fields are the missing ones

type Status = 'passed' | 'failed' | 'skipped' | 'pending' | 'todo' | 'disabled' | 'repeated'
type Status = 'passed' | 'failed' | 'skipped' | 'pending' | 'todo' | 'disabled'
type Milliseconds = number
interface Callsite { line: number; column: number }
const StatusMap: Record<TaskState, Status> = {
Expand All @@ -20,7 +20,7 @@ const StatusMap: Record<TaskState, Status> = {
run: 'pending',
skip: 'skipped',
todo: 'todo',
repeats: 'repeated',
repeats: 'pending',
}

interface FormattedAssertionResult {
Expand Down
3 changes: 3 additions & 0 deletions packages/vitest/src/node/reporters/renderers/listRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ export function renderTree(tasks: Task[], options: ListRendererOptions, level =
if (task.mode === 'skip' || task.mode === 'todo')
suffix += ` ${c.dim(c.gray('[skipped]'))}`

if (task.mode === 'repeats')
suffix += ` ${c.dim(c.gray('[repeated]'))}`

if (task.result?.duration != null) {
if (task.result.duration > DURATION_LONG)
suffix += c.yellow(` ${Math.round(task.result.duration)}${c.dim('ms')}`)
Expand Down
2 changes: 0 additions & 2 deletions packages/vitest/src/node/reporters/renderers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,11 @@ export function getStateString(tasks: Task[], name = 'tests', showTotal = true)
const failed = tasks.filter(i => i.result?.state === 'fail')
const skipped = tasks.filter(i => i.mode === 'skip')
const todo = tasks.filter(i => i.mode === 'todo')
const repeated = tasks.filter(i => i.mode === 'repeats')

return [
failed.length ? c.bold(c.red(`${failed.length} failed`)) : null,
passed.length ? c.bold(c.green(`${passed.length} passed`)) : null,
skipped.length ? c.yellow(`${skipped.length} skipped`) : null,
repeated.length ? c.yellow(`${repeated.length} repeated`) : null,
todo.length ? c.gray(`${todo.length} todo`) : null,
].filter(Boolean).join(c.dim(' | ')) + (showTotal ? c.gray(` (${tasks.length})`) : '')
}
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/typecheck/collect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export async function collectTests(ctx: Vitest, filepath: string): Promise<null
const { arguments: [{ value: message }] } = node as any
const property = callee?.property?.name
let mode = !property || property === name ? 'run' : property
if (!['run', 'skip', 'todo', 'only', 'skipIf', 'runIf'].includes(mode))
if (!['run', 'skip', 'todo', 'only', 'skipIf', 'runIf', 'repeats'].includes(mode))
throw new Error(`${name}.${mode} syntax is not supported when testing types`)
// cannot statically analyze, so we always skip it
if (mode === 'skipIf' || mode === 'runIf')
Expand Down
6 changes: 3 additions & 3 deletions test/core/test/repeats.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { afterAll, describe, expect, test } from 'vitest'

const testNumbers: number[] = []

describe('repeat tests', () => {
describe('testing it/test', () => {
const result = [1, 1, 1, 1, 1, 2, 2, 2]
// repeats 5 times by default
test.repeats('test 1', () => {
Expand All @@ -26,13 +26,13 @@ describe('repeat tests', () => {

const describeNumbers: number[] = []

describe.repeats('repeat tests', () => {
describe.repeats('testing describe 1', () => {
test('test 1', () => {
describeNumbers.push(1)
})
})

describe.repeats('repeat tests', () => {
describe.repeats('testing describe 2', () => {
test('test 2', () => {
describeNumbers.push(2)
})
Expand Down

0 comments on commit 5836160

Please sign in to comment.