Skip to content

Commit

Permalink
Merge branch 'main' into feat/have-been-called-before-after
Browse files Browse the repository at this point in the history
  • Loading branch information
Barbapapazes committed Jul 10, 2024
2 parents 6b95014 + 99a12ae commit 4a60233
Show file tree
Hide file tree
Showing 133 changed files with 2,477 additions and 626 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Next generation testing framework powered by Vite.
- Out-of-box TypeScript / JSX support
- Filtering, timeouts, concurrent for suite and tests
- Sharding support
- Run your tests in the browser natively (experimental)

> Vitest 1.0 requires Vite >=v5.0.0 and Node >=v18.0.0
Expand Down
59 changes: 33 additions & 26 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,32 +246,39 @@ export default ({ mode }: { mode: string }) => {
{
text: 'Browser Mode',
link: '/guide/browser/',
collapsed: true,
items: [{
text: 'Assertion API',
link: '/guide/browser/assertion-api',
docFooterText: 'Assertion API | Browser Mode',
}, {
text: 'Retry-ability',
link: '/guide/browser/retry-ability',
docFooterText: 'Retry-ability | Browser Mode',
}, {
text: 'Context',
link: '/guide/browser/context',
docFooterText: 'Context | Browser Mode',
}, {
text: 'Interactivity API',
link: '/guide/browser/interactivity-api',
docFooterText: 'Interactivity API | Browser Mode',
}, {
text: 'Commands',
link: '/guide/browser/commands',
docFooterText: 'Commands | Browser Mode',
}, {
text: 'Examples',
link: '/guide/browser/examples',
docFooterText: 'Examples | Browser Mode',
}],
collapsed: false,
items: [
{
text: 'Assertion API',
link: '/guide/browser/assertion-api',
docFooterText: 'Assertion API | Browser Mode',
},
{
text: 'Retry-ability',
link: '/guide/browser/retry-ability',
docFooterText: 'Retry-ability | Browser Mode',
},
{
text: 'Context',
link: '/guide/browser/context',
docFooterText: 'Context | Browser Mode',
},
{
text: 'Interactivity API',
link: '/guide/browser/interactivity-api',
docFooterText: 'Interactivity API | Browser Mode',
},
{
text: 'Commands',
link: '/guide/browser/commands',
docFooterText: 'Commands | Browser Mode',
},
{
text: 'Examples',
link: '/guide/browser/examples',
docFooterText: 'Examples | Browser Mode',
},
],
},
{
text: 'In-Source Testing',
Expand Down
2 changes: 1 addition & 1 deletion docs/api/mock.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Accepts a function that will be used as an implementation of the mock.
```ts twoslash
import { vi } from 'vitest'
// ---cut---
const mockFn = vi.fn().mockImplementation(apples => apples + 1)
const mockFn = vi.fn().mockImplementation((apples: number) => apples + 1)
// or: vi.fn(apples => apples + 1);

const NelliesBucket = mockFn(0)
Expand Down
2 changes: 1 addition & 1 deletion docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2181,7 +2181,7 @@ Path to a diff config that will be used to generate diff interface. Useful if yo
:::code-group
```ts [vitest.diff.ts]
import type { DiffOptions } from 'vitest'
import c from 'picocolors'
import c from 'tinyrainbow'

export default {
aIndicator: c.bold('--'),
Expand Down
17 changes: 16 additions & 1 deletion docs/guide/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ fn('hello', 1)
expect(vi.isMockFunction(fn)).toBe(true)
expect(fn.mock.calls[0]).toEqual(['hello', 1])

fn.mockImplementation(arg => arg)
fn.mockImplementation((arg: string) => arg)

fn('world', 2)

Expand Down Expand Up @@ -243,3 +243,18 @@ vitest --merge-reports --reporter=junit --coverage.reporter=text
```

See [`Improving Performance | Sharding`](/guide/improving-performance#sharding) for more information.

## Environment Variables

Vitest exclusively autoloads environment variables prefixed with `VITE_` from `.env` files to maintain compatibility with frontend-related tests, adhering to [Vite's established convention](https://vitejs.dev/guide/env-and-mode.html#env-files). To load every environmental variable from `.env` files anyway, you can use `loadEnv` method imported from `vite`:

```ts twoslash
import { loadEnv } from 'vite'
import { defineConfig } from 'vitest/config'

export default defineConfig(({ mode }) => ({
test: {
// mode defines what ".env.{mode}" file to choose if exists
env: loadEnv(mode, process.cwd(), ''),
},
}))
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@vitest/monorepo",
"type": "module",
"version": "2.0.1",
"version": "2.0.2",
"private": true,
"packageManager": "[email protected]",
"description": "Next generation testing framework powered by Vite",
Expand Down
5 changes: 4 additions & 1 deletion packages/browser/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@vitest/browser",
"type": "module",
"version": "2.0.1",
"version": "2.0.2",
"description": "Browser running for Vitest",
"license": "MIT",
"funding": "https://opencollective.com/vitest",
Expand All @@ -28,6 +28,9 @@
"types": "./context.d.ts",
"default": "./context.js"
},
"./client": {
"default": "./dist/client.js"
},
"./matchers": {
"types": "./matchers.d.ts"
},
Expand Down
15 changes: 15 additions & 0 deletions packages/browser/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@ export default () =>
}),
],
},
{
input: './src/client/client.ts',
output: {
file: 'dist/client.js',
format: 'esm',
},
plugins: [
resolve({
preferBuiltins: true,
}),
esbuild({
target: 'node18',
}),
],
},
{
input: './src/client/tester/state.ts',
output: {
Expand Down
6 changes: 3 additions & 3 deletions packages/browser/src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import { getBrowserState } from './utils'

const PAGE_TYPE = getBrowserState().type

export const PORT = import.meta.hot ? '51204' : location.port
export const PORT = location.port
export const HOST = [location.hostname, PORT].filter(Boolean).join(':')
export const SESSION_ID
= PAGE_TYPE === 'orchestrator'
? getBrowserState().contextId
: crypto.randomUUID()
: getBrowserState().testerId
export const ENTRY_URL = `${
location.protocol === 'https:' ? 'wss:' : 'ws:'
}//${HOST}/__vitest_browser_api__?type=${PAGE_TYPE}&sessionId=${SESSION_ID}`
Expand Down Expand Up @@ -136,4 +136,4 @@ function createClient() {

export const client = createClient()

export { channel, waitForChannel } from './channel'
export * from './channel'
3 changes: 2 additions & 1 deletion packages/browser/src/client/orchestrator.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
height: 100%;
}
</style>
<script>{__VITEST_INJECTOR__}</script>
{__VITEST_INJECTOR__}
{__VITEST_ERROR_CATCHER__}
{__VITEST_SCRIPTS__}
</head>
<body>
Expand Down
6 changes: 3 additions & 3 deletions packages/browser/src/client/orchestrator.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { ResolvedConfig } from 'vitest'
import { channel, client } from '@vitest/browser/client'
import { generateHash } from '@vitest/runner/utils'
import { type GlobalChannelIncomingEvent, type IframeChannelEvent, type IframeChannelIncomingEvent, globalChannel } from '@vitest/browser/client'
import { relative } from 'pathe'
import { channel, client } from './client'
import { getBrowserState, getConfig } from './utils'
import { getUiAPI } from './ui'
import { type GlobalChannelIncomingEvent, type IframeChannelEvent, type IframeChannelIncomingEvent, globalChannel } from './channel'
import { createModuleMocker } from './tester/msw'

const url = new URL(location.href)
Expand Down Expand Up @@ -245,7 +245,7 @@ async function getContainer(config: ResolvedConfig): Promise<HTMLDivElement> {
return document.querySelector('#vitest-tester') as HTMLDivElement
}

client.ws.addEventListener('open', async () => {
client.waitForConnection().then(async () => {
const testFiles = getBrowserState().files

await orchestrator.init()
Expand Down
81 changes: 81 additions & 0 deletions packages/browser/src/client/public/error-catcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { channel, client } from '@vitest/browser/client'

function on(event, listener) {
window.addEventListener(event, listener)
return () => window.removeEventListener(event, listener)
}

function serializeError(unhandledError) {
if (typeof unhandledError !== 'object' || !unhandledError) {
return {
message: String(unhandledError),
}
}

return {
name: unhandledError.name,
message: unhandledError.message,
stack: String(unhandledError.stack),
}
}

function catchWindowErrors(cb) {
let userErrorListenerCount = 0
function throwUnhandlerError(e) {
if (userErrorListenerCount === 0 && e.error != null) {
cb(e)
}
else {
console.error(e.error)
}
}
const addEventListener = window.addEventListener.bind(window)
const removeEventListener = window.removeEventListener.bind(window)
window.addEventListener('error', throwUnhandlerError)
window.addEventListener = function (...args) {
if (args[0] === 'error') {
userErrorListenerCount++
}
return addEventListener.apply(this, args)
}
window.removeEventListener = function (...args) {
if (args[0] === 'error' && userErrorListenerCount) {
userErrorListenerCount--
}
return removeEventListener.apply(this, args)
}
return function clearErrorHandlers() {
window.removeEventListener('error', throwUnhandlerError)
}
}

function registerUnexpectedErrors() {
catchWindowErrors(event =>
reportUnexpectedError('Error', event.error),
)
on('unhandledrejection', event =>
reportUnexpectedError('Unhandled Rejection', event.reason))
}

async function reportUnexpectedError(
type,
error,
) {
const processedError = serializeError(error)
await client.rpc.onUnhandledError(processedError, type)
const state = __vitest_browser_runner__

if (state.type === 'orchestrator') {
return
}

if (!state.runTests || !__vitest_worker__.current) {
channel.postMessage({
type: 'done',
filenames: state.files,
id: state.iframeId,
})
}
}

registerUnexpectedErrors()
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 @@ -23,6 +23,7 @@ window.__vitest_browser_runner__ = {
files: { __VITEST_FILES__ },
type: { __VITEST_TYPE__ },
contextId: { __VITEST_CONTEXT_ID__ },
testerId: { __VITEST_TESTER_ID__ },
provider: { __VITEST_PROVIDER__ },
providedContext: { __VITEST_PROVIDED_CONTEXT__ },
};
Expand Down
2 changes: 1 addition & 1 deletion packages/browser/src/client/tester/context.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Task, WorkerGlobalState } from 'vitest'
import type { BrowserRPC } from '@vitest/browser/client'
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
4 changes: 2 additions & 2 deletions packages/browser/src/client/tester/mocker.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { getType } from '@vitest/utils'
import { extname, join } from 'pathe'
import type { IframeChannelOutgoingEvent } from '@vitest/browser/client'
import { channel, waitForChannel } from '@vitest/browser/client'
import { getBrowserState, importId } from '../utils'
import type { IframeChannelOutgoingEvent } from '../channel'
import { channel, waitForChannel } from '../client'
import { rpc } from './rpc'

const now = Date.now
Expand Down
Loading

0 comments on commit 4a60233

Please sign in to comment.