UNPKG

appium-xcuitest-driver

Version:

Appium driver for iOS using XCUITest for backend

434 lines 21.7 kB
"use strict"; 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