Skip to content

Commit

Permalink
fix(browser): init browsers eagerly when tests are running (#6876)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va authored Nov 13, 2024
1 parent 32be0af commit 417bdb4
Show file tree
Hide file tree
Showing 13 changed files with 83 additions and 65 deletions.
2 changes: 2 additions & 0 deletions docs/guide/browser/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ export default defineConfig({

::: info
Vitest assigns port `63315` to avoid conflicts with the development server, allowing you to run both in parallel. You can change that with the [`browser.api`](/config/#browser-api) option.

Since Vitest 2.1.5, CLI no longer prints the Vite URL automcatically. You can press "b" to print the URL when running in watch mode.
:::

If you have not used Vite before, make sure you have your framework's plugin installed and specified in the config. Some frameworks might require extra configuration to work - check their Vite related documentation to be sure.
Expand Down
1 change: 1 addition & 0 deletions packages/browser/src/node/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export function createBrowserPool(ctx: Vitest): ProcessPool {
if (isCancelled) {
break
}
await project.initBrowserProvider()

await executeTests(method, project, files)
}
Expand Down
15 changes: 8 additions & 7 deletions packages/browser/src/node/providers/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { BrowserProvider, WorkspaceProject } from 'vitest/node'
export class PreviewBrowserProvider implements BrowserProvider {
public name = 'preview' as const
public supportsParallelism: boolean = false
private ctx!: WorkspaceProject
private project!: WorkspaceProject
private open = false

getSupportedBrowsers() {
Expand All @@ -19,25 +19,26 @@ export class PreviewBrowserProvider implements BrowserProvider {
return {}
}

async initialize(ctx: WorkspaceProject) {
this.ctx = ctx
async initialize(project: WorkspaceProject) {
this.project = project
this.open = false
if (ctx.config.browser.headless) {
if (project.config.browser.headless) {
throw new Error(
'You\'ve enabled headless mode for "preview" provider but it doesn\'t support it. Use "playwright" or "webdriverio" instead: https://vitest.dev/guide/browser/#configuration',
)
}
project.ctx.logger.printBrowserBanner(project)
}

async openPage(_contextId: string, url: string) {
this.open = true
if (!this.ctx.browser) {
if (!this.project.browser) {
throw new Error('Browser is not initialized')
}
const options = this.ctx.browser.vite.config.server
const options = this.project.browser.vite.config.server
const _open = options.open
options.open = url
this.ctx.browser.vite.openBrowser()
this.project.browser.vite.openBrowser()
options.open = _open
}

Expand Down
12 changes: 4 additions & 8 deletions packages/vitest/src/node/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,6 @@ export class Vitest {
return this.coverageProvider
}

private async initBrowserProviders() {
return Promise.all(this.projects.map(w => w.initBrowserProvider()))
}

async mergeReports() {
if (this.reporters.some(r => r instanceof BlobReporter)) {
throw new Error('Cannot merge reports when `--reporter=blob` is used. Remove blob reporter from the config first.')
Expand Down Expand Up @@ -369,8 +365,6 @@ export class Vitest {
async collect(filters?: string[]) {
this._onClose = []

await this.initBrowserProviders()

const files = await this.filterTestsBySource(
await this.globTestFiles(filters),
)
Expand Down Expand Up @@ -402,7 +396,6 @@ export class Vitest {
try {
await this.initCoverageProvider()
await this.coverageProvider?.clean(this.config.coverage.clean)
await this.initBrowserProviders()
}
finally {
await this.report('onInit', this)
Expand Down Expand Up @@ -445,7 +438,6 @@ export class Vitest {
try {
await this.initCoverageProvider()
await this.coverageProvider?.clean(this.config.coverage.clean)
await this.initBrowserProviders()
}
finally {
await this.report('onInit', this)
Expand Down Expand Up @@ -693,6 +685,10 @@ export class Vitest {
await Promise.all(this._onCancelListeners.splice(0).map(listener => listener(reason)))
}

async initBrowserServers() {
await Promise.all(this.projects.map(p => p.initBrowserServer()))
}

async rerunFiles(files: string[] = this.state.getFilepaths(), trigger?: string, allTestsRun = true) {
if (this.filenamePattern) {
const filteredFiles = await this.globTestFiles([this.filenamePattern])
Expand Down
41 changes: 25 additions & 16 deletions packages/vitest/src/node/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { createLogUpdate } from 'log-update'
import c from 'tinyrainbow'
import { highlightCode } from '../utils/colors'
import { printError } from './error'
import { divider, withLabel } from './reporters/renderers/utils'
import { divider, formatProjectName, withLabel } from './reporters/renderers/utils'
import { RandomSequencer } from './sequencers/RandomSequencer'

export interface ErrorOptions {
Expand Down Expand Up @@ -217,21 +217,6 @@ export class Logger {
this.log(PAD + c.gray(`Running tests with seed "${this.ctx.config.sequence.seed}"`))
}

this.ctx.projects.forEach((project) => {
if (!project.browser) {
return
}
const name = project.getName()
const output = project.isCore() ? '' : ` [${name}]`

const resolvedUrls = project.browser.vite.resolvedUrls
const origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0]
const provider = project.browser.provider.name
const providerString = provider === 'preview' ? '' : ` by ${provider}`

this.log(PAD + c.dim(c.green(`${output} Browser runner started${providerString} at ${new URL('/', origin)}`)))
})

if (this.ctx.config.ui) {
const host = this.ctx.config.api?.host || 'localhost'
const port = this.ctx.server.config.server.port
Expand Down Expand Up @@ -260,6 +245,30 @@ export class Logger {
}
}

printBrowserBanner(project: WorkspaceProject) {
if (!project.browser) {
return
}

const resolvedUrls = project.browser.vite.resolvedUrls
const origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0]
if (!origin) {
return
}

const name = project.getName()
const output = project.isCore()
? ''
: formatProjectName(name)
const provider = project.browser.provider.name
const providerString = provider === 'preview' ? '' : ` by ${c.reset(c.bold(provider))}`
this.log(
c.dim(
`${output}Browser runner started${providerString} ${c.dim('at')} ${c.blue(new URL('/', origin))}\n`,
),
)
}

printUnhandledErrors(errors: unknown[]) {
const errorMessage = c.red(
c.bold(
Expand Down
9 changes: 9 additions & 0 deletions packages/vitest/src/node/stdin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const keys = [
['p', 'filter by a filename'],
['t', 'filter by a test name regex pattern'],
['w', 'filter by a project name'],
['b', 'start the browser server if not started yet'],
['q', 'quit'],
]
const cancelKeys = ['space', 'c', 'h', ...keys.map(key => key[0]).flat()]
Expand Down Expand Up @@ -120,6 +121,14 @@ export function registerConsoleShortcuts(
if (name === 'p') {
return inputFilePattern()
}
if (name === 'b') {
await ctx.initBrowserServers()
ctx.projects.forEach((project) => {
ctx.logger.log()
ctx.logger.printBrowserBanner(project)
})
return null
}
}

async function keypressHandler(str: string, key: any) {
Expand Down
18 changes: 8 additions & 10 deletions packages/vitest/src/node/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,16 +358,15 @@ export class WorkspaceProject {
return testFiles
}

async initBrowserServer(configFile: string | undefined) {
if (!this.isBrowserEnabled()) {
async initBrowserServer() {
if (!this.isBrowserEnabled() || this.browser) {
return
}
await this.ctx.packageInstaller.ensureInstalled('@vitest/browser', this.config.root, this.ctx.version)
const { createBrowserServer, distRoot } = await import('@vitest/browser')
await this.browser?.close()
const browser = await createBrowserServer(
this,
configFile,
this.server.config.configFile,
[
...MocksPlugins({
filter(id) {
Expand Down Expand Up @@ -408,9 +407,7 @@ export class WorkspaceProject {
}

static async createCoreProject(ctx: Vitest) {
const project = WorkspaceProject.createBasicProject(ctx)
await project.initBrowserServer(ctx.server.config.configFile)
return project
return WorkspaceProject.createBasicProject(ctx)
}

async setServer(options: UserConfig, server: ViteDevServer) {
Expand Down Expand Up @@ -449,8 +446,6 @@ export class WorkspaceProject {
return node.resolveId(id, importer)
},
})

await this.initBrowserServer(this.server.config.configFile)
}

isBrowserEnabled(): boolean {
Expand Down Expand Up @@ -495,9 +490,12 @@ export class WorkspaceProject {
}

async initBrowserProvider() {
if (!this.isBrowserEnabled()) {
if (!this.isBrowserEnabled() || this.browser?.provider) {
return
}
if (!this.browser) {
await this.initBrowserServer()
}
await this.browser?.initBrowserProvider()
}
}
5 changes: 1 addition & 4 deletions test/browser/specs/runner.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { readFile } from 'node:fs/promises'
import { beforeAll, describe, expect, onTestFailed, test } from 'vitest'
import { defaultBrowserPort } from 'vitest/config'
import { browser, provider, runBrowserTests } from './utils'
import { browser, runBrowserTests } from './utils'

describe('running browser tests', async () => {
let stderr: string
Expand Down Expand Up @@ -29,8 +28,6 @@ describe('running browser tests', async () => {
console.error(stderr)
})

expect(stdout).toContain(`Browser runner started by ${provider} at http://localhost:${defaultBrowserPort}/`)

expect(browserResultJson.testResults).toHaveLength(19)
expect(passedTests).toHaveLength(17)
expect(failedTests).toHaveLength(2)
Expand Down
14 changes: 9 additions & 5 deletions test/browser/specs/server-url.test.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import { afterEach, expect, test } from 'vitest'
import { provider, runBrowserTests } from './utils'
import { runBrowserTests } from './utils'

afterEach(() => {
delete process.env.TEST_HTTPS
})

test('server-url http', async () => {
const { stdout, stderr } = await runBrowserTests({
const { stderr, ctx } = await runBrowserTests({
root: './fixtures/server-url',
watch: true, // otherwise the browser is closed before we can get the url
})
const url = ctx?.projects[0].browser?.vite.resolvedUrls?.local[0]
expect(stderr).toBe('')
expect(stdout).toContain(`Browser runner started by ${provider} at http://localhost:51133/`)
expect(url).toBe('http://localhost:51133/')
})

test('server-url https', async () => {
process.env.TEST_HTTPS = '1'
const { stdout, stderr } = await runBrowserTests({
const { stdout, stderr, ctx } = await runBrowserTests({
root: './fixtures/server-url',
watch: true, // otherwise the browser is closed before we can get the url
})
expect(stderr).toBe('')
expect(stdout).toContain(`Browser runner started by ${provider} at https://localhost:51122/`)
const url = ctx?.projects[0].browser?.vite.resolvedUrls?.local[0]
expect(url).toBe('https://localhost:51122/')
expect(stdout).toContain('Test Files 1 passed')
})
3 changes: 3 additions & 0 deletions test/cli/fixtures/browser-multiple/basic.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { test } from 'vitest';

test('passes')
6 changes: 4 additions & 2 deletions test/cli/fixtures/browser-multiple/vitest.workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export default defineWorkspace([
cacheDir: resolve(import.meta.dirname, 'basic-1'),
test: {
name: 'basic-1',
include: ['none'],
dir: import.meta.dirname,
include: ['./basic.test.js'],
browser: {
enabled: true,
name: 'chromium',
Expand All @@ -19,7 +20,8 @@ export default defineWorkspace([
cacheDir: resolve(import.meta.dirname, 'basic-2'),
test: {
name: 'basic-2',
include: ['none'],
dir: import.meta.dirname,
include: ['./basic.test.js'],
browser: {
enabled: true,
name: 'chromium',
Expand Down
9 changes: 5 additions & 4 deletions test/cli/test/browser-multiple.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ it('automatically assigns the port', async () => {
const workspace = resolve(import.meta.dirname, '../fixtures/browser-multiple/vitest.workspace.ts')
const spy = vi.spyOn(console, 'log')
onTestFinished(() => spy.mockRestore())
const { stderr, stdout } = await runVitest({
const { stderr, ctx } = await runVitest({
root,
workspace,
dir: root,
watch: false,
watch: true,
})
const urls = ctx?.projects.map(p => p.browser?.vite.resolvedUrls?.local[0])

expect(spy).not.toHaveBeenCalled()
expect(stderr).not.toContain('is in use, trying another one...')
expect(stdout).toContain('Browser runner started by playwright at http://localhost:63315/')
expect(stdout).toContain('Browser runner started by playwright at http://localhost:63316/')
expect(urls).toContain('http://localhost:63315/')
expect(urls).toContain('http://localhost:63316/')
})
13 changes: 4 additions & 9 deletions test/config/test/browser-html.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,20 @@ import { runVitest } from '../../test-utils'
const root = resolve(import.meta.dirname, '../fixtures/browser-custom-html')

test('throws an error with non-existing path', async () => {
const { stderr, thrown } = await runVitest({
const { stderr } = await runVitest({
root,
config: './vitest.config.non-existing.ts',
}, [], 'test', {}, { fails: true })
expect(thrown).toBe(true)
expect(stderr).toContain(`Tester HTML file "${resolve(root, './some-non-existing-path')}" doesn't exist.`)
})

test('throws an error and exits if there is an error in the html file hook', async () => {
const { stderr, stdout, exitCode } = await runVitest({
const { stderr, exitCode } = await runVitest({
root,
config: './vitest.config.error-hook.ts',
})
expect(stderr).toContain('expected error in transformIndexHtml')
// error happens when browser is opened
expect(stdout).toContain('Browser runner started by playwright')
expect(stderr).toContain('Error: expected error in transformIndexHtml')
expect(stderr).toContain('[vite] Internal server error: expected error in transformIndexHtml')
expect(exitCode).toBe(1)
})

Expand All @@ -31,7 +29,6 @@ test('allows correct custom html', async () => {
reporters: ['basic'],
})
expect(stderr).toBe('')
expect(stdout).toContain('Browser runner started by playwright')
expect(stdout).toContain('✓ browser-basic.test.ts')
expect(exitCode).toBe(0)
})
Expand All @@ -43,7 +40,6 @@ test('allows custom transformIndexHtml with custom html file', async () => {
reporters: ['basic'],
})
expect(stderr).toBe('')
expect(stdout).toContain('Browser runner started by playwright')
expect(stdout).toContain('✓ browser-custom.test.ts')
expect(exitCode).toBe(0)
})
Expand All @@ -55,7 +51,6 @@ test('allows custom transformIndexHtml without custom html file', async () => {
reporters: ['basic'],
})
expect(stderr).toBe('')
expect(stdout).toContain('Browser runner started by playwright')
expect(stdout).toContain('✓ browser-custom.test.ts')
expect(exitCode).toBe(0)
})

0 comments on commit 417bdb4

Please sign in to comment.