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(browser): init browsers eagerly when tests are running #6876

Merged
merged 6 commits into from
Nov 13, 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
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)
})