Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: handle safari alert for iOS 13.4 #1174

Merged
merged 2 commits into from
Feb 21, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 26 additions & 17 deletions lib/commands/alert.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { errors, isErrorType } from 'appium-base-driver';
import { errors } from 'appium-base-driver';
import { util } from 'appium-support';
import _ from 'lodash';

Expand Down Expand Up @@ -94,32 +94,39 @@ commands.mobileHandleAlert = async function mobileHandleAlert (opts = {}) {
};

helpers.getAlert = async function getAlert () {
const possibleAlert = await this.findNativeElementOrElements('class name',
// the alert ought to be the first scroll view, but we do not want to
// wait for any implicit wait so get multiple
const scrollViews = await this.findNativeElementOrElements('class name',
'XCUIElementTypeScrollView', true);
if (possibleAlert.length !== 1) {
if (scrollViews.length !== 1) {
throw new errors.NoAlertOpenError();
}

// if there is an alert, it will be the first scroll view
const alert = scrollViews[0];

// within the alert there should be one or two buttons (no more, no less)
const possibleAlertButtons = await this.findNativeElementOrElements('class name',
'XCUIElementTypeButton', true, util.unwrapElement(possibleAlert[0]));
'XCUIElementTypeButton', true, util.unwrapElement(alert));
if (possibleAlertButtons.length < 1 || possibleAlertButtons.length > 2) {
throw new errors.NoAlertOpenError();
}

const assertButtonName = async (button, expectedName) => {
// determine that the name of the button is what is expected
const assertButtonName = async (button, expectedName = '') => {
button = util.unwrapElement(button);
const name = await this.proxyCommand(`/element/${button}/attribute/name`, 'GET');
if (_.isNil(name) || name.toLowerCase() !== expectedName) {
if (name?.toLowerCase() !== expectedName.toLowerCase()) {
throw new errors.NoAlertOpenError();
}
};

const alert = possibleAlert[0];
if (possibleAlertButtons.length === 1) {
// make sure the button is 'Close'
const closeButton = possibleAlertButtons[0];
await assertButtonName(closeButton, 'close');

// add a function on the alert to close by clicking the 'Close' button
alert.close = async () => {
await this.proxyCommand(`/element/${util.unwrapElement(closeButton)}/click`, 'POST');
};
Expand All @@ -130,28 +137,30 @@ helpers.getAlert = async function getAlert () {
const okButton = possibleAlertButtons[1];
await assertButtonName(okButton, 'ok');

// add cancel function to the alert, clicking the 'Cancel' button
alert.cancel = async () => {
await this.proxyCommand(`/element/${util.unwrapElement(cancelButton)}/click`, 'POST');
};
// add ok function to the alert, clicking the 'OK' button
alert.ok = async () => {
await this.proxyCommand(`/element/${util.unwrapElement(okButton)}/click`, 'POST');
};
}

// add getText function to the alert, getting the value of the correct element
alert.getText = async () => {
let textView = await this.findNativeElementOrElements('class name', 'XCUIElementTypeTextView', false, util.unwrapElement(alert));
return await this.proxyCommand(`/element/${util.unwrapElement(textView)}/attribute/value`, 'GET');
// iOS up to 13.3 will report a single text view, while 13.4 will have two
// but the _last_ one will be the one presenting the text of the alert
const textViews = await this.findNativeElementOrElements('class name', 'XCUIElementTypeTextView', true, util.unwrapElement(alert));
return await this.proxyCommand(`/element/${util.unwrapElement(_.last(textViews))}/attribute/value`, 'GET');
};
// add setText function to the alert, setting the value of the text field element
alert.setText = async (value) => {
try {
let textView = await this.findNativeElementOrElements('class name', 'XCUIElementTypeTextField', false, util.unwrapElement(alert));
await this.proxyCommand(`/element/${util.unwrapElement(textView)}/value `, 'POST', {value});
} catch (err) {
if (isErrorType(err, errors.NoSuchElementError)) {
throw new Error('Tried to set text of an alert that was not a prompt');
}
throw err;
const textViews = await this.findNativeElementOrElements('class name', 'XCUIElementTypeTextField', true, util.unwrapElement(alert));
if (textViews.length === 0) {
throw new Error('Tried to set text of an alert that was not a prompt');
}
await this.proxyCommand(`/element/${util.unwrapElement(textViews[0])}/value `, 'POST', {value});
};

return alert;
Expand Down