Skip to content

Commit

Permalink
Merge branch 'main' into fix-skip-suite-snapshot
Browse files Browse the repository at this point in the history
  • Loading branch information
hi-ogawa committed Dec 28, 2023
2 parents ee72d9d + ee8b46d commit 3413ae2
Show file tree
Hide file tree
Showing 27 changed files with 246 additions and 108 deletions.
1 change: 1 addition & 0 deletions examples/mocks/src/cyclic-deps/module-1.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './module-2'
1 change: 1 addition & 0 deletions examples/mocks/src/cyclic-deps/module-2.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './module-3'
1 change: 1 addition & 0 deletions examples/mocks/src/cyclic-deps/module-3.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './module-4'
1 change: 1 addition & 0 deletions examples/mocks/src/cyclic-deps/module-4.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './module-1'
13 changes: 13 additions & 0 deletions examples/mocks/test/cyclic-import-actual.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { expect, test, vi } from 'vitest'

import '../src/cyclic-deps/module-1'

vi.mock('../src/cyclic-deps/module-2', async () => {
await vi.importActual('../src/cyclic-deps/module-2')

return { default: () => {} }
})

test('some test', () => {
expect(1 + 1).toBe(2)
})
13 changes: 13 additions & 0 deletions examples/mocks/test/cyclic-import-original.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { expect, test, vi } from 'vitest'

import '../src/cyclic-deps/module-1'

vi.mock('../src/cyclic-deps/module-2', async (importOriginal) => {
await importOriginal()

return { default: () => {} }
})

test('some test', () => {
expect(1 + 1).toBe(2)
})
1 change: 1 addition & 0 deletions packages/runner/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export async function runTest(test: Test | Custom, runner: VitestRunner) {
test.mode = 'skip'
test.result = { state: 'skip' }
updateTask(test, runner)
setCurrentTest(undefined)
return
}

Expand Down
8 changes: 4 additions & 4 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"@faker-js/faker": "^8.2.0",
"@iconify-json/logos": "^1.1.41",
"@testing-library/cypress": "^10.0.1",
"@types/codemirror": "^5.60.13",
"@types/codemirror": "^5.60.15",
"@types/d3-force": "^3.0.9",
"@types/d3-selection": "^3.0.10",
"@types/ws": "^8.5.9",
Expand All @@ -73,10 +73,10 @@
"@vueuse/core": "^10.6.1",
"ansi-to-html": "^0.7.2",
"birpc": "0.2.14",
"codemirror": "^5.65.15",
"codemirror": "^5.65.16",
"codemirror-theme-vars": "^0.1.2",
"cypress": "^13.6.0",
"d3-graph-controller": "^3.0.1",
"cypress": "^13.6.2",
"d3-graph-controller": "^3.0.2",
"floating-vue": "^2.0.0-y.0",
"splitpanes": "^3.1.5",
"unocss": "^0.57.4",
Expand Down
5 changes: 2 additions & 3 deletions packages/vitest/src/integrations/env/edge-runtime.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { importModule } from 'local-pkg'
import type { Environment } from '../../types'
import { populateGlobal } from './utils'
import { KEYS } from './jsdom-keys'
Expand All @@ -7,7 +6,7 @@ export default <Environment>({
name: 'edge-runtime',
transformMode: 'ssr',
async setupVM() {
const { EdgeVM } = await importModule('@edge-runtime/vm') as typeof import('@edge-runtime/vm')
const { EdgeVM } = await import('@edge-runtime/vm')
const vm = new EdgeVM({
extend: (context) => {
context.global = context
Expand All @@ -25,7 +24,7 @@ export default <Environment>({
}
},
async setup(global) {
const { EdgeVM } = await importModule('@edge-runtime/vm') as typeof import('@edge-runtime/vm')
const { EdgeVM } = await import('@edge-runtime/vm')
const vm = new EdgeVM({
extend: (context) => {
context.global = context
Expand Down
5 changes: 2 additions & 3 deletions packages/vitest/src/integrations/env/happy-dom.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { importModule } from 'local-pkg'
import type { Environment } from '../../types'
import { populateGlobal } from './utils'

export default <Environment>({
name: 'happy-dom',
transformMode: 'web',
async setupVM({ happyDOM = {} }) {
const { Window } = await importModule('happy-dom') as typeof import('happy-dom')
const { Window } = await import('happy-dom')
const win = new Window({
...happyDOM,
console: (console && globalThis.console) ? globalThis.console : undefined,
Expand Down Expand Up @@ -36,7 +35,7 @@ export default <Environment>({
async setup(global, { happyDOM = {} }) {
// happy-dom v3 introduced a breaking change to Window, but
// provides GlobalWindow as a way to use previous behaviour
const { Window, GlobalWindow } = await importModule('happy-dom') as typeof import('happy-dom')
const { Window, GlobalWindow } = await import('happy-dom')
const win = new (GlobalWindow || Window)({
...happyDOM,
console: (console && global.console) ? global.console : undefined,
Expand Down
5 changes: 2 additions & 3 deletions packages/vitest/src/integrations/env/jsdom.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { importModule } from 'local-pkg'
import type { Environment } from '../../types'
import { populateGlobal } from './utils'

Expand Down Expand Up @@ -35,7 +34,7 @@ export default <Environment>({
JSDOM,
ResourceLoader,
VirtualConsole,
} = await importModule('jsdom') as typeof import('jsdom')
} = await import('jsdom')
const {
html = '<!DOCTYPE html>',
userAgent,
Expand Down Expand Up @@ -107,7 +106,7 @@ export default <Environment>({
JSDOM,
ResourceLoader,
VirtualConsole,
} = await importModule('jsdom') as typeof import('jsdom')
} = await import('jsdom')
const {
html = '<!DOCTYPE html>',
userAgent,
Expand Down
10 changes: 7 additions & 3 deletions packages/vitest/src/integrations/vi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ function createVitest(): VitestUtils {
_mocker.queueMock(
path,
importer,
factory ? () => factory(() => _mocker.importActual(path, importer)) : undefined,
factory ? () => factory(() => _mocker.importActual(path, importer, _mocker.getMockContext().callstack)) : undefined,
)
},

Expand All @@ -495,7 +495,7 @@ function createVitest(): VitestUtils {
_mocker.queueMock(
path,
importer,
factory ? () => factory(() => _mocker.importActual(path, importer)) : undefined,
factory ? () => factory(() => _mocker.importActual(path, importer, _mocker.getMockContext().callstack)) : undefined,
)
},

Expand All @@ -504,7 +504,11 @@ function createVitest(): VitestUtils {
},

async importActual<T = unknown>(path: string): Promise<T> {
return _mocker.importActual<T>(path, getImporter())
return _mocker.importActual<T>(
path,
getImporter(),
_mocker.getMockContext().callstack,
)
},

async importMock<T>(path: string): Promise<MaybeMockedDeep<T>> {
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/node/pools/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function createBrowserPool(ctx: Vitest): ProcessPool {
if (project.config.browser.isolate) {
for (const path of paths) {
if (isCancelled) {
ctx.state.cancelFiles(files.slice(paths.indexOf(path)), ctx.config.root, project.getName())
ctx.state.cancelFiles(files.slice(paths.indexOf(path)), ctx.config.root, project.config.name)
break
}

Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/node/pools/child.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export function createChildProcessPool(ctx: Vitest, { execArgv, env, forksPath }

// Intentionally cancelled
else if (ctx.isCancelling && error instanceof Error && /The task has been cancelled/.test(error.message))
ctx.state.cancelFiles(files, ctx.config.root, project.getName())
ctx.state.cancelFiles(files, ctx.config.root, project.config.name)

else
throw error
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/node/pools/threads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export function createThreadsPool(ctx: Vitest, { execArgv, env, workerPath }: Po

// Intentionally cancelled
else if (ctx.isCancelling && error instanceof Error && /The task has been cancelled/.test(error.message))
ctx.state.cancelFiles(files, ctx.config.root, project.getName())
ctx.state.cancelFiles(files, ctx.config.root, project.config.name)

else
throw error
Expand Down
5 changes: 4 additions & 1 deletion packages/vitest/src/node/pools/vm-threads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export function createVmThreadsPool(ctx: Vitest, { execArgv, env, vmPath }: Pool

// Intentionally cancelled
else if (ctx.isCancelling && error instanceof Error && /The task has been cancelled/.test(error.message))
ctx.state.cancelFiles(files, ctx.config.root, project.getName())
ctx.state.cancelFiles(files, ctx.config.root, project.config.name)

else
throw error
Expand All @@ -123,6 +123,9 @@ export function createVmThreadsPool(ctx: Vitest, { execArgv, env, vmPath }: Pool
}

return async (specs, invalidates) => {
// Cancel pending tasks from pool when possible
ctx.onCancel(() => pool.cancelPendingTasks())

const configs = new Map<WorkspaceProject, ResolvedConfig>()
const getConfig = (project: WorkspaceProject): ResolvedConfig => {
if (configs.has(project))
Expand Down
32 changes: 26 additions & 6 deletions packages/vitest/src/runtime/mocker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ class RefTracker {

type Key = string | symbol

interface MockContext {
/**
* When mocking with a factory, this refers to the module that imported the mock.
*/
callstack: null | string[]
}

function isSpecialProp(prop: Key, parentType: string) {
return parentType.includes('Function')
&& typeof prop === 'string'
Expand All @@ -54,6 +61,10 @@ export class VitestMocker {

private filterPublicKeys: (symbol | string)[]

private mockContext: MockContext = {
callstack: null,
}

constructor(
public executor: VitestExecutor,
) {
Expand Down Expand Up @@ -201,9 +212,9 @@ export class VitestMocker {
throw this.createError(
`[vitest] No "${String(prop)}" export is defined on the "${mockpath}" mock. `
+ 'Did you forget to return it from "vi.mock"?'
+ '\nIf you need to partially mock a module, you can use "vi.importActual" inside:\n\n'
+ `${c.green(`vi.mock("${mockpath}", async () => {
const actual = await vi.importActual("${mockpath}")
+ '\nIf you need to partially mock a module, you can use "importOriginal" helper inside:\n\n'
+ `${c.green(`vi.mock("${mockpath}", async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
Expand All @@ -221,6 +232,10 @@ export class VitestMocker {
return moduleExports
}

public getMockContext() {
return this.mockContext
}

public getMockPath(dep: string) {
return `mock:${dep}`
}
Expand Down Expand Up @@ -407,9 +422,9 @@ export class VitestMocker {
this.deleteCachedItem(id)
}

public async importActual<T>(rawId: string, importee: string): Promise<T> {
const { id, fsPath } = await this.resolvePath(rawId, importee)
const result = await this.executor.cachedRequest(id, fsPath, [importee])
public async importActual<T>(rawId: string, importer: string, callstack?: string[] | null): Promise<T> {
const { id, fsPath } = await this.resolvePath(rawId, importer)
const result = await this.executor.cachedRequest(id, fsPath, callstack || [importer])
return result as T
}

Expand Down Expand Up @@ -453,9 +468,14 @@ export class VitestMocker {
if (typeof mock === 'function' && !callstack.includes(mockPath) && !callstack.includes(url)) {
try {
callstack.push(mockPath)
// this will not work if user does Promise.all(import(), import())
// we can also use AsyncLocalStorage to store callstack, but this won't work in the browser
// maybe we should improve mock API in the future?
this.mockContext.callstack = callstack
return await this.callFunctionMock(mockPath, mock)
}
finally {
this.mockContext.callstack = null
const indexMock = callstack.indexOf(mockPath)
callstack.splice(indexMock, 1)
}
Expand Down
46 changes: 24 additions & 22 deletions packages/vitest/src/runtime/runners/test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { CancelReason, Custom, ExtendedContext, File, Suite, TaskContext, Test, VitestRunner, VitestRunnerImportSource } from '@vitest/runner'
import type { CancelReason, Custom, ExtendedContext, Suite, TaskContext, Test, VitestRunner, VitestRunnerImportSource } from '@vitest/runner'
import type { ExpectStatic } from '@vitest/expect'
import { GLOBAL_EXPECT, getState, setState } from '@vitest/expect'
import { getSnapshotClient } from '../../integrations/snapshot/chai'
Expand Down Expand Up @@ -27,25 +27,23 @@ export class VitestTestRunner implements VitestRunner {
this.snapshotClient.clear()
}

async onAfterRunFiles(files?: File[]) {
// mark snapshots in skipped tests as not obsolete
// TODO: this probably doesn't work when `VitestTestRunner` is handling multiple files concurrently,
// but `snapshotClient.startCurrentRun/finishCurrentRun` doesn't work in that case already?
for (const test of getTests(files ?? [])) {
if (test.mode === 'skip') {
const name = getNames(test).slice(1).join(' > ')
this.snapshotClient.skipTestSnapshots(name)
}
}

const result = await this.snapshotClient.finishCurrentRun()
if (result)
await rpc().snapshotSaved(result)
}

onAfterRunSuite(suite: Suite) {
async onAfterRunSuite(suite: Suite) {
if (this.config.logHeapUsage && typeof process !== 'undefined')
suite.result!.heap = process.memoryUsage().heapUsed

if (suite.mode !== 'skip' && typeof suite.filepath !== 'undefined') {
// mark snapshots in skipped tests as not obsolete
for (const test of getTests(suite)) {
if (test.mode === 'skip') {
const name = getNames(test).slice(1).join(' > ')
this.snapshotClient.skipTestSnapshots(name)
}
}

const result = await this.snapshotClient.finishCurrentRun()
if (result)
await rpc().snapshotSaved(result)
}
}

onAfterRunTask(test: Test) {
Expand All @@ -62,23 +60,27 @@ export class VitestTestRunner implements VitestRunner {
}

async onBeforeRunTask(test: Test) {
const name = getNames(test).slice(1).join(' > ')

if (this.cancelRun)
test.mode = 'skip'

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

clearModuleMocks(this.config)
await this.snapshotClient.startCurrentRun(test.file!.filepath, name, this.workerState.config.snapshotOptions)

this.workerState.current = test
}

onBeforeRunSuite(suite: Suite) {
async onBeforeRunSuite(suite: Suite) {
if (this.cancelRun)
suite.mode = 'skip'

// initialize snapshot state before running file suite
if (suite.mode !== 'skip' && typeof suite.filepath !== 'undefined') {
// default "name" is irrelevant for Vitest since each snapshot assertion
// (e.g. `toMatchSnapshot`) specifies "filepath" / "name" pair explicitly
await this.snapshotClient.startCurrentRun(suite.filepath, '__default_name_', this.workerState.config.snapshotOptions)
}
}

onBeforeTryTask(test: Test) {
Expand Down
Loading

0 comments on commit 3413ae2

Please sign in to comment.