import { type local } from 'webdriver'
import { SessionManager } from './session.js'
import { getContextManager } from './context.js'
export function getDialogManager(browser: WebdriverIO.Browser) {
return SessionManager.getSessionManager(browser, DialogManager)
}
* This class is responsible for managing shadow roots and their elements.
* It allows to do deep element lookups and pierce into shadow DOMs across
* all components of a page.
*/
export class DialogManager extends SessionManager {
#browser: WebdriverIO.Browser
#initialize: Promise<boolean>
#autoHandleDialog = true
constructor(browser: WebdriverIO.Browser) {
super(browser, DialogManager.name)
this.#browser = browser
* don't run setup when Bidi is not supported or running unit tests
*/
if (!this.isEnabled()) {
this.#initialize = Promise.resolve(true)
return
}
* listen on required bidi events
*/
this.#initialize = this.#browser.sessionSubscribe({
events: ['browsingContext.userPromptOpened']
}).then(() => true, () => false)
this.#browser.on('_dialogListenerRegistered', () => this.#switchListenerFlag(false))
this.#browser.on('_dialogListenerRemoved', () => this.#switchListenerFlag(true))
this.#browser.on('browsingContext.userPromptOpened', this.#handleUserPrompt.bind(this))
}
removeListeners(): void {
super.removeListeners()
this.#browser.off('browsingContext.userPromptOpened', this.#handleUserPrompt.bind(this))
this.#browser.removeAllListeners('_dialogListenerRegistered')
this.#browser.removeAllListeners('_dialogListenerRemoved')
}
async initialize () {
return this.#initialize
}
* capture shadow root elements propagated through console.debug
*/
async #handleUserPrompt(log: local.BrowsingContextUserPromptOpenedParameters) {
if (this.#autoHandleDialog) {
return this.#browser.browsingContextHandleUserPrompt({
accept: false,
context: log.context
})
}
const dialog = new Dialog(log, this.#browser)
this.#browser.emit('dialog', dialog)
}
* Is called when a new dialog listener is registered with the `dialog` name.
* In these cases we set a flag to the `#listener` map to indicate that we
* are listening to dialog events for this page in this context.
*/
#switchListenerFlag(value: boolean) {
this.#autoHandleDialog = value
}
}
export class Dialog {
#browser: WebdriverIO.Browser
#context: string
#message: string
#defaultValue?: string
#type: local.BrowsingContextUserPromptOpenedParameters['type']
constructor (event: local.BrowsingContextUserPromptOpenedParameters, browser: WebdriverIO.Browser) {
this.#message = event.message
this.#defaultValue = event.defaultValue
this.#type = event.type
this.#context = event.context
this.#browser = browser
}
message() {
return this.#message
}
defaultValue() {
return this.#defaultValue
}
type() {
return this.#type
}
* Returns when the dialog has been accepted.
*
* @alias dialog.accept
* @param {string=} promptText A text to enter into prompt. Does not cause any effects if the dialog's type is not prompt.
* @returns {Promise<void>}
*/
async accept(userText?: string) {
const contextManager = getContextManager(this.#browser)
const context = await contextManager.getCurrentContext()
if (this.#context !== context) {
return
}
await this.#browser.browsingContextHandleUserPrompt({
accept: true,
context: this.#context,
userText
})
}
async dismiss() {
const contextManager = getContextManager(this.#browser)
const context = await contextManager.getCurrentContext()
if (this.#context !== context) {
return
}
await this.#browser.browsingContextHandleUserPrompt({
accept: false,
context: this.#context
})
}
}