Skip to content

Commit a855be2

Browse files
authored
fix(webdriverio): handle 'no such alert' error when dialog closes early (webdriverio#14823) (webdriverio#14957)
1 parent 76f960b commit a855be2

2 files changed

Lines changed: 77 additions & 4 deletions

File tree

packages/webdriverio/src/session/dialog.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,18 @@ export class DialogManager extends SessionManager {
5757
*/
5858
async #handleUserPrompt(log: local.BrowsingContextUserPromptOpenedParameters) {
5959
if (this.#autoHandleDialog) {
60-
return this.#browser.browsingContextHandleUserPrompt({
61-
accept: false,
62-
context: log.context
63-
})
60+
try {
61+
return await this.#browser.browsingContextHandleUserPrompt({
62+
accept: false,
63+
context: log.context
64+
})
65+
} catch (err) {
66+
// ignore error if dialog is already closed
67+
if (err instanceof Error && err.message.includes('no such alert')) {
68+
return
69+
}
70+
throw err
71+
}
6472
}
6573

6674
const dialog = new Dialog(log, this.#browser)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest'
2+
import { DialogManager } from '../../src/session/dialog.js'
3+
4+
describe('DialogManager', () => {
5+
let browser: any
6+
let dialogManager: DialogManager
7+
8+
beforeEach(() => {
9+
browser = {
10+
isBidi: true,
11+
sessionId: 'session-id',
12+
sessionSubscribe: vi.fn().mockResolvedValue({}),
13+
on: vi.fn(),
14+
off: vi.fn(),
15+
removeAllListeners: vi.fn(),
16+
emit: vi.fn(),
17+
browsingContextHandleUserPrompt: vi.fn()
18+
}
19+
// Mock isEnabled to bypass environment check which disables it in unit tests
20+
vi.spyOn(DialogManager.prototype, 'isEnabled').mockReturnValue(true)
21+
dialogManager = new DialogManager(browser)
22+
})
23+
24+
it('should ignore "no such alert" error when auto-handling dialogs', async () => {
25+
const log = { context: 'some-context' } as any
26+
27+
// Mock handleUserPrompt to reject with "no such alert"
28+
browser.browsingContextHandleUserPrompt.mockRejectedValue(new Error('no such alert'))
29+
30+
// We need to access the private #handleUserPrompt method or trigger it via the event listener
31+
// Since it's bound in constructor: this.#browser.on('browsingContext.userPromptOpened', this.#handleUserPrompt.bind(this))
32+
// We can simulate the event if we can get the listener.
33+
// However, accessing private private method via trigger might be hard if not exposed.
34+
// But since we are in test, we can try to call the listener registered to 'browsingContext.userPromptOpened'
35+
36+
// Better approach for unit testing private method logic if strictly private:
37+
// Trigger the event handler registered in constructor
38+
const calls = browser.on.mock.calls
39+
const userPromptHandler = calls.find((call: unknown[]) => call[0] === 'browsingContext.userPromptOpened')?.[1]
40+
41+
expect(userPromptHandler).toBeDefined()
42+
43+
// Expectation: The promise should resolve without throwing (error caught internally)
44+
await expect(userPromptHandler(log)).resolves.toBeUndefined()
45+
46+
// Verify handleUserPrompt was called
47+
expect(browser.browsingContextHandleUserPrompt).toHaveBeenCalledWith({
48+
accept: false,
49+
context: 'some-context'
50+
})
51+
})
52+
53+
it('should re-throw other errors', async () => {
54+
const log = { context: 'some-context' } as any
55+
const msg = 'some other error'
56+
browser.browsingContextHandleUserPrompt.mockRejectedValue(new Error(msg))
57+
58+
const calls = browser.on.mock.calls
59+
const userPromptHandler = calls.find((call: unknown[]) => call[0] === 'browsingContext.userPromptOpened')?.[1]
60+
61+
expect(userPromptHandler).toBeDefined()
62+
63+
await expect(userPromptHandler(log)).rejects.toThrow(msg)
64+
})
65+
})

0 commit comments

Comments
 (0)