mirror of
https://github.com/vitest-dev/vitest.git
synced 2026-02-01 17:36:51 +00:00
fix(browser): init browsers eagerly when tests are running (#6876)
This commit is contained in:
parent
32be0af753
commit
417bdb423d
@ -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.
|
||||
|
||||
@ -131,6 +131,7 @@ export function createBrowserPool(ctx: Vitest): ProcessPool {
|
||||
if (isCancelled) {
|
||||
break
|
||||
}
|
||||
await project.initBrowserProvider()
|
||||
|
||||
await executeTests(method, project, files)
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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.')
|
||||
@ -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),
|
||||
)
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -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])
|
||||
|
||||
@ -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 {
|
||||
@ -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
|
||||
@ -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(
|
||||
|
||||
@ -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()]
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
@ -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) {
|
||||
@ -449,8 +446,6 @@ export class WorkspaceProject {
|
||||
return node.resolveId(id, importer)
|
||||
},
|
||||
})
|
||||
|
||||
await this.initBrowserServer(this.server.config.configFile)
|
||||
}
|
||||
|
||||
isBrowserEnabled(): boolean {
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -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)
|
||||
|
||||
@ -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
test/cli/fixtures/browser-multiple/basic.test.js
Normal file
3
test/cli/fixtures/browser-multiple/basic.test.js
Normal file
@ -0,0 +1,3 @@
|
||||
import { test } from 'vitest';
|
||||
|
||||
test('passes')
|
||||
@ -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',
|
||||
@ -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',
|
||||
|
||||
@ -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/')
|
||||
})
|
||||
|
||||
@ -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)
|
||||
})
|
||||
|
||||
@ -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)
|
||||
})
|
||||
@ -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)
|
||||
})
|
||||
@ -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)
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user