forked from mirrors/gecko-dev
Backed out 4 changesets (bug 1895738, bug 1884650) for causing wdspec failures in user_prompts.py. CLOSED TREE
Backed out changeset 124e4f18e16d (bug 1895738)
Backed out changeset 967f3124eaf7 (bug 1895738)
Backed out changeset 5d46db5db0cd (bug 1895738)
Backed out changeset 9e54c5eb281c (bug 1884650)
This commit is contained in:
parent
e8447adc2a
commit
3e352285bd
16 changed files with 288 additions and 935 deletions
|
|
@ -82,7 +82,6 @@ remote.jar:
|
|||
content/shared/webdriver/NodeCache.sys.mjs (shared/webdriver/NodeCache.sys.mjs)
|
||||
content/shared/webdriver/Session.sys.mjs (shared/webdriver/Session.sys.mjs)
|
||||
content/shared/webdriver/URLPattern.sys.mjs (shared/webdriver/URLPattern.sys.mjs)
|
||||
content/shared/webdriver/UserPromptHandler.sys.mjs (shared/webdriver/UserPromptHandler.sys.mjs)
|
||||
content/shared/webdriver/process-actors/WebDriverProcessDataChild.sys.mjs (shared/webdriver/process-actors/WebDriverProcessDataChild.sys.mjs)
|
||||
content/shared/webdriver/process-actors/WebDriverProcessDataParent.sys.mjs (shared/webdriver/process-actors/WebDriverProcessDataParent.sys.mjs)
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||
permissions: "chrome://remote/content/shared/Permissions.sys.mjs",
|
||||
pprint: "chrome://remote/content/shared/Format.sys.mjs",
|
||||
print: "chrome://remote/content/shared/PDF.sys.mjs",
|
||||
PromptHandlers:
|
||||
"chrome://remote/content/shared/webdriver/UserPromptHandler.sys.mjs",
|
||||
PromptListener:
|
||||
"chrome://remote/content/shared/listeners/PromptListener.sys.mjs",
|
||||
quit: "chrome://remote/content/shared/Browser.sys.mjs",
|
||||
|
|
@ -45,6 +43,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||
TabManager: "chrome://remote/content/shared/TabManager.sys.mjs",
|
||||
TimedPromise: "chrome://remote/content/marionette/sync.sys.mjs",
|
||||
Timeouts: "chrome://remote/content/shared/webdriver/Capabilities.sys.mjs",
|
||||
UnhandledPromptBehavior:
|
||||
"chrome://remote/content/shared/webdriver/Capabilities.sys.mjs",
|
||||
unregisterCommandsActor:
|
||||
"chrome://remote/content/marionette/actors/MarionetteCommandsParent.sys.mjs",
|
||||
waitForInitialNavigationCompleted:
|
||||
|
|
@ -2817,35 +2817,43 @@ GeckoDriver.prototype._handleUserPrompts = async function () {
|
|||
return;
|
||||
}
|
||||
|
||||
const textContent = await this.dialog.getText();
|
||||
const promptType = this.dialog.promptType;
|
||||
|
||||
let type = "default";
|
||||
if (["alert", "confirm", "prompt"].includes(this.dialog.promptType)) {
|
||||
type = promptType;
|
||||
if (this.dialog.promptType == "beforeunload") {
|
||||
// Wait until the "beforeunload" prompt has been accepted.
|
||||
await this.promptListener.dialogClosed();
|
||||
return;
|
||||
}
|
||||
|
||||
const userPromptHandler = this.currentSession.userPromptHandler;
|
||||
const textContent = await this.dialog.getText();
|
||||
|
||||
const handler = userPromptHandler.getPromptHandler(type);
|
||||
switch (handler.handler) {
|
||||
case lazy.PromptHandlers.Accept:
|
||||
const behavior = this.currentSession.unhandledPromptBehavior;
|
||||
switch (behavior) {
|
||||
case lazy.UnhandledPromptBehavior.Accept:
|
||||
await this.acceptDialog();
|
||||
break;
|
||||
case lazy.PromptHandlers.Dismiss:
|
||||
|
||||
case lazy.UnhandledPromptBehavior.AcceptAndNotify:
|
||||
await this.acceptDialog();
|
||||
throw new lazy.error.UnexpectedAlertOpenError(
|
||||
`Accepted user prompt dialog: ${textContent}`
|
||||
);
|
||||
|
||||
case lazy.UnhandledPromptBehavior.Dismiss:
|
||||
await this.dismissDialog();
|
||||
break;
|
||||
case lazy.PromptHandlers.Ignore:
|
||||
break;
|
||||
}
|
||||
|
||||
if (handler.notify) {
|
||||
throw new lazy.error.UnexpectedAlertOpenError(
|
||||
`Unexpected ${promptType} dialog detected. Performed handler "${handler.handler}"`,
|
||||
{
|
||||
text: textContent,
|
||||
}
|
||||
);
|
||||
case lazy.UnhandledPromptBehavior.DismissAndNotify:
|
||||
await this.dismissDialog();
|
||||
throw new lazy.error.UnexpectedAlertOpenError(
|
||||
`Dismissed user prompt dialog: ${textContent}`
|
||||
);
|
||||
|
||||
case lazy.UnhandledPromptBehavior.Ignore:
|
||||
throw new lazy.error.UnexpectedAlertOpenError(
|
||||
"Encountered unhandled user prompt dialog"
|
||||
);
|
||||
|
||||
default:
|
||||
throw new TypeError(`Unknown unhandledPromptBehavior "${behavior}"`);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||
error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
|
||||
pprint: "chrome://remote/content/shared/Format.sys.mjs",
|
||||
RemoteAgent: "chrome://remote/content/components/RemoteAgent.sys.mjs",
|
||||
UserPromptHandler:
|
||||
"chrome://remote/content/shared/webdriver/UserPromptHandler.sys.mjs",
|
||||
});
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "remoteAgent", () => {
|
||||
|
|
@ -407,6 +405,30 @@ export class Proxy {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum of unhandled prompt behavior.
|
||||
*
|
||||
* @enum
|
||||
*/
|
||||
export const UnhandledPromptBehavior = {
|
||||
/** All simple dialogs encountered should be accepted. */
|
||||
Accept: "accept",
|
||||
/**
|
||||
* All simple dialogs encountered should be accepted, and an error
|
||||
* returned that the dialog was handled.
|
||||
*/
|
||||
AcceptAndNotify: "accept and notify",
|
||||
/** All simple dialogs encountered should be dismissed. */
|
||||
Dismiss: "dismiss",
|
||||
/**
|
||||
* All simple dialogs encountered should be dismissed, and an error
|
||||
* returned that the dialog was handled.
|
||||
*/
|
||||
DismissAndNotify: "dismiss and notify",
|
||||
/** All simple dialogs encountered should be left to the user to handle. */
|
||||
Ignore: "ignore",
|
||||
};
|
||||
|
||||
/** WebDriver session capabilities representation. */
|
||||
export class Capabilities extends Map {
|
||||
/** @class */
|
||||
|
|
@ -422,7 +444,7 @@ export class Capabilities extends Map {
|
|||
["setWindowRect", !lazy.AppInfo.isAndroid],
|
||||
["timeouts", new Timeouts()],
|
||||
["strictFileInteractability", false],
|
||||
["unhandledPromptBehavior", new lazy.UserPromptHandler()],
|
||||
["unhandledPromptBehavior", UnhandledPromptBehavior.DismissAndNotify],
|
||||
[
|
||||
"userAgent",
|
||||
Cc["@mozilla.org/network/protocol;1?name=http"].getService(
|
||||
|
|
@ -491,7 +513,6 @@ export class Capabilities extends Map {
|
|||
}
|
||||
|
||||
marshalled.timeouts = super.get("timeouts");
|
||||
marshalled.unhandledPromptBehavior = super.get("unhandledPromptBehavior");
|
||||
|
||||
return marshalled;
|
||||
}
|
||||
|
|
@ -567,7 +588,15 @@ export class Capabilities extends Map {
|
|||
break;
|
||||
|
||||
case "unhandledPromptBehavior":
|
||||
v = lazy.UserPromptHandler.fromJSON(v);
|
||||
lazy.assert.string(
|
||||
v,
|
||||
lazy.pprint`Expected ${k} to be a string, got ${v}`
|
||||
);
|
||||
if (!Object.values(UnhandledPromptBehavior).includes(v)) {
|
||||
throw new lazy.error.InvalidArgumentError(
|
||||
`Unknown unhandled prompt behavior: ${v}`
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case "webSocketUrl":
|
||||
|
|
@ -722,7 +751,16 @@ export class Capabilities extends Map {
|
|||
return Timeouts.fromJSON(value);
|
||||
|
||||
case "unhandledPromptBehavior":
|
||||
return lazy.UserPromptHandler.fromJSON(value);
|
||||
lazy.assert.string(
|
||||
value,
|
||||
lazy.pprint`Expected ${name} to be a string, got ${value}`
|
||||
);
|
||||
if (!Object.values(UnhandledPromptBehavior).includes(value)) {
|
||||
throw new lazy.error.InvalidArgumentError(
|
||||
`Unknown unhandled prompt behavior: ${value}`
|
||||
);
|
||||
}
|
||||
return value;
|
||||
|
||||
case "webSocketUrl":
|
||||
lazy.assert.boolean(
|
||||
|
|
@ -964,8 +1002,9 @@ export function validateCapabilities(capabilities) {
|
|||
Object.entries(capabilities).forEach(([name, value]) => {
|
||||
const deserialized = Capabilities.validate(name, value);
|
||||
if (deserialized !== null) {
|
||||
if (["proxy", "timeouts", "unhandledPromptBehavior"].includes(name)) {
|
||||
// Return pure values for objects that will be setup during session creation.
|
||||
if (name === "proxy" || name === "timeouts") {
|
||||
// Return pure value, the Proxy and Timeouts objects will be setup
|
||||
// during session creation.
|
||||
result[name] = value;
|
||||
} else {
|
||||
result[name] = deserialized;
|
||||
|
|
@ -1023,8 +1062,7 @@ export function processCapabilities(params) {
|
|||
});
|
||||
|
||||
// TODO: Bug 1836288. Implement the capability matching logic
|
||||
// for "browserName", "browserVersion", "platformName", and
|
||||
// "unhandledPromptBehavior" features,
|
||||
// for "browserName", "browserVersion" and "platformName" features,
|
||||
// for now we can just pick the first merged capability.
|
||||
const matchedCapabilities = mergedCapabilities[0];
|
||||
|
||||
|
|
|
|||
|
|
@ -62,13 +62,12 @@ export class WebDriverSession {
|
|||
* <dd>Indicates whether the remote end supports all of the resizing
|
||||
* and repositioning commands.
|
||||
*
|
||||
* <dt><code>strictFileInteractability</code> (boolean)
|
||||
* <dd>Defines the current session’s strict file interactability.
|
||||
*
|
||||
* <dt><code>timeouts</code> (Timeouts object)
|
||||
* <dd>Describes the timeouts imposed on certian session operations.
|
||||
*
|
||||
* TODO: update for WebDriver BiDi type
|
||||
* <dt><code>strictFileInteractability</code> (boolean)
|
||||
* <dd>Defines the current session’s strict file interactability.
|
||||
*
|
||||
* <dt><code>unhandledPromptBehavior</code> (string)
|
||||
* <dd>Describes the current session’s user prompt handler. Must be one of
|
||||
* "<tt>accept</tt>", "<tt>accept and notify</tt>", "<tt>dismiss</tt>",
|
||||
|
|
@ -195,12 +194,6 @@ export class WebDriverSession {
|
|||
throw new lazy.error.SessionNotCreatedError(e);
|
||||
}
|
||||
|
||||
if (this.proxy.init()) {
|
||||
lazy.logger.info(
|
||||
`Proxy settings initialized: ${JSON.stringify(this.proxy)}`
|
||||
);
|
||||
}
|
||||
|
||||
if (this.capabilities.get("acceptInsecureCerts")) {
|
||||
lazy.logger.warn(
|
||||
"TLS certificate errors will be ignored for this session"
|
||||
|
|
@ -208,6 +201,12 @@ export class WebDriverSession {
|
|||
lazy.allowAllCerts.enable();
|
||||
}
|
||||
|
||||
if (this.proxy.init()) {
|
||||
lazy.logger.info(
|
||||
`Proxy settings initialised: ${JSON.stringify(this.proxy)}`
|
||||
);
|
||||
}
|
||||
|
||||
// If we are testing accessibility with marionette, start a11y service in
|
||||
// chrome first. This will ensure that we do not have any content-only
|
||||
// services hanging around.
|
||||
|
|
@ -320,7 +319,7 @@ export class WebDriverSession {
|
|||
this.capabilities.set("timeouts", timeouts);
|
||||
}
|
||||
|
||||
get userPromptHandler() {
|
||||
get unhandledPromptBehavior() {
|
||||
return this.capabilities.get("unhandledPromptBehavior");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,279 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs",
|
||||
pprint: "chrome://remote/content/shared/Format.sys.mjs",
|
||||
});
|
||||
|
||||
/**
|
||||
* @typedef {string} PromptHandlers
|
||||
*/
|
||||
|
||||
/**
|
||||
* Enum of known prompt handlers.
|
||||
*
|
||||
* @readonly
|
||||
* @enum {PromptHandlers}
|
||||
*
|
||||
* @see https://w3c.github.io/webdriver/#dfn-known-prompt-handlers
|
||||
*/
|
||||
export const PromptHandlers = {
|
||||
/** All simple dialogs encountered should be accepted. */
|
||||
Accept: "accept",
|
||||
/**
|
||||
* All simple dialogs encountered should be accepted, and an error
|
||||
* returned that the dialog was handled.
|
||||
*/
|
||||
AcceptAndNotify: "accept and notify",
|
||||
/** All simple dialogs encountered should be dismissed. */
|
||||
Dismiss: "dismiss",
|
||||
/**
|
||||
* All simple dialogs encountered should be dismissed, and an error
|
||||
* returned that the dialog was handled.
|
||||
*/
|
||||
DismissAndNotify: "dismiss and notify",
|
||||
/** All simple dialogs encountered should be left to the user to handle. */
|
||||
Ignore: "ignore",
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {string} PromptTypes
|
||||
*/
|
||||
|
||||
/**
|
||||
* Enum of valid prompt types.
|
||||
*
|
||||
* @readonly
|
||||
* @enum {PromptTypes}
|
||||
*
|
||||
* @see https://w3c.github.io/webdriver/#dfn-valid-prompt-types
|
||||
*/
|
||||
export const PromptTypes = {
|
||||
// A simple alert dialog
|
||||
Alert: "alert",
|
||||
// A simple beforeunload dialog. If no handler is set it falls back to the
|
||||
// `accept` handler to keep backward compatibility with WebDriver classic,
|
||||
// which doesn't customize this prompt type.
|
||||
BeforeUnload: "beforeUnload",
|
||||
// A simple confirm dialog
|
||||
Confirm: "confirm",
|
||||
// Fallback value when no specific handler is defined. Not used for the
|
||||
// `beforeunload` prompt type.
|
||||
Default: "default",
|
||||
// A simple prompt dialog
|
||||
Prompt: "prompt",
|
||||
};
|
||||
|
||||
export class PromptHandlerConfiguration {
|
||||
#handler;
|
||||
#notify;
|
||||
|
||||
/**
|
||||
* Create an instance of a prompt handler configuration.
|
||||
*
|
||||
* @param {string} handler
|
||||
* Handler to use for the user prompt. One of "accept", "dismiss" or "ignore".
|
||||
* @param {boolean} notify
|
||||
* Flag to indicate if the user needs to be notified when the dialog was
|
||||
* handled.
|
||||
*
|
||||
* @see https://w3c.github.io/webdriver/#dfn-prompt-handler-configuration
|
||||
*/
|
||||
constructor(handler, notify) {
|
||||
this.#handler = handler;
|
||||
this.#notify = notify;
|
||||
}
|
||||
|
||||
get handler() {
|
||||
return this.#handler;
|
||||
}
|
||||
|
||||
get notify() {
|
||||
return this.#notify;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return "[object PromptHandlerConfiguration]";
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON serialization of the prompt handler configuration object.
|
||||
*
|
||||
* @returns {Object<string, ?>} json
|
||||
*
|
||||
* @see https://w3c.github.io/webdriver/#dfn-serialize-a-prompt-handler-configuration
|
||||
*/
|
||||
toJSON() {
|
||||
let serialized = this.#handler;
|
||||
if (["accept", "dismiss"].includes(serialized) && this.#notify) {
|
||||
serialized += " and notify";
|
||||
}
|
||||
|
||||
return serialized;
|
||||
}
|
||||
}
|
||||
|
||||
export class UserPromptHandler {
|
||||
#promptTypeToHandlerMap;
|
||||
|
||||
constructor() {
|
||||
this.#promptTypeToHandlerMap = new Map();
|
||||
}
|
||||
|
||||
get activePromptHandlers() {
|
||||
return this.#promptTypeToHandlerMap;
|
||||
}
|
||||
|
||||
get PromptHandlers() {
|
||||
return PromptHandlers;
|
||||
}
|
||||
|
||||
get PromptTypes() {
|
||||
return PromptTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarshal a JSON object representation of the unhandledPromptBehavior capability.
|
||||
*
|
||||
* @param {Object<string, ?>} json
|
||||
* JSON Object to unmarshal.
|
||||
*
|
||||
* @throws {InvalidArgumentError}
|
||||
* When the value of the unhandledPromptBehavior capability is invalid.
|
||||
*
|
||||
* @returns {UserPromptHandler}
|
||||
*
|
||||
* @see https://w3c.github.io/webdriver/#dfn-deserialize-as-an-unhandled-prompt-behavior
|
||||
*/
|
||||
static fromJSON(json) {
|
||||
let value = json;
|
||||
if (typeof value === "string") {
|
||||
// A single specified prompt behavior or for WebDriver classic.
|
||||
value = { [PromptTypes.Default]: value };
|
||||
}
|
||||
|
||||
lazy.assert.object(
|
||||
value,
|
||||
lazy.pprint`Expected "unhandledPromptBehavior" to be an object, got ${value}`
|
||||
);
|
||||
|
||||
const userPromptHandler = new UserPromptHandler();
|
||||
for (let [promptType, handler] of Object.entries(value)) {
|
||||
const validPromptTypes = Object.values(PromptTypes);
|
||||
lazy.assert.in(
|
||||
promptType,
|
||||
validPromptTypes,
|
||||
lazy.pprint`Expected "promptType" to be one of ${validPromptTypes}, got ${promptType}`
|
||||
);
|
||||
|
||||
const knownPromptHandlers = Object.values(PromptHandlers);
|
||||
lazy.assert.in(
|
||||
handler,
|
||||
knownPromptHandlers,
|
||||
lazy.pprint`Expected "handler" to be one of ${knownPromptHandlers}, got ${handler}`
|
||||
);
|
||||
|
||||
let notify = false;
|
||||
switch (handler) {
|
||||
case PromptHandlers.AcceptAndNotify:
|
||||
handler = PromptHandlers.Accept;
|
||||
notify = true;
|
||||
break;
|
||||
case PromptHandlers.DismissAndNotify:
|
||||
handler = PromptHandlers.Dismiss;
|
||||
notify = true;
|
||||
break;
|
||||
case PromptHandlers.Ignore:
|
||||
notify = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const configuration = new PromptHandlerConfiguration(handler, notify);
|
||||
userPromptHandler.set(promptType, configuration);
|
||||
}
|
||||
|
||||
return userPromptHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the handler for the given prompt type.
|
||||
*
|
||||
* @param {string} promptType
|
||||
* The prompt type to retrieve the handler for.
|
||||
*
|
||||
* @returns {PromptHandlerConfiguration}
|
||||
*
|
||||
* @see https://w3c.github.io/webdriver/#dfn-get-the-prompt-handler
|
||||
*/
|
||||
getPromptHandler(promptType) {
|
||||
let handlers;
|
||||
|
||||
if (this.#promptTypeToHandlerMap.size === 0) {
|
||||
handlers = new Map();
|
||||
} else {
|
||||
handlers = this.#promptTypeToHandlerMap;
|
||||
}
|
||||
|
||||
if (handlers.has(promptType)) {
|
||||
return handlers.get(promptType);
|
||||
}
|
||||
|
||||
if (promptType === PromptTypes.BeforeUnload) {
|
||||
return new PromptHandlerConfiguration(PromptHandlers.Accept, false);
|
||||
}
|
||||
|
||||
if (handlers.has(PromptTypes.Default)) {
|
||||
return handlers.get(PromptTypes.Default);
|
||||
}
|
||||
|
||||
return new PromptHandlerConfiguration(PromptHandlers.Dismiss, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a prompt handler configuration for a given prompt type.
|
||||
*
|
||||
* @param {string} promptType
|
||||
* Prompt type to set the handler configuration for.
|
||||
* @param {PromptHandlerConfiguration} handler
|
||||
* Handler for the prompt type.
|
||||
*/
|
||||
set(promptType, handler) {
|
||||
this.#promptTypeToHandlerMap.set(promptType, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON serialization of the user prompt handler object.
|
||||
*
|
||||
* @returns {Object<string, ?>} json
|
||||
*
|
||||
* @see https://w3c.github.io/webdriver/#dfn-serialize-the-user-prompt-handler
|
||||
*/
|
||||
toJSON() {
|
||||
if (this.#promptTypeToHandlerMap.size == 0) {
|
||||
// Fallback to "dismiss and notify" if no handler is set
|
||||
return PromptHandlers.DismissAndNotify;
|
||||
}
|
||||
|
||||
if (
|
||||
this.#promptTypeToHandlerMap.size === 1 &&
|
||||
this.#promptTypeToHandlerMap.has(PromptTypes.Default)
|
||||
) {
|
||||
return this.#promptTypeToHandlerMap.get(PromptTypes.Default).toJSON();
|
||||
}
|
||||
|
||||
const serialized = {};
|
||||
for (const [key, value] of this.#promptTypeToHandlerMap.entries()) {
|
||||
serialized[key] = value.toJSON();
|
||||
}
|
||||
|
||||
return serialized;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return "[object UserPromptHandler]";
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ const {
|
|||
processCapabilities,
|
||||
Proxy,
|
||||
Timeouts,
|
||||
UnhandledPromptBehavior,
|
||||
validateCapabilities,
|
||||
} = ChromeUtils.importESModule(
|
||||
"chrome://remote/content/shared/webdriver/Capabilities.sys.mjs"
|
||||
|
|
@ -369,6 +370,14 @@ add_task(function test_Proxy_fromJSON() {
|
|||
deepEqual(p, Proxy.fromJSON(manual));
|
||||
});
|
||||
|
||||
add_task(function test_UnhandledPromptBehavior() {
|
||||
equal(UnhandledPromptBehavior.Accept, "accept");
|
||||
equal(UnhandledPromptBehavior.AcceptAndNotify, "accept and notify");
|
||||
equal(UnhandledPromptBehavior.Dismiss, "dismiss");
|
||||
equal(UnhandledPromptBehavior.DismissAndNotify, "dismiss and notify");
|
||||
equal(UnhandledPromptBehavior.Ignore, "ignore");
|
||||
});
|
||||
|
||||
add_task(function test_Capabilities_ctor() {
|
||||
let caps = new Capabilities();
|
||||
ok(caps.has("browserName"));
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ add_task(function test_WebDriverSession_getters() {
|
|||
);
|
||||
equal(session.timeouts, session.capabilities.get("timeouts"));
|
||||
equal(
|
||||
session.userPromptHandler,
|
||||
session.unhandledPromptBehavior,
|
||||
session.capabilities.get("unhandledPromptBehavior")
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,216 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
PromptHandlerConfiguration,
|
||||
PromptHandlers,
|
||||
PromptTypes,
|
||||
UserPromptHandler,
|
||||
} = ChromeUtils.importESModule(
|
||||
"chrome://remote/content/shared/webdriver/UserPromptHandler.sys.mjs"
|
||||
);
|
||||
|
||||
add_task(function test_PromptHandlerConfiguration_ctor() {
|
||||
const config = new PromptHandlerConfiguration("accept", true);
|
||||
equal(config.handler, "accept");
|
||||
equal(config.notify, true);
|
||||
});
|
||||
|
||||
add_task(function test_PromptHandlerConfiguration_toString() {
|
||||
equal(
|
||||
new PromptHandlerConfiguration().toString(),
|
||||
"[object PromptHandlerConfiguration]"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(function test_PromptHandlerConfiguration_toJSON() {
|
||||
let configuration, serialized;
|
||||
|
||||
for (const behavior of Object.values(PromptHandlers)) {
|
||||
console.log(`Test handler - ${behavior}`);
|
||||
configuration = new PromptHandlerConfiguration(behavior, false);
|
||||
serialized = configuration.toJSON();
|
||||
equal(serialized, behavior);
|
||||
|
||||
configuration = new PromptHandlerConfiguration(behavior, true);
|
||||
serialized = configuration.toJSON();
|
||||
if ([PromptHandlers.Accept, PromptHandlers.Dismiss].includes(behavior)) {
|
||||
equal(serialized, `${behavior} and notify`);
|
||||
} else {
|
||||
equal(serialized, behavior);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function test_UserPromptHandler_ctor() {
|
||||
const handler = new UserPromptHandler();
|
||||
equal(handler.activePromptHandlers.size, 0);
|
||||
});
|
||||
|
||||
add_task(function test_UserPromptHandler_toString() {
|
||||
equal(new UserPromptHandler().toString(), "[object UserPromptHandler]");
|
||||
});
|
||||
|
||||
add_task(function test_UserPromptHandler_fromJSON() {
|
||||
let promptHandler;
|
||||
|
||||
// Unhandled prompt behavior as string
|
||||
for (const behavior of Object.values(PromptHandlers)) {
|
||||
console.log(`Test as string for ${behavior}`);
|
||||
promptHandler = UserPromptHandler.fromJSON(behavior);
|
||||
equal(promptHandler.activePromptHandlers.size, 1);
|
||||
ok(promptHandler.activePromptHandlers.has("default"));
|
||||
const handler = promptHandler.activePromptHandlers.get("default");
|
||||
ok(behavior.startsWith(handler.handler));
|
||||
if (behavior == "ignore") {
|
||||
ok(handler.notify);
|
||||
} else {
|
||||
equal(handler.notify, /and notify/.test(behavior));
|
||||
}
|
||||
}
|
||||
|
||||
// Unhandled prompt behavior as object
|
||||
for (const promptType of Object.values(PromptTypes)) {
|
||||
for (const behavior of Object.values(PromptHandlers)) {
|
||||
console.log(`Test as object for ${promptType} - ${behavior}`);
|
||||
promptHandler = UserPromptHandler.fromJSON({ [promptType]: behavior });
|
||||
equal(promptHandler.activePromptHandlers.size, 1);
|
||||
ok(promptHandler.activePromptHandlers.has(promptType));
|
||||
const handler = promptHandler.activePromptHandlers.get(promptType);
|
||||
ok(behavior.startsWith(handler.handler));
|
||||
if (behavior == "ignore") {
|
||||
ok(handler.notify);
|
||||
} else {
|
||||
equal(handler.notify, /and notify/.test(behavior));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function test_UserPromptHandler_fromJSON_invalid() {
|
||||
for (const type of [
|
||||
undefined,
|
||||
null,
|
||||
true,
|
||||
42,
|
||||
[],
|
||||
"default",
|
||||
"beforeunload",
|
||||
]) {
|
||||
Assert.throws(
|
||||
() => UserPromptHandler.fromJSON(type),
|
||||
/InvalidArgumentError/
|
||||
);
|
||||
}
|
||||
|
||||
// Invalid types for prompt types and handlers
|
||||
for (const type of [undefined, null, true, 42, {}, []]) {
|
||||
Assert.throws(
|
||||
() => UserPromptHandler.fromJSON({ [type]: "accept" }),
|
||||
/InvalidArgumentError/
|
||||
);
|
||||
Assert.throws(
|
||||
() => UserPromptHandler.fromJSON({ alert: type }),
|
||||
/InvalidArgumentError/
|
||||
);
|
||||
}
|
||||
|
||||
// Invalid values for prompt type and handlers
|
||||
Assert.throws(
|
||||
() => UserPromptHandler.fromJSON({ foo: "accept" }),
|
||||
/InvalidArgumentError/
|
||||
);
|
||||
Assert.throws(
|
||||
() => UserPromptHandler.fromJSON({ alert: "foo" }),
|
||||
/InvalidArgumentError/
|
||||
);
|
||||
});
|
||||
|
||||
add_task(function test_UserPromptHandler_getPromptHandler() {
|
||||
let configuration, promptHandler;
|
||||
|
||||
// Check the default value with no handlers defined
|
||||
promptHandler = new UserPromptHandler();
|
||||
equal(promptHandler.activePromptHandlers.size, 0);
|
||||
for (const promptType of Object.values(PromptTypes)) {
|
||||
console.log(`Test default behavior for ${promptType}`);
|
||||
const handler = promptHandler.getPromptHandler(promptType);
|
||||
if (promptType === PromptTypes.BeforeUnload) {
|
||||
equal(handler.handler, PromptHandlers.Accept);
|
||||
equal(handler.notify, false);
|
||||
} else {
|
||||
equal(handler.handler, PromptHandlers.Dismiss);
|
||||
equal(handler.notify, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Check custom default behavior for all prompt types
|
||||
promptHandler = new UserPromptHandler();
|
||||
configuration = new PromptHandlerConfiguration(PromptHandlers.Ignore, false);
|
||||
promptHandler.set(PromptTypes.Default, configuration);
|
||||
for (const promptType of Object.values(PromptTypes)) {
|
||||
console.log(`Test custom default behavior for ${promptType}`);
|
||||
equal(promptHandler.activePromptHandlers.size, 1);
|
||||
const handler = promptHandler.getPromptHandler(promptType);
|
||||
if (promptType === PromptTypes.BeforeUnload) {
|
||||
// For backward compatibility with WebDriver classic it is not overridden
|
||||
equal(handler.handler, PromptHandlers.Accept);
|
||||
equal(handler.notify, false);
|
||||
} else {
|
||||
equal(handler.handler, PromptHandlers.Ignore);
|
||||
equal(handler.notify, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Check custom behavior overrides default for all prompt types
|
||||
promptHandler = new UserPromptHandler();
|
||||
configuration = new PromptHandlerConfiguration(PromptHandlers.Ignore, false);
|
||||
promptHandler.set(PromptTypes.Default, configuration);
|
||||
for (const promptType of Object.values(PromptTypes)) {
|
||||
if (promptType === PromptTypes.Default) {
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Test custom behavior overrides default for ${promptType}`);
|
||||
configuration = new PromptHandlerConfiguration(PromptHandlers.Accept, true);
|
||||
promptHandler.set(promptType, configuration);
|
||||
const handler = promptHandler.getPromptHandler(promptType);
|
||||
equal(handler.handler, PromptHandlers.Accept);
|
||||
equal(handler.notify, true);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function test_UserPromptHandler_toJSON() {
|
||||
let configuration, promptHandler, serialized;
|
||||
|
||||
// Default behavior
|
||||
promptHandler = new UserPromptHandler();
|
||||
serialized = promptHandler.toJSON();
|
||||
equal(serialized, "dismiss and notify");
|
||||
|
||||
// Custom default behavior
|
||||
promptHandler = new UserPromptHandler();
|
||||
configuration = new PromptHandlerConfiguration(PromptHandlers.Accept, false);
|
||||
promptHandler.set(PromptTypes.Default, configuration);
|
||||
serialized = promptHandler.toJSON();
|
||||
equal(serialized, "accept");
|
||||
|
||||
// Multiple handler definitions
|
||||
promptHandler = new UserPromptHandler();
|
||||
configuration = new PromptHandlerConfiguration(PromptHandlers.Ignore, false);
|
||||
promptHandler.set(PromptTypes.Default, configuration);
|
||||
configuration = new PromptHandlerConfiguration(PromptHandlers.Accept, true);
|
||||
promptHandler.set(PromptTypes.Alert, configuration);
|
||||
configuration = new PromptHandlerConfiguration(PromptHandlers.Dismiss, false);
|
||||
promptHandler.set(PromptTypes.Confirm, configuration);
|
||||
|
||||
serialized = promptHandler.toJSON();
|
||||
deepEqual(serialized, {
|
||||
default: "ignore",
|
||||
alert: "accept and notify",
|
||||
confirm: "dismiss",
|
||||
});
|
||||
});
|
||||
|
|
@ -18,5 +18,3 @@ head = "head.js"
|
|||
["test_URLPattern_matchURLPattern.js"]
|
||||
|
||||
["test_URLPattern_parseURLPattern.js"]
|
||||
|
||||
["test_UserPromptHandler.js"]
|
||||
|
|
|
|||
|
|
@ -10,22 +10,6 @@ import marionette_driver.errors as errors
|
|||
from marionette_harness import MarionetteTestCase
|
||||
|
||||
|
||||
PROMPT_HANDLERS = [
|
||||
"accept",
|
||||
"accept and notify",
|
||||
"dismiss",
|
||||
"dismiss and notify",
|
||||
"ignore",
|
||||
]
|
||||
|
||||
PROMPT_TYPES = [
|
||||
"alert",
|
||||
"beforeUnload",
|
||||
"confirm",
|
||||
"prompt",
|
||||
]
|
||||
|
||||
|
||||
class TestCapabilities(MarionetteTestCase):
|
||||
def setUp(self):
|
||||
super(TestCapabilities, self).setUp()
|
||||
|
|
@ -171,7 +155,7 @@ class TestCapabilityMatching(MarionetteTestCase):
|
|||
|
||||
def test_accept_insecure_certs(self):
|
||||
for value in ["", 42, {}, []]:
|
||||
print(f" type {type(value)}")
|
||||
print(" type {}".format(type(value)))
|
||||
with self.assertRaises(errors.SessionNotCreatedException):
|
||||
self.marionette.start_session({"acceptInsecureCerts": value})
|
||||
|
||||
|
|
@ -181,7 +165,7 @@ class TestCapabilityMatching(MarionetteTestCase):
|
|||
|
||||
def test_page_load_strategy(self):
|
||||
for strategy in ["none", "eager", "normal"]:
|
||||
print(f"valid strategy {strategy}")
|
||||
print("valid strategy {}".format(strategy))
|
||||
self.delete_session()
|
||||
self.marionette.start_session({"pageLoadStrategy": strategy})
|
||||
self.assertEqual(
|
||||
|
|
@ -191,7 +175,7 @@ class TestCapabilityMatching(MarionetteTestCase):
|
|||
self.delete_session()
|
||||
|
||||
for value in ["", "EAGER", True, 42, {}, []]:
|
||||
print(f"invalid strategy {value}")
|
||||
print("invalid strategy {}".format(value))
|
||||
with self.assertRaisesRegexp(
|
||||
errors.SessionNotCreatedException, "InvalidArgumentError"
|
||||
):
|
||||
|
|
@ -205,7 +189,7 @@ class TestCapabilityMatching(MarionetteTestCase):
|
|||
|
||||
def test_timeouts(self):
|
||||
for value in ["", 2.5, {}, []]:
|
||||
print(f" type {type(value)}")
|
||||
print(" type {}".format(type(value)))
|
||||
with self.assertRaises(errors.SessionNotCreatedException):
|
||||
self.marionette.start_session({"timeouts": {"pageLoad": value}})
|
||||
|
||||
|
|
@ -221,7 +205,7 @@ class TestCapabilityMatching(MarionetteTestCase):
|
|||
|
||||
def test_strict_file_interactability(self):
|
||||
for value in ["", 2.5, {}, []]:
|
||||
print(f" type {type(value)}")
|
||||
print(" type {}".format(type(value)))
|
||||
with self.assertRaises(errors.SessionNotCreatedException):
|
||||
self.marionette.start_session({"strictFileInteractability": value})
|
||||
|
||||
|
|
@ -241,19 +225,25 @@ class TestCapabilityMatching(MarionetteTestCase):
|
|||
self.marionette.session_capabilities["strictFileInteractability"]
|
||||
)
|
||||
|
||||
def test_unhandled_prompt_behavior_as_string(self):
|
||||
"""WebDriver Classic (HTTP) style"""
|
||||
def test_unhandled_prompt_behavior(self):
|
||||
behaviors = [
|
||||
"accept",
|
||||
"accept and notify",
|
||||
"dismiss",
|
||||
"dismiss and notify",
|
||||
"ignore",
|
||||
]
|
||||
|
||||
# Invalid values
|
||||
self.delete_session()
|
||||
for handler in ["", "ACCEPT", True, 42, []]:
|
||||
print(f"invalid unhandled prompt behavior {handler}")
|
||||
with self.assertRaisesRegexp(
|
||||
errors.SessionNotCreatedException, "InvalidArgumentError"
|
||||
):
|
||||
self.marionette.start_session({"unhandledPromptBehavior": handler})
|
||||
for behavior in behaviors:
|
||||
print("valid unhandled prompt behavior {}".format(behavior))
|
||||
self.delete_session()
|
||||
self.marionette.start_session({"unhandledPromptBehavior": behavior})
|
||||
self.assertEqual(
|
||||
self.marionette.session_capabilities["unhandledPromptBehavior"],
|
||||
behavior,
|
||||
)
|
||||
|
||||
# Default value if capability is not requested when creating a new session.
|
||||
# Default value
|
||||
self.delete_session()
|
||||
self.marionette.start_session()
|
||||
self.assertEqual(
|
||||
|
|
@ -261,45 +251,14 @@ class TestCapabilityMatching(MarionetteTestCase):
|
|||
"dismiss and notify",
|
||||
)
|
||||
|
||||
for handler in PROMPT_HANDLERS:
|
||||
print(f" value {handler}")
|
||||
self.delete_session()
|
||||
self.marionette.start_session({"unhandledPromptBehavior": handler})
|
||||
self.assertEqual(
|
||||
self.marionette.session_capabilities["unhandledPromptBehavior"],
|
||||
handler,
|
||||
)
|
||||
|
||||
def test_unhandled_prompt_behavior_as_object(self):
|
||||
"""WebDriver BiDi style"""
|
||||
|
||||
# Invalid values
|
||||
self.delete_session()
|
||||
for handler in [{"foo": "accept"}, {"alert": "bar"}]:
|
||||
print(f"invalid unhandled prompt behavior {handler}")
|
||||
for behavior in ["", "ACCEPT", True, 42, {}, []]:
|
||||
print("invalid unhandled prompt behavior {}".format(behavior))
|
||||
with self.assertRaisesRegexp(
|
||||
errors.SessionNotCreatedException, "InvalidArgumentError"
|
||||
):
|
||||
self.marionette.start_session({"unhandledPromptBehavior": handler})
|
||||
|
||||
# Default value if capability is not requested when creating a new session.
|
||||
self.delete_session()
|
||||
self.marionette.start_session({"unhandledPromptBehavior": {}})
|
||||
self.assertEqual(
|
||||
self.marionette.session_capabilities["unhandledPromptBehavior"],
|
||||
"dismiss and notify",
|
||||
)
|
||||
|
||||
for prompt_type in PROMPT_TYPES:
|
||||
for handler in PROMPT_HANDLERS:
|
||||
value = {prompt_type: handler}
|
||||
print(f" value {value}")
|
||||
self.delete_session()
|
||||
self.marionette.start_session({"unhandledPromptBehavior": value})
|
||||
self.assertEqual(
|
||||
self.marionette.session_capabilities["unhandledPromptBehavior"],
|
||||
value,
|
||||
)
|
||||
self.marionette.start_session({"unhandledPromptBehavior": behavior})
|
||||
|
||||
def test_web_socket_url(self):
|
||||
self.marionette.start_session({"webSocketUrl": True})
|
||||
|
|
@ -308,7 +267,7 @@ class TestCapabilityMatching(MarionetteTestCase):
|
|||
|
||||
def test_webauthn_extension_cred_blob(self):
|
||||
for value in ["", 42, {}, []]:
|
||||
print(f" type {type(value)}")
|
||||
print(" type {}".format(type(value)))
|
||||
with self.assertRaises(errors.SessionNotCreatedException):
|
||||
self.marionette.start_session({"webauthn:extension:credBlob": value})
|
||||
|
||||
|
|
@ -320,7 +279,7 @@ class TestCapabilityMatching(MarionetteTestCase):
|
|||
|
||||
def test_webauthn_extension_large_blob(self):
|
||||
for value in ["", 42, {}, []]:
|
||||
print(f" type {type(value)}")
|
||||
print(" type {}".format(type(value)))
|
||||
with self.assertRaises(errors.SessionNotCreatedException):
|
||||
self.marionette.start_session({"webauthn:extension:largeBlob": value})
|
||||
|
||||
|
|
@ -332,7 +291,7 @@ class TestCapabilityMatching(MarionetteTestCase):
|
|||
|
||||
def test_webauthn_extension_prf(self):
|
||||
for value in ["", 42, {}, []]:
|
||||
print(f" type {type(value)}")
|
||||
print(" type {}".format(type(value)))
|
||||
with self.assertRaises(errors.SessionNotCreatedException):
|
||||
self.marionette.start_session({"webauthn:extension:prf": value})
|
||||
|
||||
|
|
@ -342,7 +301,7 @@ class TestCapabilityMatching(MarionetteTestCase):
|
|||
|
||||
def test_webauthn_extension_uvm(self):
|
||||
for value in ["", 42, {}, []]:
|
||||
print(f" type {type(value)}")
|
||||
print(" type {}".format(type(value)))
|
||||
with self.assertRaises(errors.SessionNotCreatedException):
|
||||
self.marionette.start_session({"webauthn:extension:uvm": value})
|
||||
|
||||
|
|
@ -352,7 +311,7 @@ class TestCapabilityMatching(MarionetteTestCase):
|
|||
|
||||
def test_webauthn_virtual_authenticators(self):
|
||||
for value in ["", 42, {}, []]:
|
||||
print(f" type {type(value)}")
|
||||
print(" type {}".format(type(value)))
|
||||
with self.assertRaises(errors.SessionNotCreatedException):
|
||||
self.marionette.start_session({"webauthn:virtualAuthenticators": value})
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
from marionette_driver import errors
|
||||
from marionette_driver.marionette import Alert
|
||||
from marionette_driver.wait import Wait
|
||||
from marionette_harness import MarionetteTestCase, parameterized
|
||||
|
||||
|
||||
class TestUnhandledPromptBehavior(MarionetteTestCase):
|
||||
def setUp(self):
|
||||
super(TestUnhandledPromptBehavior, self).setUp()
|
||||
|
||||
self.marionette.delete_session()
|
||||
|
||||
def tearDown(self):
|
||||
# Ensure to close a possible remaining tab modal dialog
|
||||
try:
|
||||
alert = self.marionette.switch_to_alert()
|
||||
alert.dismiss()
|
||||
|
||||
Wait(self.marionette).until(lambda _: not self.alert_present)
|
||||
except errors.NoAlertPresentException:
|
||||
pass
|
||||
|
||||
super(TestUnhandledPromptBehavior, self).tearDown()
|
||||
|
||||
@property
|
||||
def alert_present(self):
|
||||
try:
|
||||
Alert(self.marionette).text
|
||||
return True
|
||||
except errors.NoAlertPresentException:
|
||||
return False
|
||||
|
||||
def perform_user_prompt_check(
|
||||
self,
|
||||
prompt_type,
|
||||
text,
|
||||
expected_result,
|
||||
expected_close=True,
|
||||
expected_notify=True,
|
||||
):
|
||||
if prompt_type not in ["alert", "confirm", "prompt"]:
|
||||
raise TypeError("Invalid dialog type: {}".format(prompt_type))
|
||||
|
||||
# No need to call resolve() because opening a prompt stops the script
|
||||
self.marionette.execute_async_script(
|
||||
"""
|
||||
window.return_value = null;
|
||||
window.return_value = window[arguments[0]](arguments[1]);
|
||||
""",
|
||||
script_args=(prompt_type, text),
|
||||
)
|
||||
|
||||
if expected_notify:
|
||||
with self.assertRaises(errors.UnexpectedAlertOpen):
|
||||
self.marionette.title
|
||||
# Bug 1469752 - WebDriverError misses optional data property
|
||||
# self.assertEqual(ex.data.text, text)
|
||||
else:
|
||||
self.marionette.title
|
||||
|
||||
self.assertEqual(self.alert_present, not expected_close)
|
||||
|
||||
# Close an expected left-over user prompt
|
||||
if not expected_close:
|
||||
alert = self.marionette.switch_to_alert()
|
||||
alert.dismiss()
|
||||
|
||||
else:
|
||||
prompt_result = self.marionette.execute_script(
|
||||
"return window.return_value", new_sandbox=False
|
||||
)
|
||||
self.assertEqual(prompt_result, expected_result)
|
||||
|
||||
@parameterized("alert", "alert", None)
|
||||
@parameterized("confirm", "confirm", True)
|
||||
@parameterized("prompt", "prompt", "")
|
||||
def test_accept(self, prompt_type, result):
|
||||
self.marionette.start_session({"unhandledPromptBehavior": "accept"})
|
||||
self.perform_user_prompt_check(
|
||||
prompt_type, "foo {}".format(prompt_type), result, expected_notify=False
|
||||
)
|
||||
|
||||
@parameterized("alert", "alert", None)
|
||||
@parameterized("confirm", "confirm", True)
|
||||
@parameterized("prompt", "prompt", "")
|
||||
def test_accept_and_notify(self, prompt_type, result):
|
||||
self.marionette.start_session({"unhandledPromptBehavior": "accept and notify"})
|
||||
self.perform_user_prompt_check(
|
||||
prompt_type, "foo {}".format(prompt_type), result
|
||||
)
|
||||
|
||||
@parameterized("alert", "alert", None)
|
||||
@parameterized("confirm", "confirm", False)
|
||||
@parameterized("prompt", "prompt", None)
|
||||
def test_dismiss(self, prompt_type, result):
|
||||
self.marionette.start_session({"unhandledPromptBehavior": "dismiss"})
|
||||
self.perform_user_prompt_check(
|
||||
prompt_type, "foo {}".format(prompt_type), result, expected_notify=False
|
||||
)
|
||||
|
||||
@parameterized("alert", "alert", None)
|
||||
@parameterized("confirm", "confirm", False)
|
||||
@parameterized("prompt", "prompt", None)
|
||||
def test_dismiss_and_notify(self, prompt_type, result):
|
||||
self.marionette.start_session({"unhandledPromptBehavior": "dismiss and notify"})
|
||||
self.perform_user_prompt_check(
|
||||
prompt_type, "foo {}".format(prompt_type), result
|
||||
)
|
||||
|
||||
@parameterized("alert", "alert", None)
|
||||
@parameterized("confirm", "confirm", None)
|
||||
@parameterized("prompt", "prompt", None)
|
||||
def test_ignore(self, prompt_type, result):
|
||||
self.marionette.start_session({"unhandledPromptBehavior": "ignore"})
|
||||
self.perform_user_prompt_check(
|
||||
prompt_type, "foo {}".format(prompt_type), result, expected_close=False
|
||||
)
|
||||
|
||||
@parameterized("alert", "alert", None)
|
||||
@parameterized("confirm", "confirm", False)
|
||||
@parameterized("prompt", "prompt", None)
|
||||
def test_default(self, prompt_type, result):
|
||||
self.marionette.start_session({})
|
||||
self.perform_user_prompt_check(
|
||||
prompt_type, "foo {}".format(prompt_type), result
|
||||
)
|
||||
|
|
@ -165,6 +165,8 @@ skip-if = ["os == 'mac'"] # bug 1674411
|
|||
|
||||
["test_typing.py"]
|
||||
|
||||
["test_unhandled_prompt_behavior.py"]
|
||||
|
||||
["test_visibility.py"]
|
||||
|
||||
["test_wait.py"]
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ def fixture_configuration(configuration):
|
|||
|
||||
|
||||
@pytest.fixture(name="new_session")
|
||||
def fixture_new_session(configuration, current_session):
|
||||
def fixture_new_session(request, configuration, current_session):
|
||||
"""Start a new session for tests which themselves test creating new sessions.
|
||||
|
||||
:param body: The content of the body for the new session POST request.
|
||||
|
|
@ -50,22 +50,16 @@ def fixture_new_session(configuration, current_session):
|
|||
is useful for tests which call this fixture multiple times within the
|
||||
same test.
|
||||
"""
|
||||
custom_session = {}
|
||||
|
||||
transport = HTTPWireProtocol(
|
||||
configuration["host"],
|
||||
configuration["port"],
|
||||
url_prefix="/",
|
||||
)
|
||||
|
||||
custom_session = {
|
||||
"capabilities": None,
|
||||
"sessionId": None,
|
||||
"transport": transport,
|
||||
}
|
||||
|
||||
def _delete_session(session_id):
|
||||
response = transport.send("DELETE", "session/{}".format(session_id))
|
||||
if response.status != 200:
|
||||
raise Exception("Failed to delete WebDriver session")
|
||||
transport.send("DELETE", "session/{}".format(session_id))
|
||||
|
||||
def new_session(body, delete_existing_session=False):
|
||||
# If there is an active session from the global session fixture,
|
||||
|
|
@ -74,16 +68,15 @@ def fixture_new_session(configuration, current_session):
|
|||
current_session.end()
|
||||
|
||||
if delete_existing_session:
|
||||
_delete_session(custom_session["sessionId"])
|
||||
_delete_session(custom_session["session"]["sessionId"])
|
||||
|
||||
response = transport.send("POST", "session", body)
|
||||
if response.status == 200:
|
||||
custom_session["sessionId"] = response.body["value"]["sessionId"]
|
||||
custom_session["capabilities"] = response.body["value"]["capabilities"]
|
||||
return response, custom_session
|
||||
custom_session["session"] = response.body["value"]
|
||||
return response, custom_session.get("session", None)
|
||||
|
||||
yield new_session
|
||||
|
||||
if custom_session["sessionId"] is not None:
|
||||
_delete_session(custom_session["sessionId"])
|
||||
if custom_session.get("session") is not None:
|
||||
_delete_session(custom_session["session"]["sessionId"])
|
||||
custom_session = None
|
||||
|
|
|
|||
|
|
@ -30,6 +30,11 @@ valid_data = [
|
|||
("strictFileInteractability", [
|
||||
True, False, None,
|
||||
]),
|
||||
("unhandledPromptBehavior", [
|
||||
"dismiss",
|
||||
"accept",
|
||||
None,
|
||||
]),
|
||||
("test:extension", [
|
||||
None, False, "abc", 123, [],
|
||||
{"key": "value"},
|
||||
|
|
@ -97,18 +102,12 @@ invalid_data = [
|
|||
1, [], {}, "false",
|
||||
]),
|
||||
("unhandledPromptBehavior", [
|
||||
False, 1, [],
|
||||
"Dismiss",
|
||||
"ACCEPT",
|
||||
"ignore ",
|
||||
"dismiss and Notify",
|
||||
"accept and NOTIFY",
|
||||
{"foo": "accept"},
|
||||
{"beforeunload": "bar"},
|
||||
{"alert": None},
|
||||
{"confirm": False},
|
||||
{"prompt": 1},
|
||||
{"beforeUnload": []},
|
||||
1, [], {}, False,
|
||||
"DISMISS",
|
||||
"dismissABC",
|
||||
"Accept",
|
||||
" dismiss",
|
||||
"dismiss ",
|
||||
])
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,201 +0,0 @@
|
|||
# META: timeout=long
|
||||
|
||||
import pytest
|
||||
import webdriver.protocol as protocol
|
||||
|
||||
from tests.support.asserts import assert_error, assert_success
|
||||
|
||||
|
||||
PROMPT_HANDLERS = [
|
||||
"accept",
|
||||
"accept and notify",
|
||||
"dismiss",
|
||||
"dismiss and notify",
|
||||
"ignore",
|
||||
]
|
||||
|
||||
PROMPT_TYPES = [
|
||||
"alert",
|
||||
"beforeUnload",
|
||||
"confirm",
|
||||
"default",
|
||||
"prompt",
|
||||
]
|
||||
|
||||
|
||||
def element_send_keys(session, element, text):
|
||||
return session["transport"].send(
|
||||
"POST",
|
||||
f"/session/{session['sessionId']}/element/{element.id}/value",
|
||||
{"text": text},
|
||||
)
|
||||
|
||||
|
||||
def execute_script(session, script, args=None):
|
||||
if args is None:
|
||||
args = []
|
||||
body = {"script": script, "args": args}
|
||||
|
||||
return session["transport"].send(
|
||||
"POST",
|
||||
f"/session/{session['sessionId']}/execute/sync",
|
||||
body,
|
||||
encoder=protocol.Encoder,
|
||||
decoder=protocol.Decoder,
|
||||
session=session,
|
||||
)
|
||||
|
||||
|
||||
def accept_alert(session):
|
||||
return session["transport"].send(
|
||||
"POST", f"session/{session['sessionId']}/alert/accept"
|
||||
)
|
||||
|
||||
|
||||
def navigate_to(session, url):
|
||||
return session["transport"].send(
|
||||
"POST", f"session/{session['sessionId']}/url", {"url": url}
|
||||
)
|
||||
|
||||
|
||||
def test_unhandled_prompt_behavior_as_string_default(
|
||||
new_session, add_browser_capabilities
|
||||
):
|
||||
response, _ = new_session(
|
||||
{"capabilities": {"alwaysMatch": add_browser_capabilities({})}}
|
||||
)
|
||||
value = assert_success(response)
|
||||
assert value["capabilities"]["unhandledPromptBehavior"] == "dismiss and notify"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("handler", PROMPT_HANDLERS)
|
||||
def test_unhandled_prompt_behavior_as_string(
|
||||
new_session, add_browser_capabilities, handler
|
||||
):
|
||||
response, _ = new_session(
|
||||
{
|
||||
"capabilities": {
|
||||
"alwaysMatch": add_browser_capabilities(
|
||||
{"unhandledPromptBehavior": handler}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
value = assert_success(response)
|
||||
assert value["capabilities"]["unhandledPromptBehavior"] == handler
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"handler,expected_capability,closed,notify",
|
||||
[
|
||||
( # Check the default behavior with no handlers defined
|
||||
{},
|
||||
"dismiss and notify",
|
||||
True,
|
||||
True,
|
||||
),
|
||||
( # Check the default behavior with a custom value defined
|
||||
{"default": "accept"},
|
||||
"accept",
|
||||
True,
|
||||
False,
|
||||
),
|
||||
( # Check the default behavior with a custom value and override defined
|
||||
{"default": "accept", "alert": "ignore"},
|
||||
{"default": "accept", "alert": "ignore"},
|
||||
False,
|
||||
True,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_unhandled_prompt_behavior_as_object_default(
|
||||
new_session, add_browser_capabilities, handler, expected_capability, closed, notify
|
||||
):
|
||||
response, session = new_session(
|
||||
{
|
||||
"capabilities": {
|
||||
"alwaysMatch": add_browser_capabilities(
|
||||
{"unhandledPromptBehavior": handler}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
value = assert_success(response)
|
||||
assert value["capabilities"]["unhandledPromptBehavior"] == expected_capability
|
||||
|
||||
execute_script(session, "alert('foo');")
|
||||
|
||||
# Open user prompt and check if an error gets reported if expected
|
||||
response = execute_script(session, "window.result = 1; return window.result;")
|
||||
if notify:
|
||||
assert_error(response, "unexpected alert open")
|
||||
else:
|
||||
assert_success(response, 1)
|
||||
|
||||
# Check that the user prompt has already closed if one was expected
|
||||
response = accept_alert(session)
|
||||
if closed:
|
||||
assert_error(response, "no such alert")
|
||||
else:
|
||||
assert_success(response, None)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("handler", PROMPT_HANDLERS)
|
||||
@pytest.mark.parametrize("prompt", PROMPT_TYPES)
|
||||
def test_unhandled_prompt_behavior_as_object(
|
||||
new_session, add_browser_capabilities, prompt, handler
|
||||
):
|
||||
response, _ = new_session(
|
||||
{
|
||||
"capabilities": {
|
||||
"alwaysMatch": add_browser_capabilities(
|
||||
{"unhandledPromptBehavior": {prompt: handler}}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
value = assert_success(response)
|
||||
if prompt == "default":
|
||||
# For a single default handler the capability is serialized as a string
|
||||
assert value["capabilities"]["unhandledPromptBehavior"] == handler
|
||||
else:
|
||||
assert value["capabilities"]["unhandledPromptBehavior"] == {prompt: handler}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("handler", PROMPT_HANDLERS)
|
||||
def test_beforeunload_prompts_always_automatically_accepted(
|
||||
new_session, add_browser_capabilities, url, handler
|
||||
):
|
||||
response, session = new_session(
|
||||
{
|
||||
"capabilities": {
|
||||
"alwaysMatch": add_browser_capabilities(
|
||||
{"unhandledPromptBehavior": {"beforeUnload": handler}}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
assert_success(response)
|
||||
|
||||
page_beforeunload = url("/webdriver/tests/support/html/beforeunload.html")
|
||||
navigate_to(session, page_beforeunload)
|
||||
|
||||
response = execute_script(
|
||||
session,
|
||||
"""
|
||||
return document.querySelector("input");
|
||||
""",
|
||||
)
|
||||
elem = assert_success(response)
|
||||
|
||||
# Set sticky user activation
|
||||
response = element_send_keys(session, elem, "foo")
|
||||
assert_success(response)
|
||||
|
||||
# Trigger a beforeunload prompt
|
||||
response = navigate_to(session, "about:blank")
|
||||
assert_success(response)
|
||||
|
||||
# Prompt was automatically accepted
|
||||
response = accept_alert(session)
|
||||
assert_error(response, "no such alert")
|
||||
|
|
@ -180,7 +180,7 @@ impl SpecNewSessionParameters {
|
|||
"proxy" => SpecNewSessionParameters::validate_proxy(value)?,
|
||||
"timeouts" => SpecNewSessionParameters::validate_timeouts(value)?,
|
||||
"unhandledPromptBehavior" => {
|
||||
SpecNewSessionParameters::validate_unhandled_prompt_behavior(value)?
|
||||
SpecNewSessionParameters::validate_unhandled_prompt_behaviour(value)?
|
||||
}
|
||||
x => {
|
||||
if !x.contains(':') {
|
||||
|
|
@ -415,65 +415,19 @@ impl SpecNewSessionParameters {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_unhandled_prompt_behavior(value: &Value) -> WebDriverResult<()> {
|
||||
match value {
|
||||
Value::Object(obj) => {
|
||||
// Unhandled Prompt Behavior type as used by WebDriver BiDi
|
||||
for (key, value) in obj {
|
||||
match &**key {
|
||||
x @ "alert"
|
||||
| x @ "beforeUnload"
|
||||
| x @ "confirm"
|
||||
| x @ "default"
|
||||
| x @ "prompt" => {
|
||||
let behavior = try_opt!(
|
||||
value.as_str(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!(
|
||||
"'{}' unhandledPromptBehavior value is not a string: {}",
|
||||
x, value
|
||||
)
|
||||
);
|
||||
fn validate_unhandled_prompt_behaviour(value: &Value) -> WebDriverResult<()> {
|
||||
let behaviour = try_opt!(
|
||||
value.as_str(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("unhandledPromptBehavior is not a string: {}", value)
|
||||
);
|
||||
|
||||
match behavior {
|
||||
"accept" | "accept and notify" | "dismiss"
|
||||
| "dismiss and notify" | "ignore" => {}
|
||||
x => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!(
|
||||
"'{}' unhandledPromptBehavior value is invalid: {}",
|
||||
x, behavior
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
x => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("Invalid unhandledPromptBehavior entry: {}", x),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Value::String(behavior) => match behavior.as_str() {
|
||||
"accept" | "accept and notify" | "dismiss" | "dismiss and notify" | "ignore" => {}
|
||||
x => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("Invalid unhandledPromptBehavior value: {}", x),
|
||||
))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
match behaviour {
|
||||
"accept" | "accept and notify" | "dismiss" | "dismiss and notify" | "ignore" => {}
|
||||
x => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!(
|
||||
"unhandledPromptBehavior is neither an object nor a string: {}",
|
||||
value
|
||||
),
|
||||
format!("Invalid unhandledPromptBehavior value: {}", x),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -839,41 +793,6 @@ mod tests {
|
|||
assert_de(&caps, json);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_unhandled_prompt_behavior() {
|
||||
fn validate_prompt_behavior(v: Value) -> WebDriverResult<()> {
|
||||
SpecNewSessionParameters::validate_unhandled_prompt_behavior(&v)
|
||||
}
|
||||
|
||||
// capability as string
|
||||
validate_prompt_behavior(json!("accept")).unwrap();
|
||||
validate_prompt_behavior(json!("accept and notify")).unwrap();
|
||||
validate_prompt_behavior(json!("dismiss")).unwrap();
|
||||
validate_prompt_behavior(json!("dismiss and notify")).unwrap();
|
||||
validate_prompt_behavior(json!("ignore")).unwrap();
|
||||
assert!(validate_prompt_behavior(json!("foo")).is_err());
|
||||
|
||||
// capability as object
|
||||
let types = ["alert", "beforeUnload", "confirm", "default", "prompt"];
|
||||
let handlers = [
|
||||
"accept",
|
||||
"accept and notify",
|
||||
"dismiss",
|
||||
"dismiss and notify",
|
||||
"ignore",
|
||||
];
|
||||
for promptType in types {
|
||||
assert!(validate_prompt_behavior(json!({promptType: "foo"})).is_err());
|
||||
for handler in handlers {
|
||||
validate_prompt_behavior(json!({promptType: handler})).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
for handler in handlers {
|
||||
assert!(validate_prompt_behavior(json!({"foo": handler})).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_proxy() {
|
||||
fn validate_proxy(v: Value) -> WebDriverResult<()> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue