Skip to content

Commit

Permalink
fix: don't panic when file contains invalid syntax in browser mode (#522
Browse files Browse the repository at this point in the history
)

* fix: don't panic when file contains invalid syntax

* fix: always return something

* chore: construct an errors array

* test: add a test

* chore: remove only
  • Loading branch information
sheremet-va authored Oct 31, 2024
1 parent bfcdac7 commit 0b90200
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 16 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
run: |
pnpm -C samples/e2e i
pnpm -C samples/monorepo-vitest-workspace i
pnpm -C samples/browser i
npm -C samples/imba i
pnpm test-e2e --retry 2
Expand Down
66 changes: 55 additions & 11 deletions src/worker/collect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
generateHash,
someTasksAreOnly,
} from '@vitest/runner/utils'
import type { RunnerTestCase, RunnerTestFile, RunnerTestSuite, TaskBase } from 'vitest'
import type { RunnerTestCase, RunnerTestFile, RunnerTestSuite, TaskBase, TestError } from 'vitest'
import type { WorkspaceProject } from 'vitest/node'

interface ParsedFile extends RunnerTestFile {
Expand Down Expand Up @@ -42,7 +42,7 @@ interface LocalCallDefinition {
export interface FileInformation {
file: RunnerTestFile
filepath: string
parsed: string
parsed: string | null
map: SourceMap | { mappings: string } | null
definitions: LocalCallDefinition[]
}
Expand Down Expand Up @@ -164,17 +164,67 @@ export function astParseFile(filepath: string, code: string) {
}
}

export function createFailedFileTask(ctx: WorkspaceProject, filepath: string, error: Error) {
const testFilepath = relative(ctx.config.root, filepath)
const file: ParsedFile = {
filepath,
type: 'suite',
id: /* @__PURE__ */ generateHash(`${testFilepath}${ctx.config.name || ''}`),
name: testFilepath,
mode: 'run',
tasks: [],
start: 0,
end: 0,
projectName: ctx.getName(),
meta: {},
pool: 'browser',
file: null!,
result: {
state: 'fail',
errors: serializeError(ctx, error),
},
}
file.file = file
return file
}

function serializeError(ctx: WorkspaceProject, error: any): TestError[] {
if ('errors' in error && 'pluginCode' in error) {
const errors = error.errors.map((e: any) => {
return {
name: error.name,
message: e.text,
stack: e.location
? `${error.name}: ${e.text}\n at ${relative(ctx.config.root, e.location.file)}:${e.location.line}:${e.location.column}`
: '',
}
})
return errors
}
return [
{
name: error.name,
stack: error.stack,
message: error.message,
},
]
}

export async function astCollectTests(
ctx: WorkspaceProject,
filepath: string,
transformMode: 'web' | 'ssr',
): Promise<null | FileInformation> {
): Promise<ParsedFile> {
const request = await ctx.vitenode.transformRequest(filepath, filepath, transformMode)
// TODO: error cannot parse
const testFilepath = relative(ctx.config.root, filepath)
if (!request) {
debug?.('Cannot parse', testFilepath, '(vite didn\'t return anything)')
return null
return createFailedFileTask(
ctx,
filepath,
new Error(`Failed to parse ${testFilepath}. Vite didn't return anything.`),
)
}
const { definitions, ast } = astParseFile(testFilepath, request.code)
const file: ParsedFile = {
Expand Down Expand Up @@ -303,13 +353,7 @@ export async function astCollectTests(
],
}
}
return {
file,
parsed: request.code,
filepath,
map: request.map,
definitions,
}
return file
}

function createIndexMap(source: string) {
Expand Down
7 changes: 3 additions & 4 deletions src/worker/vitest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { SerializedTestSpecification, VitestMethods } from '../api/rpc'
import { VitestWatcher } from './watcher'
import { VitestCoverage } from './coverage'
import { assert, limitConcurrency } from './utils'
import { astCollectTests } from './collect'
import { astCollectTests, createFailedFileTask } from './collect'

const verbose = process.env.VITEST_VSCODE_LOG === 'verbose'
? (...args: any[]) => {
Expand Down Expand Up @@ -80,10 +80,9 @@ export class Vitest implements VitestMethods {
const runConcurrently = limitConcurrency(5)

const promises = specs.map(([project, filename]) => runConcurrently(
() => astCollectTests(project, filename, transformMode),
() => astCollectTests(project, filename, transformMode).catch(err => createFailedFileTask(project, filename, err)),
))
const result = await Promise.all(promises)
const files = result.filter(r => r != null).map((r => r!.file))
const files = await Promise.all(promises)
this.ctx.configOverride.testNamePattern = new RegExp(Vitest.COLLECT_NAME_PATTERN)
await this.ctx.report('onCollected', files)
this.setTestNamePattern(undefined)
Expand Down
12 changes: 12 additions & 0 deletions test-e2e/assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ expect.extend({
name: 'toHaveState',
}
},
async toHaveError(item: TesterTestItem, error: string) {
const page = item.page
const depth = Number(await item.locator.getAttribute('aria-level'))

await expect(page.locator(`[aria-label*="${error}"][aria-level="${depth + 1}"]`)).toBeVisible()

return {
pass: true,
message: () => '',
}
},
async toHaveTests(item: TesterTestItem, tests: TestsTree) {
const page = item.page
const depth = Number(await item.locator.getAttribute('aria-level'))
Expand Down Expand Up @@ -81,6 +92,7 @@ declare global {
* })
*/
toHaveTests: (tests: TestsTree) => Promise<R>
toHaveError: (error: string) => Promise<R>
}
}
}
23 changes: 22 additions & 1 deletion test-e2e/basic.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { readFileSync } from 'node:fs'
import { readFileSync, rmSync } from 'node:fs'
import { beforeAll, beforeEach, describe, onTestFailed } from 'vitest'
import { expect } from '@playwright/test'
import { test } from './helper'
Expand All @@ -13,6 +13,9 @@ beforeAll(() => {
beforeEach<{ logPath: string }>(({ logPath }) => {
onTestFailed(() => {
console.error(`Log during test:\n${readFileSync(logPath, 'utf-8')}`)
if (!process.env.CI) {
rmSync(logPath)
}
})
})

Expand Down Expand Up @@ -70,6 +73,24 @@ test('custom imba language', async ({ launch }) => {
await expect(tester.tree.getFileItem('counter.imba')).toHaveState('failed')
})

test('browser mode correctly collects tests', async ({ launch }) => {
const { tester } = await launch({
workspacePath: './samples/browser',
})

await tester.tree.expand('test/console.test.ts')
const consoleTest = tester.tree.getFileItem('console.test.ts')
await consoleTest.navigate()

await expect(consoleTest).toHaveTests({
console: 'waiting',
})

editFile('samples/browser/test/console.test.ts', content => `/arakara---\n${content}`)

await expect(consoleTest).toHaveError('Error: Unterminated regular expression')
})

describe('continuous testing', () => {
test('reruns tests on test file change', async ({ launch }) => {
const { tester } = await launch({
Expand Down

0 comments on commit 0b90200

Please sign in to comment.