appium-xcuitest-driver
Version:
Appium driver for iOS using XCUITest for backend
434 lines • 21.7 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const bluebird_1 = __importDefault(require("bluebird"));
const lodash_1 = __importDefault(require("lodash"));
const asyncbox_1 = require("asyncbox");
const desired_1 = require("../desired");
const element_1 = require("../helpers/element");
const session_1 = require("../helpers/session");
const support_1 = require("appium/support");
const chai_1 = __importStar(require("chai"));
const chai_as_promised_1 = __importDefault(require("chai-as-promised"));
chai_1.default.use(chai_as_promised_1.default);
const TEST_PAUSE_DURATION = 500;
const PV_ABOVE_13 = support_1.util.compareVersions(desired_1.PLATFORM_VERSION, '>=', '13.0');
// there are some differences in the apps
const FIRST_ELEMENT = PV_ABOVE_13 ? 'Activity Indicators' : 'Action Sheets';
const APP_TITLE = PV_ABOVE_13 ? 'UIKitCatalog' : 'UICatalog';
describe('XCUITestDriver - find -', function () {
this.timeout(session_1.MOCHA_TIMEOUT);
let driver;
before(async function () {
const uiCatalogCaps = await (0, desired_1.getUICatalogCaps)();
driver = await (0, session_1.initSession)(uiCatalogCaps);
});
after(async function () {
await (0, session_1.deleteSession)();
});
// establish that the basic things work as we imagine
describe('basics', function () {
let el1;
before(async function () {
el1 = await driver.$('~Buttons');
(0, chai_1.expect)(el1.elementId).to.exist;
});
it('should find an element within descendants', async function () {
const el2 = await el1.$('XCUIElementTypeStaticText');
(0, chai_1.expect)(await el2.getAttribute('name')).to.contain('Buttons');
});
it('should not find an element not within itself', async function () {
const el2 = await el1.$('class name', 'XCUIElementTypeNavigationBar');
(0, chai_1.expect)(el2.error.error).to.equal('no such element');
});
it.skip('should find some elements within itself', async function () {
const els = await el1.$$('XCUIElementTypeStaticText');
(0, chai_1.expect)(els).to.have.length(2);
});
it('should not find elements not within itself', async function () {
const els = await el1.$$('XCUIElementTypeNavigationBar');
(0, chai_1.expect)(els).to.have.length(0);
});
});
// make sure that elements are mixed up
describe.skip('no mix up', function () {
after(async function () {
await driver.back();
});
it('should not allow found elements to be mixed up', async function () {
let table = await driver.$('XCUIElementTypeTable');
const el1 = await table.$('XCUIElementTypeStaticText');
const el1Name = await el1.getAttribute('name');
await el1.click();
// we need a hard pause, because if we haven't shifted views yet
// we will have the previous elements, so the get command will be fulfilled.
await bluebird_1.default.delay(1000);
await driver.setTimeout({ implicit: 5000 });
table = await driver.$('XCUIElementTypeTable');
const el2 = await driver.$('XCUIElementTypeStaticText');
const el2Name = await el2.getAttribute('name');
(0, chai_1.expect)(el1).to.not.equal(el2);
(0, chai_1.expect)(el1Name).to.not.equal(el2Name);
// el1 is gone, so it doesn't have a name anymore
(0, chai_1.expect)(await el1.getAttribute('name')).to.equal('');
});
});
describe('by id', function () {
it('should find a single element by id', async function () {
const el = await driver.$('~Alert Views');
(0, chai_1.expect)(el.elementId).to.exist;
});
it('should find a single element by id wrapped in array for multi', async function () {
const els = await driver.$$('~Alert Views');
(0, chai_1.expect)(els).to.have.length(1);
});
it('should first attempt to match accessibility id', async function () {
const el = await driver.$('~Alert Views');
(0, chai_1.expect)(await el.getAttribute('label')).to.equal('Alert Views');
});
it('should attempt to match by string if no accessibility id matches', async function () {
const el = await driver.$('~Alert Views');
(0, chai_1.expect)(await el.getAttribute('label')).to.equal('Alert Views');
});
it.skip('should use a localized string if the id is a localization key', async function () {
const el = await driver.$('#main.button.computeSum');
(0, chai_1.expect)(await el.getAttribute('label')).to.equal('Compute Sum');
});
it.skip('should be able to return multiple matches', async function () {
const els = await driver.$$('#Cell');
(0, chai_1.expect)(els.length).to.be.greaterThan(1);
});
});
describe('by xpath', function () {
describe('individual calls', function () {
before(async function () {
// before anything, try to go back
// otherwise the tests will fail erroneously
await driver.back();
// and make sure we are at the top of the page
try {
await driver.execute('mobile: scroll', { direction: 'up' });
}
catch { }
});
beforeEach(async function () {
// go into the right page
await (0, asyncbox_1.retryInterval)(10, 500, async () => {
const el = await driver.$('~Buttons');
await el.click();
(0, chai_1.expect)(await driver.$$('~Button')).to.have.length.at.least(1);
});
});
afterEach(async function () {
await driver.back();
});
it('should respect implicit wait', async function () {
await driver.setTimeout({ implicit: 5000 });
const begin = Date.now();
const el = await driver.$('//something_not_there');
(0, chai_1.expect)(el.error.error).to.equal('no such element');
(0, chai_1.expect)(Date.now() - begin).to.be.above(5000);
});
it.skip('should return the last button', async function () {
const el = await driver.$('//XCUIElementTypeButton[last()]');
(0, chai_1.expect)(await el.getAttribute('name')).to.equal('Button'); // this is the name of the last button
});
it('should return a single element', async function () {
const el = await driver.$('//XCUIElementTypeButton');
(0, chai_1.expect)(await el.getAttribute('label')).to.equal(APP_TITLE);
});
it('should return multiple elements', async function () {
const els = await driver.$$('//XCUIElementTypeButton');
(0, chai_1.expect)(els).to.have.length.above(4);
});
it('should filter by name', async function () {
const el = await driver.$(`//XCUIElementTypeButton[@name='X Button']`);
(0, chai_1.expect)(await el.getAttribute('name')).to.equal('X Button');
});
it('should know how to restrict root-level elements', async function () {
const el = await driver.$('/XCUIElementTypeButton');
(0, chai_1.expect)(el.error.error).to.equal('no such element');
});
it('should search an extended path by child', async function () {
// pause a moment or the next command gets stuck getting the xpath :(
await bluebird_1.default.delay(TEST_PAUSE_DURATION);
let el;
try {
el = await driver.$('//XCUIElementTypeNavigationBar/XCUIElementTypeStaticText');
}
catch {
el = await driver.$('//XCUIElementTypeNavigationBar/XCUIElementTypeOther');
}
(0, chai_1.expect)(await el.getAttribute('name')).to.equal('Buttons');
});
it('should search an extended path by descendant', async function () {
const els = await driver.$$('//XCUIElementTypeTable//XCUIElementTypeButton');
const texts = await bluebird_1.default.all(lodash_1.default.map(els, (el) => el.getAttribute('name')));
(0, chai_1.expect)(texts).to.not.include('UICatalog');
(0, chai_1.expect)(texts).to.not.include('UIKitCatalog');
(0, chai_1.expect)(texts).to.include('X Button');
});
it.skip('should filter by indices', async function () {
const el = await driver.$('//XCUIElementTypeTable[1]//XCUIElementTypeButton[4]');
(0, chai_1.expect)(await el.getAttribute('name')).to.equal('X Button');
});
it('should filter by partial text', async function () {
const el = await driver.$(`//XCUIElementTypeTable//XCUIElementTypeButton[contains(@name, 'X')]`);
(0, chai_1.expect)(await el.getAttribute('name')).to.equal('X Button');
});
});
describe.skip('multiple calls', function () {
const runs = 5;
before(async function () {
// go into the right page
const el = await driver.$('~Buttons');
await el.click();
});
after(async function () {
await driver.back();
});
const test = function (path, minLength) {
return function () {
it('should not crash', async function () {
const els = await driver.$$(path);
(0, chai_1.expect)(els).to.have.length.above(minLength);
});
};
};
describe.skip('finding specific path', function () {
for (let n = 0; n < runs; n++) {
describe(`test ${n + 1}`, test('//XCUIElementTypeApplication[0]/XCUIElementTypeWindow[0]', 17));
}
});
describe('finding //*', function () {
for (let n = 0; n < runs; n++) {
describe(`test ${n + 1}`, test('//*', 52));
}
});
});
});
describe('by accessibility id', function () {
afterEach(async function () {
await driver.back();
});
it('should find one element', async function () {
const el1 = await driver.$('~Alert Views');
await el1.click();
const el2 = await driver.$('~Okay / Cancel');
(0, chai_1.expect)(await el2.getAttribute('name')).to.equal('Okay / Cancel');
});
it.skip('should find several elements', async function () {
const el1 = await driver.$('~Alert Views');
await el1.click();
const els = await driver.$$('~Okay / Cancel');
(0, chai_1.expect)(els).to.have.length(2);
});
it('should find an element beneath another element', async function () {
const el1 = await driver.$('XCUIElementTypeTable');
const el2 = await el1.$('~Alert Views');
(0, chai_1.expect)(el2.elementId).to.exist;
});
});
describe('by class name', function () {
afterEach(async function () {
await driver.back();
});
it('should return all image elements with internally generated ids', async function () {
const el = await driver.$('~Image View');
await el.click();
const els = await driver.$$('XCUIElementTypeImage');
(0, chai_1.expect)(els.length).to.be.above(0);
for (const el of els) {
(0, chai_1.expect)(el.elementId).to.exist;
}
});
describe('textfield case', function () {
it('should find only one textfield', async function () {
// TODO: this works locally but fails in CI.
const uiCatalogCaps = await (0, desired_1.getUICatalogCaps)();
if (process.env.CI &&
(0, desired_1.extractCapabilityValue)(uiCatalogCaps, 'appium:platformVersion') === '10.3') {
return this.skip();
}
const el1 = await driver.$('~Alert Views');
await el1.click();
const el2 = await driver.$('~Okay / Cancel');
const els = await el2.$$('XCUIElementTypeStaticText');
(0, chai_1.expect)(els).to.have.length(1);
});
});
});
describe('duplicate text field', function () {
before(async function () {
try {
const el = await driver.$('~Text Fields');
await driver.execute('mobile: scroll', { element: el.elementId, toVisible: true });
}
catch { }
});
afterEach(async function () {
await driver.back();
});
after(async function () {
// make sure we scroll back so as not to mess up subsequent tests
const el = await driver.$('~Alert Views');
await driver.execute('mobile: scroll', { element: el.elementId, toVisible: true });
});
it('should find only one element per text field', async function () {
await driver.$('~Text Fields').click();
const els = await driver.$$('XCUIElementTypeTextField');
(0, chai_1.expect)(els).to.have.length(PV_ABOVE_13 ? 5 : 4);
});
it('should find only one element per secure text field', async function () {
await driver.$('~Text Fields').click();
const els = await driver.$$('XCUIElementTypeSecureTextField');
(0, chai_1.expect)(els).to.have.length(1);
});
});
describe('by predicate string', function () {
before(async function () {
// if we don't pause, WDA freaks out sometimes, especially on fast systems
await bluebird_1.default.delay(TEST_PAUSE_DURATION);
});
it('should find invisible elements', async function () {
const selector = 'visible = 0';
const els = await driver.$$(`${element_1.PREDICATE_SEARCH}:${selector}`);
(0, chai_1.expect)(els).to.have.length.above(0);
});
it('should find elements with widths above 0', async function () {
const selector = 'wdRect.width >= 0';
const els = await driver.$$(`${element_1.PREDICATE_SEARCH}:${selector}`);
(0, chai_1.expect)(els).to.have.length.above(0);
});
it('should find elements with widths between 100 and 200', async function () {
const selector = 'wdRect.width BETWEEN {100,200}';
const els = await driver.$$(`${element_1.PREDICATE_SEARCH}:${selector}`);
(0, chai_1.expect)(els).to.have.length.above(0);
});
it('should find elements that end in the word "View" in the name', async function () {
const selector = "wdName LIKE '* View'";
const els = await driver.$$(`${element_1.PREDICATE_SEARCH}:${selector}`);
(0, chai_1.expect)(els).to.have.length.above(1);
});
it('should find elements that have x and y coordinates greater than 0', async function () {
const selector = 'wdRect.x >= 0 AND wdRect.y >= 0';
const els = await driver.$$(`${element_1.PREDICATE_SEARCH}:${selector}`);
(0, chai_1.expect)(els).to.have.length.above(1);
});
});
describe('by class chain', function () {
before(async function () {
// if we don't pause, WDA freaks out sometimes, especially on fast systems
await bluebird_1.default.delay(TEST_PAUSE_DURATION);
});
it('should find elements', async function () {
const selector = 'XCUIElementTypeWindow';
const els = await driver.$$(`${element_1.CLASS_CHAIN_SEARCH}:${selector}`);
(0, chai_1.expect)(els).to.have.length.above(0);
});
it('should find child elements', async function () {
const selector = 'XCUIElementTypeWindow/*';
const els = await driver.$$(`${element_1.CLASS_CHAIN_SEARCH}:${selector}`);
(0, chai_1.expect)(els).to.have.length.above(0);
});
it('should find elements with index', async function () {
const selector = 'XCUIElementTypeWindow[1]/*';
const els = await driver.$$(`${element_1.CLASS_CHAIN_SEARCH}:${selector}`);
(0, chai_1.expect)(els).to.have.length.above(0);
});
it('should find elements with negative index', async function () {
const selector = 'XCUIElementTypeWindow/*[-1]';
const els = await driver.$$(`${element_1.CLASS_CHAIN_SEARCH}:${selector}`);
(0, chai_1.expect)(els).to.have.length(1);
});
});
describe('by css selector', function () {
before(async function () {
// if we don't pause, WDA freaks out sometimes, especially on fast systems
await bluebird_1.default.delay(TEST_PAUSE_DURATION);
});
it('should find cell types', async function () {
const cellEls = await driver.$$('cell');
(0, chai_1.expect)(cellEls).to.have.length.above(1);
});
it('should find elements', async function () {
const els = await driver.$$('window');
(0, chai_1.expect)(els).to.have.length.above(0);
});
it('should find child elements', async function () {
const els = await driver.$$('window > *');
(0, chai_1.expect)(els).to.have.length.above(0);
});
it('should find elements with index', async function () {
const els = await driver.$$('window:nth-child(1) > *');
(0, chai_1.expect)(els).to.have.length.above(0);
});
it('should find elements with negative index', async function () {
const els = await driver.$$('window > *:nth-child(-1)');
(0, chai_1.expect)(els).to.have.length(1);
});
it('should work with a nested CSS selector', async function () {
const imageViewButtons = await driver.$$('cell > staticText[value="Image View"]');
(0, chai_1.expect)(imageViewButtons).to.have.length(1);
});
});
describe('magic first visible child xpath', function () {
it('should find the first visible child of an element', async function () {
const el = await driver.$('XCUIElementTypeTable');
const child = await el.$('/*[@firstVisible="true"]');
await (0, chai_1.expect)(child.getAttribute('type')).to.eventually.eql('XCUIElementTypeCell');
// do another call and double-check the different quote/spacing works
const grandchild = await child.$("/*[@firstVisible = 'true']");
const type = await grandchild.getAttribute('type');
if (type === 'XCUIElementTypeStaticText') {
await (0, chai_1.expect)(grandchild.getAttribute('name')).to.eventually.eql(FIRST_ELEMENT);
}
else {
(0, chai_1.expect)(type).to.equal('XCUIElementTypeOther');
}
});
});
describe('magic scrollable descendents xpath', function () {
it('should find any scrollable elements', async function () {
const els = await driver.$$('//*[@scrollable="true"]');
(0, chai_1.expect)(els).to.have.length(1);
await (0, chai_1.expect)(els[0].getAttribute('type')).to.eventually.eql('XCUIElementTypeTable');
});
});
});
//# sourceMappingURL=find-e2e-specs.js.map