Skip to content

Commit 280ad1e

Browse files
fix: spy on popup apis like alert (#3076)
Co-authored-by: Vladimir <[email protected]>
1 parent efb91e2 commit 280ad1e

File tree

6 files changed

+109
-91
lines changed

6 files changed

+109
-91
lines changed

docs/guide/browser.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
---
2-
title: Browser mode | Guide
2+
title: Browser Mode | Guide
33
---
44

5-
# Browser mode (experimental)
5+
# Browser Mode (experimental)
66

77
This page provides information about the experimental browser mode feature in the Vitest API, which allows you to run your tests in the browser natively, providing access to browser globals like window and document. This feature is currently under development, and APIs may change in the future.
88

@@ -87,3 +87,10 @@ npx vitest --browser.name=chrome --browser.headless
8787
```
8888

8989
In this case, Vitest will run in headless mode using the Chrome browser.
90+
91+
## Limitations
92+
### Thread Blocking Dialogs
93+
94+
When using Vitest Browser, it's important to note that thread blocking dialogs like `alert` or `confirm` cannot be used natively. This is because they block the web page, which means Vitest cannot continue communicating with the page, causing the execution to hang.
95+
96+
In such situations, Vitest provides default mocks with default returned values for these APIs. This ensures that if the user accidentally uses synchronous popup web APIs, the execution would not hang. However, it's still recommended for the user to mock these web APIs for better experience. Read more in [Mocking](/guide/mocking).

packages/browser/src/client/dialog.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const showPopupWarning = <T>(name: string, value: T, defaultValue?: T) => (...params: any[]) => {
2+
const formatedParams = params.map(p => JSON.stringify(p)).join(', ')
3+
4+
console.warn(`Vitest encountered a \`${name}(${formatedParams})\` call that it cannot handle by default, so it returned \`${value}\`. Read more in https://vitest.dev/guide/browser#thread-blocking-dialogs.
5+
If needed, mock the \`${name}\` call manually like:
6+
7+
\`\`\`
8+
import { expect, vi } from "vitest"
9+
10+
vi.spyOn(window, "${name}")${defaultValue ? `.mockReturnValue(${JSON.stringify(defaultValue)})` : ''}
11+
${name}(${formatedParams})
12+
expect(${name}).toHaveBeenCalledWith(${formatedParams})
13+
\`\`\``)
14+
return value
15+
}
16+
17+
export const setupDialogsSpy = () => {
18+
globalThis.alert = showPopupWarning('alert', undefined)
19+
globalThis.confirm = showPopupWarning('confirm', false, true)
20+
globalThis.prompt = showPopupWarning('prompt', null, 'your value')
21+
}

packages/browser/src/client/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { BrowserSnapshotEnvironment } from './snapshot'
77
import { importId } from './utils'
88
import { setupConsoleLogSpy } from './logger'
99
import { createSafeRpc, rpc, rpcDone } from './rpc'
10+
import { setupDialogsSpy } from './dialog'
1011

1112
// @ts-expect-error mocking some node apis
1213
globalThis.process = { env: {}, argv: [], cwd: () => '/', stdout: { write: () => {} }, nextTick: cb => cb() }
@@ -71,6 +72,7 @@ ws.addEventListener('open', async () => {
7172
iFrame.setAttribute('src', '/__vitest__/')
7273

7374
await setupConsoleLogSpy()
75+
setupDialogsSpy()
7476
await runTests(paths, config)
7577
})
7678

0 commit comments

Comments
 (0)