Skip to content

Commit

Permalink
refactor(browser): move the browser server into its own entity (#5924)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va authored Jun 19, 2024
1 parent 98f9b7a commit 67d6add
Show file tree
Hide file tree
Showing 59 changed files with 1,315 additions and 1,266 deletions.
4 changes: 3 additions & 1 deletion packages/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"types": "./dist/index.d.ts",
"files": [
"*.d.ts",
"context.js",
"dist",
"providers"
],
Expand Down Expand Up @@ -74,7 +75,8 @@
"@vitest/utils": "workspace:*",
"magic-string": "^0.30.10",
"msw": "^2.3.1",
"sirv": "^2.0.4"
"sirv": "^2.0.4",
"ws": "^8.17.1"
},
"devDependencies": {
"@types/ws": "^8.5.10",
Expand Down
21 changes: 19 additions & 2 deletions packages/browser/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const external = [
/^@?vitest(\/|$)/,
'worker_threads',
'node:worker_threads',
'vite',
]

const plugins = [
Expand Down Expand Up @@ -45,7 +46,7 @@ export default () =>
plugins,
},
{
input: './src/client/context.ts',
input: './src/client/tester/context.ts',
output: {
file: 'dist/context.js',
format: 'esm',
Expand All @@ -56,13 +57,29 @@ export default () =>
}),
],
},
{
input: './src/client/tester/state.ts',
output: {
file: 'dist/state.js',
format: 'esm',
},
plugins: [
esbuild({
target: 'node18',
minifyWhitespace: true,
}),
resolve(),
],
},
{
input: input.index,
output: {
file: 'dist/index.d.ts',
format: 'esm',
},
external,
plugins: [dts()],
plugins: [dts({
respectExternal: true,
})],
},
])
15 changes: 1 addition & 14 deletions packages/browser/src/client/client.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { CancelReason } from '@vitest/runner'
import { type BirpcReturn, createBirpc } from 'birpc'
import type { WebSocketBrowserEvents, WebSocketBrowserHandlers } from 'vitest'
import { parse, stringify } from 'flatted'
import type { VitestBrowserClientMocker } from './mocker'
import type { WebSocketBrowserEvents, WebSocketBrowserHandlers } from '../node/types'
import { getBrowserState } from './utils'

const PAGE_TYPE = getBrowserState().type
Expand Down Expand Up @@ -51,18 +50,6 @@ function createClient() {
ctx.rpc = createBirpc<WebSocketBrowserHandlers, WebSocketBrowserEvents>(
{
onCancel: setCancel,
async startMocking(id: string) {
// @ts-expect-error not typed global
if (typeof __vitest_mocker__ === 'undefined') {
throw new TypeError(
`Cannot mock modules in the orchestrator process`,
)
}
// @ts-expect-error not typed global
const mocker = __vitest_mocker__ as VitestBrowserClientMocker
const exports = await mocker.resolve(id)
return Object.keys(exports)
},
async createTesters(files: string[]) {
if (PAGE_TYPE !== 'orchestrator') {
return
Expand Down
4 changes: 2 additions & 2 deletions packages/browser/src/client/orchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import type { ResolvedConfig } from 'vitest'
import { generateHash } from '@vitest/runner/utils'
import { relative } from 'pathe'
import { channel, client } from './client'
import { rpcDone } from './rpc'
import { rpcDone } from './tester/rpc'
import { getBrowserState, getConfig } from './utils'
import { getUiAPI } from './ui'
import type { IframeChannelEvent, IframeChannelIncomingEvent } from './channel'
import { createModuleMocker } from './msw'
import { createModuleMocker } from './tester/msw'

const url = new URL(location.href)

Expand Down
1 change: 1 addition & 0 deletions packages/browser/src/client/public/esm-client-injector.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ window.__vitest_browser_runner__ = {
type: { __VITEST_TYPE__ },
contextId: { __VITEST_CONTEXT_ID__ },
provider: { __VITEST_PROVIDER__ },
providedContext: { __VITEST_PROVIDED_CONTEXT__ },
};

const config = __vitest_browser_runner__.config;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Task, WorkerGlobalState } from 'vitest'
import type { BrowserPage, UserEvent, UserEventClickOptions, UserEventTabOptions, UserEventTypeOptions } from '../../context'
import type { BrowserRPC } from './client'
import type { BrowserRunnerState } from './utils'
import type { BrowserPage, UserEvent, UserEventClickOptions, UserEventTabOptions, UserEventTypeOptions } from '../../../context'
import type { BrowserRunnerState } from '../utils'
import type { BrowserRPC } from '../client'

// this file should not import anything directly, only types

Expand Down Expand Up @@ -143,7 +143,9 @@ function getWebdriverioSelectOptions(element: Element, value: string | string[]
return [{ index: valueIndex }]
}

const labelIndex = options.findIndex(option => option.textContent?.trim() === optionValue || option.ariaLabel === optionValue)
const labelIndex = options.findIndex(option =>
option.textContent?.trim() === optionValue || option.ariaLabel === optionValue,
)

if (labelIndex === -1) {
throw new Error(`The option "${optionValue}" was not found in the "select" options.`)
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { format, inspect, stringify } from 'vitest/utils'
import { getConfig } from '../utils'
import { rpc } from './rpc'
import { getConfig, importId } from './utils'

const { Date, console } = globalThis

export async function setupConsoleLogSpy() {
const { stringify, format, inspect } = (await importId(
'vitest/utils',
)) as typeof import('vitest/utils')
export function setupConsoleLogSpy() {
const {
log,
info,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { getType } from '@vitest/utils'
import { extname, join } from 'pathe'
import { getBrowserState, importId } from '../utils'
import type { IframeChannelOutgoingEvent } from '../channel'
import { channel, waitForChannel } from '../client'
import { rpc } from './rpc'
import { getBrowserState, importId } from './utils'
import { channel, waitForChannel } from './client'
import type { IframeChannelOutgoingEvent } from './channel'

const now = Date.now

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import type {
IframeMockEvent,
IframeMockingDoneEvent,
IframeUnmockEvent,
} from './channel'
import { channel } from './channel'
import { client } from './client'
} from '../channel'
import { channel } from '../channel'

export function createModuleMocker() {
const mocks: Map<string, string | null | undefined> = new Map()
Expand All @@ -23,7 +22,6 @@ export function createModuleMocker() {

// using a factory
if (mock === undefined) {
// TODO: check how the error looks
const exports = await getFactoryExports(path)
const module = `const module = __vitest_mocker__.get('${path}');`
const keys = exports
Expand All @@ -46,12 +44,7 @@ export function createModuleMocker() {
return Response.redirect(mock)
}

const content = await client.rpc.automock(path)
return new Response(content, {
headers: {
'Content-Type': 'application/javascript',
},
})
return Response.redirect(injectQuery(path, 'mock=auto'))
}),
)

Expand All @@ -68,7 +61,7 @@ export function createModuleMocker() {
startPromise = worker
.start({
serviceWorker: {
url: '/__virtual_vitest__:mocker-worker.js',
url: '/__vitest_msw__',
},
quiet: true,
})
Expand Down Expand Up @@ -133,3 +126,23 @@ function passthrough() {
},
})
}

const postfixRE = /[?#].*$/
function cleanUrl(url: string): string {
return url.replace(postfixRE, '')
}

const replacePercentageRE = /%/g
function injectQuery(url: string, queryToInject: string): string {
// encode percents for consistent behavior with pathToFileURL
// see #2614 for details
const resolvedUrl = new URL(
url.replace(replacePercentageRE, '%25'),
location.href,
)
const { search, hash } = resolvedUrl
const pathname = cleanUrl(url)
return `${pathname}?${queryToInject}${search ? `&${search.slice(1)}` : ''}${
hash ?? ''
}`
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { getSafeTimers } from '@vitest/utils'
import { importId } from './utils'
import type { VitestBrowserClient } from './client'
import { getSafeTimers } from 'vitest/utils'
import type { VitestBrowserClient } from '../client'

const { get } = Reflect

Expand Down Expand Up @@ -42,7 +41,6 @@ export async function rpcDone() {

export function createSafeRpc(
client: VitestBrowserClient,
getTimers: () => any,
): VitestBrowserClient['rpc'] {
return new Proxy(client.rpc, {
get(target, p, handler) {
Expand All @@ -51,7 +49,7 @@ export function createSafeRpc(
}
const sendCall = get(target, p, handler)
const safeSendCall = (...args: any[]) =>
withSafeTimers(getTimers, async () => {
withSafeTimers(getSafeTimers, async () => {
const result = sendCall(...args)
promises.add(result)
try {
Expand All @@ -67,14 +65,6 @@ export function createSafeRpc(
})
}

export async function loadSafeRpc(client: VitestBrowserClient) {
// if importing /@id/ failed, we reload the page waiting until Vite prebundles it
const { getSafeTimers } = (await importId(
'vitest/utils',
)) as typeof import('vitest/utils')
return createSafeRpc(client, getSafeTimers)
}

export function rpc(): VitestBrowserClient['rpc'] {
// @ts-expect-error not typed global
return globalThis.__vitest_worker__.rpc
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { File, Task, TaskResultPack, VitestRunner } from '@vitest/runner'
import type { ResolvedConfig, WorkerGlobalState } from 'vitest'
import type { VitestExecutor } from 'vitest/execute'
import { rpc } from './rpc'
import { importId } from './utils'
import { importId } from '../utils'
import { VitestBrowserSnapshotEnvironment } from './snapshot'
import { rpc } from './rpc'
import type { VitestBrowserClientMocker } from './mocker'

interface BrowserRunnerOptions {
Expand Down Expand Up @@ -126,8 +126,7 @@ export async function initiateRunner(
takeCoverageInsideWorker(config.coverage, { executeId: importId }),
})
if (!config.snapshotOptions.snapshotEnvironment) {
config.snapshotOptions.snapshotEnvironment
= new VitestBrowserSnapshotEnvironment()
config.snapshotOptions.snapshotEnvironment = new VitestBrowserSnapshotEnvironment()
}
const runner = new BrowserRunner({
config,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { SnapshotEnvironment } from 'vitest/snapshot'
import type { VitestBrowserClient } from './client'
import type { VitestBrowserClient } from '../client'

export class VitestBrowserSnapshotEnvironment implements SnapshotEnvironment {
getVersion(): string {
Expand Down
46 changes: 46 additions & 0 deletions packages/browser/src/client/tester/state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { WorkerGlobalState } from 'vitest'
import { parse } from 'flatted'
import { getBrowserState } from '../utils'

const config = getBrowserState().config

const providedContext = parse(getBrowserState().providedContext)

const state: WorkerGlobalState = {
ctx: {
pool: 'browser',
worker: './browser.js',
workerId: 1,
config,
projectName: config.name || '',
files: [],
environment: {
name: 'browser',
options: null,
},
providedContext,
invalidates: [],
},
onCancel: null as any,
mockMap: new Map(),
config,
environment: {
name: 'browser',
transformMode: 'web',
setup() {
throw new Error('Not called in the browser')
},
},
moduleCache: getBrowserState().moduleCache,
rpc: null as any,
durations: {
environment: 0,
prepare: performance.now(),
},
providedContext,
}

// @ts-expect-error not typed global
globalThis.__vitest_browser__ = true
// @ts-expect-error not typed global
globalThis.__vitest_worker__ = state
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
}
</style>
<script>{__VITEST_INJECTOR__}</script>
<script>{__VITEST_STATE__}</script>
{__VITEST_SCRIPTS__}
</head>
<body
Expand All @@ -28,7 +29,7 @@
transform-origin: left top;
"
>
<script type="module" src="/tester.ts"></script>
<script type="module" src="./tester.ts"></script>
{__VITEST_APPEND__}
</body>
</html>
Loading

0 comments on commit 67d6add

Please sign in to comment.