Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: run tests with correct CWD #408

Merged
merged 2 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ These options are resolved relative to the [workspace file](https://code.visuals
`process.env`
- `vitest.debugExclude`: Excludes files matching specified glob patterns from debugging. Default:
`[\"<node_internals>/**\", \"**/node_modules/**\"]`
- `vitest.maximumConfigs`: The maximum amount of configs that Vitest extension can load. If exceeded, the extension will show a warning suggesting to use a workspace config file. Default: `3`

## FAQs (Frequently Asked Questions)

Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@
"default": false,
"scope": "resource"
},
"vitest.maximumConfigs": {
"description": "The maximum amount of configs that Vitest extension can load. If exceeded, the extension will show a warning suggesting to use a workspace config file.",
"type": "number",
"default": 3,
"scope": "window"
},
"vitest.debugExclude": {
"markdownDescription": "Automatically skip files covered by these glob patterns.",
"type": "array",
Expand Down
99 changes: 62 additions & 37 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,33 @@ export class VitestReporter {

export class VitestAPI {
private disposing = false
private _disposes: (() => void)[] = []

constructor(
private readonly api: VitestFolderAPI[],
private readonly meta: ResolvedMeta,
) {
meta.process.on('error', (error) => {
if (!this.disposing)
showVitestError('Vitest process failed', error)
this.processes.forEach((process) => {
const warn = (error: any) => {
if (!this.disposing)
showVitestError('Vitest process failed', error)
}
process.on('error', warn)
this._disposes.push(() => {
process.off('error', warn)
})
})
}

onUnexpectedExit(callback: (code: number | null) => void) {
this.meta.process.on('exit', (code) => {
if (!this.disposing)
callback(code)
this.processes.forEach((process) => {
const onExit = (code: number | null) => {
if (!this.disposing)
callback(code)
}
process.on('exit', onExit)
this._disposes.push(() => {
process.off('exit', onExit)
})
})
}

Expand All @@ -75,29 +87,17 @@ export class VitestAPI {
async dispose() {
this.disposing = true
try {
this.forEach(api => api.dispose())
this.meta.packages.forEach((pkg) => {
delete require.cache[pkg.vitestPackageJsonPath]
})
if (!this.meta.process.closed) {
try {
await this.meta.rpc.close()
log.info('[API]', `Vitest process ${this.meta.process.id} closed successfully`)
}
catch (err) {
log.error('[API]', 'Failed to close Vitest process', err)
}
const promise = new Promise<void>((resolve) => {
this.meta.process.once('exit', () => resolve())
})
this.meta.process.close()
await promise
}
this._disposes.forEach(dispose => dispose())
await Promise.all(this.api.map(api => api.dispose()))
}
finally {
this.disposing = false
}
}

private get processes() {
return this.api.map(api => api.process)
}
}

const WEAKMAP_API_FOLDER = new WeakMap<VitestFolderAPI, vscode.WorkspaceFolder>()
Expand All @@ -116,13 +116,17 @@ export class VitestFolderAPI extends VitestReporter {
}

get processId() {
return this.meta.process.id
return this.process.id
}

get prefix() {
return this.pkg.prefix
}

get process() {
return this.meta.process
}

get version() {
return this.pkg.version
}
Expand Down Expand Up @@ -170,9 +174,26 @@ export class VitestFolderAPI extends VitestReporter {
await this.collectPromise
}

dispose() {
async dispose() {
WEAKMAP_API_FOLDER.delete(this)
this.handlers.clearListeners()
this.meta.packages.forEach((pkg) => {
delete require.cache[pkg.vitestPackageJsonPath]
})
if (!this.meta.process.closed) {
try {
await this.meta.rpc.close()
log.info('[API]', `Vitest process ${this.meta.process.id} closed successfully`)
}
catch (err) {
log.error('[API]', 'Failed to close Vitest process', err)
}
const promise = new Promise<void>((resolve) => {
this.meta.process.once('exit', () => resolve())
})
this.meta.process.close()
await promise
}
}

async cancelRun() {
Expand Down Expand Up @@ -201,11 +222,12 @@ export class VitestFolderAPI extends VitestReporter {
}

export async function resolveVitestAPI(showWarning: boolean, packages: VitestPackage[]) {
const vitest = await createVitestProcess(showWarning, packages)
const apis = packages.map(pkg =>
new VitestFolderAPI(pkg, vitest),
)
return new VitestAPI(apis, vitest)
const promises = packages.map(async (pkg) => {
const vitest = await createVitestProcess(showWarning, [pkg])
return new VitestFolderAPI(pkg, vitest)
})
const apis = await Promise.all(promises)
return new VitestAPI(apis)
}

export interface ResolvedMeta {
Expand Down Expand Up @@ -243,7 +265,7 @@ async function createChildVitestProcess(showWarning: boolean, meta: VitestPackag
: undefined
const env = getConfig().env || {}
const execPath = getConfig().nodeExecutable || await findNode(vscode.workspace.workspaceFile?.fsPath || vscode.workspace.workspaceFolders![0].uri.fsPath)
log.info('[API]', `Starting Vitest process with Node.js: ${execPath}`)
log.info('[API]', `Running Vitest: ${meta.map(x => `v${x.version} (${relative(dirname(x.cwd), x.id)})`).join(', ')} with Node.js: ${execPath}`)
const vitest = fork(
workerPath,
{
Expand All @@ -260,7 +282,7 @@ async function createChildVitestProcess(showWarning: boolean, meta: VitestPackag
NODE_ENV: env.NODE_ENV ?? process.env.NODE_ENV ?? 'test',
},
stdio: 'overlapped',
cwd: pnp ? dirname(pnp) : undefined,
cwd: pnp ? dirname(pnp) : meta[0].cwd,
},
)

Expand Down Expand Up @@ -340,12 +362,11 @@ async function createChildVitestProcess(showWarning: boolean, meta: VitestPackag
})
}

// TODO: packages should be a single package
export async function createVitestProcess(showWarning: boolean, packages: VitestPackage[]): Promise<ResolvedMeta> {
log.info('[API]', `Running Vitest: ${packages.map(x => `v${x.version} (${relative(x.cwd, x.id)})`).join(', ')}`)

const vitest = await createChildVitestProcess(showWarning, packages)

log.info('[API]', `Vitest process ${vitest.pid} created`)
log.info('[API]', `Vitest ${packages.map(x => `v${x.version} (${relative(dirname(x.cwd), x.id)})`).join(', ')} process ${vitest.pid} created`)

const { handlers, api } = createVitestRpc({
on: listener => vitest.on('message', listener),
Expand Down Expand Up @@ -379,6 +400,10 @@ class VitestChildProvess implements VitestProcess {
this.child.once(event, listener)
}

off(event: string, listener: (...args: any[]) => void) {
this.child.off(event, listener)
}

close() {
this.child.kill()
}
Expand Down
1 change: 1 addition & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) {
workspaceConfig: resolvePath(workspaceConfig),
rootConfig: resolvePath(rootConfigFile),
configSearchPatternExclude,
maximumConfigs: get<number>('maximumConfigs', 3),
nodeExecutable: resolvePath(nodeExecutable),
disableWorkspaceWarning: get<boolean>('disableWorkspaceWarning', false),
debuggerPort: get<number>('debuggerPort') || undefined,
Expand Down
4 changes: 4 additions & 0 deletions src/debug/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,8 @@ class VitestWebSocketProcess implements VitestProcess {
once(event: string, listener: (...args: any[]) => void) {
this.ws.once(event, listener)
}

off(event: string, listener: (...args: any[]) => void) {
this.ws.off(event, listener)
}
}
46 changes: 28 additions & 18 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,24 +65,34 @@ class VitestExtension {

const configFiles = vitest.filter(x => x.configFile && !x.workspaceFile)

// TODO: hard limit on the number of config files

if (configFiles.length > 3 && configFiles.every(c => getConfig(c.folder).disableWorkspaceWarning !== true)) {
vscode.window.showWarningMessage(
`Vitest found ${configFiles.length} config files. For better performance, consider using a workspace configuration.`,
'Create vitest.workspace.js',
'Disable notification',
).then((result) => {
if (result === 'Create vitest.workspace.js')
createVitestWorkspaceFile(configFiles).catch(noop)

if (result === 'Disable notification') {
configFiles.forEach((c) => {
const rootConfig = vscode.workspace.getConfiguration('vitest', c.folder)
rootConfig.update('disableWorkspaceWarning', true)
})
}
})
const maximumConfigs = getConfig().maximumConfigs ?? 3

if (configFiles.length > maximumConfigs) {
const warningMessage = `Vitest found multiple config files. The extension will use only the first ${maximumConfigs} due to performance concerns. Consider using a workspace configuration to group your configs.`
// remove all but the first 3
const discardedConfigs = configFiles.splice(maximumConfigs)

if (configFiles.every(c => getConfig(c.folder).disableWorkspaceWarning !== true)) {
vscode.window.showWarningMessage(
warningMessage,
'Create vitest.workspace.js',
'Disable notification',
).then((result) => {
if (result === 'Create vitest.workspace.js')
createVitestWorkspaceFile(configFiles).catch(noop)

if (result === 'Disable notification') {
configFiles.forEach((c) => {
const rootConfig = vscode.workspace.getConfiguration('vitest', c.folder)
rootConfig.update('disableWorkspaceWarning', true)
})
}
})
}
else {
log.info(warningMessage)
log.info(`Discarded config files: ${discardedConfigs.map(x => x.workspaceFile || x.configFile).join(', ')}`)
}
}

const folders = new Set(vitest.map(x => x.folder))
Expand Down
1 change: 1 addition & 0 deletions src/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ export interface VitestProcess {
id: number
closed: boolean
on: (event: string, listener: (...args: any[]) => void) => void
off: (event: string, listener: (...args: any[]) => void) => void
once: (event: string, listener: (...args: any[]) => void) => void
}
Loading