123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977 |
- // Copyright 2014 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- import {ElementObject} from './element_object.js';
- import {KeyModifiers} from './key_modifiers.js';
- import {getCaller, pending, repeatUntil, sendTestMessage} from './test_util.js';
- import {VolumeManagerCommonVolumeType} from './volume_manager_common_volume_type.js';
- /**
- * When step by step tests are enabled, turns on automatic step() calls. Note
- * that if step() is defined at the time of this call, invoke it to start the
- * test auto-stepping ball rolling.
- */
- window.autoStep = () => {
- window.autostep = window.autostep || false;
- if (!window.autostep) {
- window.autostep = true;
- }
- if (window.autostep && typeof window.step === 'function') {
- window.step();
- }
- };
- /**
- * This error type is thrown by executeJsInPreviewTagSwa_ if the script to
- * execute in the untrusted context produces an error.
- */
- export class ExecuteScriptError extends Error {
- constructor(message) {
- super(message);
- this.name = 'ExecuteScriptError';
- }
- }
- /**
- * Class to manipulate the window in the remote extension.
- */
- export class RemoteCall {
- /**
- * @param {string} origin ID of the app to be manipulated.
- */
- constructor(origin) {
- this.origin_ = origin;
- /**
- * Tristate holding the cached result of isStepByStepEnabled_().
- * @type {?boolean}
- */
- this.cachedStepByStepEnabled_ = null;
- }
- /**
- * Checks whether step by step tests are enabled or not.
- * @private
- * @return {!Promise<boolean>}
- */
- async isStepByStepEnabled_() {
- if (this.cachedStepByStepEnabled_ === null) {
- this.cachedStepByStepEnabled_ = await new Promise((fulfill) => {
- chrome.commandLinePrivate.hasSwitch(
- 'enable-file-manager-step-by-step-tests', fulfill);
- });
- }
- return this.cachedStepByStepEnabled_;
- }
- /**
- * Sends a test |message| to the test code running in the File Manager.
- * @param {!Object} message
- * @return {!Promise<*>} A promise which when fulfilled returns the
- * result of executing test code with the given message.
- */
- sendMessage(message) {
- return new Promise((fulfill) => {
- chrome.runtime.sendMessage(this.origin_, message, {}, fulfill);
- });
- }
- /**
- * Calls a remote test util in the Files app's extension. See:
- * registerRemoteTestUtils in test_util_base.js.
- *
- * @param {string} func Function name.
- * @param {?string} appId App window Id or null for functions not requiring a
- * window.
- * @param {?Array<*>=} args Array of arguments.
- * @param {function(*)=} opt_callback Callback handling the function's result.
- * @return {!Promise} Promise to be fulfilled with the result of the remote
- * utility.
- */
- async callRemoteTestUtil(func, appId, args, opt_callback) {
- const stepByStep = await this.isStepByStepEnabled_();
- let finishCurrentStep;
- if (stepByStep) {
- while (window.currentStep) {
- await window.currentStep;
- }
- window.currentStep = new Promise(resolve => {
- finishCurrentStep = () => {
- window.currentStep = null;
- resolve();
- };
- });
- console.info('Executing: ' + func + ' on ' + appId + ' with args: ');
- console.info(args);
- if (window.autostep !== true) {
- await new Promise((onFulfilled) => {
- console.info('Type step() to continue...');
- /** @type {?function()} */
- window.step = function() {
- window.step = null;
- onFulfilled();
- };
- });
- } else {
- console.info('Auto calling step() ...');
- }
- }
- const response = await this.sendMessage({func, appId, args});
- if (stepByStep) {
- console.info('Returned value:');
- console.info(JSON.stringify(response));
- finishCurrentStep();
- }
- if (opt_callback) {
- opt_callback(response);
- }
- return response;
- }
- /**
- * Waits until a window having the given ID prefix appears.
- * @param {string} windowIdPrefix ID prefix of the requested window.
- * @return {!Promise<string>} promise Promise to be fulfilled with a found
- * window's ID.
- */
- waitForWindow(windowIdPrefix) {
- const caller = getCaller();
- const windowIdRegex = new RegExp(windowIdPrefix);
- return repeatUntil(async () => {
- const windows = await this.callRemoteTestUtil('getWindows', null, []);
- for (const id in windows) {
- if (id.indexOf(windowIdPrefix) === 0 || windowIdRegex.test(id)) {
- return id;
- }
- }
- return pending(
- caller, 'Window with the prefix %s is not found.', windowIdPrefix);
- });
- }
- /**
- * Closes a window and waits until the window is closed.
- *
- * @param {string} appId App window Id.
- * @return {Promise} promise Promise to be fulfilled with the result (true:
- * success, false: failed).
- */
- async closeWindowAndWait(appId) {
- const caller = getCaller();
- // Closes the window.
- if (!await this.callRemoteTestUtil('closeWindow', null, [appId])) {
- // Returns false when the closing is failed.
- return false;
- }
- return repeatUntil(async () => {
- const windows = await this.callRemoteTestUtil('getWindows', null, []);
- for (const id in windows) {
- if (id === appId) {
- // Window is still available. Continues waiting.
- return pending(
- caller, 'Window with the prefix %s is not found.', appId);
- }
- }
- // Window is not available. Closing is done successfully.
- return true;
- });
- }
- /**
- * Waits until the window turns to the given size.
- * @param {string} appId App window Id.
- * @param {number} width Requested width in pixels.
- * @param {number} height Requested height in pixels.
- */
- waitForWindowGeometry(appId, width, height) {
- const caller = getCaller();
- return repeatUntil(async () => {
- const windows = await this.callRemoteTestUtil('getWindows', null, []);
- if (!windows[appId]) {
- return pending(caller, 'Window %s is not found.', appId);
- }
- if (windows[appId].outerWidth !== width ||
- windows[appId].outerHeight !== height) {
- return pending(
- caller, 'Expected window size is %j, but it is %j',
- {width: width, height: height}, windows[appId]);
- }
- });
- }
- /**
- * Waits for the specified element appearing in the DOM.
- * @param {string} appId App window Id.
- * @param {string|!Array<string>} query Query to specify the element.
- * If query is an array, |query[0]| specifies the first
- * element(s), |query[1]| specifies elements inside the shadow DOM of
- * the first element, and so on.
- * @return {Promise<ElementObject>} Promise to be fulfilled when the element
- * appears.
- */
- waitForElement(appId, query) {
- return this.waitForElementStyles(appId, query, []);
- }
- /**
- * Waits for the specified element appearing in the DOM.
- * @param {string} appId App window Id.
- * @param {string|!Array<string>} query Query to specify the element.
- * If query is an array, |query[0]| specifies the first
- * element(s), |query[1]| specifies elements inside the shadow DOM of
- * the first element, and so on.
- * @param {!Array<string>} styleNames List of CSS property name to be
- * obtained. NOTE: Causes element style re-calculation.
- * @return {Promise<ElementObject>} Promise to be fulfilled when the element
- * appears.
- */
- waitForElementStyles(appId, query, styleNames) {
- const caller = getCaller();
- return repeatUntil(async () => {
- const elements = await this.callRemoteTestUtil(
- 'deepQueryAllElements', appId, [query, styleNames]);
- if (elements && elements.length > 0) {
- return /** @type {ElementObject} */ (elements[0]);
- }
- return pending(caller, 'Element %s is not found.', query);
- });
- }
- /**
- * Waits for a remote test function to return a specific result.
- *
- * @param {string} funcName Name of remote test function to be executed.
- * @param {?string} appId App window Id.
- * @param {function(Object):boolean|boolean|Object} expectedResult An value to
- * be checked against the return value of |funcName| or a callback that
- * receives the return value of |funcName| and returns true if the result
- * is the expected value.
- * @param {?Array<*>=} args Arguments to be provided to |funcName| when
- * executing it.
- * @return {Promise} Promise to be fulfilled when the |expectedResult| is
- * returned from |funcName| execution.
- */
- waitFor(funcName, appId, expectedResult, args) {
- const caller = getCaller();
- args = args || [];
- return repeatUntil(async () => {
- const result = await this.callRemoteTestUtil(funcName, appId, args);
- if (typeof expectedResult === 'function' && expectedResult(result)) {
- return result;
- }
- if (expectedResult === result) {
- return result;
- }
- const msg = 'waitFor: Waiting for ' +
- `${funcName} to return ${expectedResult}, ` +
- `but got ${JSON.stringify(result)}.`;
- return pending(caller, msg);
- });
- }
- /**
- * Waits for the specified element leaving from the DOM.
- * @param {string} appId App window Id.
- * @param {string|!Array<string>} query Query to specify the element.
- * If query is an array, |query[0]| specifies the first
- * element(s), |query[1]| specifies elements inside the shadow DOM of
- * the first element, and so on.
- * @return {Promise} Promise to be fulfilled when the element is lost.
- */
- waitForElementLost(appId, query) {
- const caller = getCaller();
- return repeatUntil(async () => {
- const elements =
- await this.callRemoteTestUtil('deepQueryAllElements', appId, [query]);
- if (elements.length > 0) {
- return pending(caller, 'Elements %j still exists.', elements);
- }
- return true;
- });
- }
- /**
- * Wait for the |query| to match |count| elements.
- *
- * @param {string} appId App window Id.
- * @param {string|!Array<string>} query Query to specify the element.
- * If query is an array, |query[0]| specifies the first
- * element(s), |query[1]| specifies elements inside the shadow DOM of
- * the first element, and so on.
- * @param {number} count The expected element match count.
- * @return {Promise} Promise to be fulfilled on success.
- */
- waitForElementsCount(appId, query, count) {
- const caller = getCaller();
- return repeatUntil(async () => {
- const expect = `Waiting for [${query}] to match ${count} elements`;
- const result =
- await this.callRemoteTestUtil('countElements', appId, [query, count]);
- return !result ? pending(caller, expect) : true;
- });
- }
- /**
- * Sends a fake key down event.
- * @param {string} appId App window Id.
- * @param {string|!Array<string>} query Query to specify the element.
- * If query is an array, |query[0]| specifies the first
- * element(s), |query[1]| specifies elements inside the shadow DOM of
- * the first element, and so on.
- * @param {string} key DOM UI Events Key value.
- * @param {boolean} ctrlKey Control key flag.
- * @param {boolean} shiftKey Shift key flag.
- * @param {boolean} altKey Alt key flag.
- * @return {Promise} Promise to be fulfilled or rejected depending on the
- * result.
- */
- async fakeKeyDown(appId, query, key, ctrlKey, shiftKey, altKey) {
- const result = await this.callRemoteTestUtil(
- 'fakeKeyDown', appId, [query, key, ctrlKey, shiftKey, altKey]);
- if (result) {
- return true;
- } else {
- throw new Error('Fail to fake key down.');
- }
- }
- /**
- * Sets the given input text on the element identified by the query.
- * @param {string} appId App window ID.
- * @param {string|!Array<string>} selector The query selector to locate
- * the element
- * @param {string} text The text to be set on the element.
- */
- async inputText(appId, selector, text) {
- chrome.test.assertTrue(
- await this.callRemoteTestUtil('inputText', appId, [selector, text]));
- }
- /**
- * Gets file entries just under the volume.
- *
- * @param {VolumeManagerCommonVolumeType} volumeType Volume type.
- * @param {Array<string>} names File name list.
- * @return {Promise} Promise to be fulfilled with file entries or rejected
- * depending on the result.
- */
- getFilesUnderVolume(volumeType, names) {
- return this.callRemoteTestUtil(
- 'getFilesUnderVolume', null, [volumeType, names]);
- }
- /**
- * Waits for a single file.
- * @param {VolumeManagerCommonVolumeType} volumeType Volume type.
- * @param {string} name File name.
- * @return {!Promise} Promise to be fulfilled when the file had found.
- */
- waitForAFile(volumeType, name) {
- const caller = getCaller();
- return repeatUntil(async () => {
- if ((await this.getFilesUnderVolume(volumeType, [name])).length === 1) {
- return true;
- }
- return pending(caller, '"' + name + '" is not found.');
- });
- }
- /**
- * Shorthand for clicking an element.
- * @param {string} appId App window Id.
- * @param {string|!Array<string>} query Query to specify the element.
- * If query is an array, |query[0]| specifies the first
- * element(s), |query[1]| specifies elements inside the shadow DOM of
- * the first element, and so on.
- * @param {KeyModifiers=} opt_keyModifiers Object
- * @return {Promise} Promise to be fulfilled with the clicked element.
- */
- async waitAndClickElement(appId, query, opt_keyModifiers) {
- const element = await this.waitForElement(appId, query);
- const result = await this.callRemoteTestUtil(
- 'fakeMouseClick', appId, [query, opt_keyModifiers]);
- chrome.test.assertTrue(result, 'mouse click failed.');
- return element;
- }
- /**
- * Shorthand for right-clicking an element.
- * @param {string} appId App window Id.
- * @param {string|!Array<string>} query Query to specify the element.
- * If query is an array, |query[0]| specifies the first
- * element(s), |query[1]| specifies elements inside the shadow DOM of
- * the first element, and so on.
- * @param {KeyModifiers=} opt_keyModifiers Object
- * @return {Promise} Promise to be fulfilled with the clicked element.
- */
- async waitAndRightClick(appId, query, opt_keyModifiers) {
- const element = await this.waitForElement(appId, query);
- const result = await this.callRemoteTestUtil(
- 'fakeMouseRightClick', appId, [query, opt_keyModifiers]);
- chrome.test.assertTrue(result, 'mouse right-click failed.');
- return element;
- }
- /**
- * Shorthand for focusing an element.
- * @param {string} appId App window Id.
- * @param {!Array<string>} query Query to specify the element to be focused.
- * @return {Promise} Promise to be fulfilled with the focused element.
- */
- async focus(appId, query) {
- const element = await this.waitForElement(appId, query);
- const result = await this.callRemoteTestUtil('focus', appId, query);
- chrome.test.assertTrue(result, 'focus failed.');
- return element;
- }
- /**
- * Simulate Click in the UI in the middle of the element.
- * @param{string} appId App window ID contains the element. NOTE: The click is
- * simulated on most recent window in the window system.
- * @param {string|!Array<string>} query Query to the element to be clicked.
- * @return {!Promise} A promise fulfilled after the click event.
- */
- async simulateUiClick(appId, query) {
- const element = /* @type {!Object} */ (
- await this.waitForElementStyles(appId, query, ['display']));
- chrome.test.assertTrue(!!element, 'element for simulateUiClick not found');
- // Find the middle of the element.
- const x =
- Math.floor(element['renderedLeft'] + (element['renderedWidth'] / 2));
- const y =
- Math.floor(element['renderedTop'] + (element['renderedHeight'] / 2));
- return sendTestMessage(
- {appId, name: 'simulateClick', 'clickX': x, 'clickY': y});
- }
- }
- /**
- * Class to manipulate the window in the remote extension.
- */
- export class RemoteCallFilesApp extends RemoteCall {
- /**
- * @return {boolean} Returns whether the code is running in SWA mode.
- */
- isSwaMode() {
- return this.origin_.startsWith('chrome://');
- }
- /**
- * Sends a test |message| to the test code running in the File Manager.
- * @param {!Object} message
- * @return {!Promise<*>}
- * @override
- */
- sendMessage(message) {
- if (!this.isSwaMode()) {
- return super.sendMessage(message);
- }
- const command = {
- name: 'callSwaTestMessageListener',
- appId: message.appId,
- data: JSON.stringify(message),
- };
- return new Promise((fulfill) => {
- chrome.test.sendMessage(JSON.stringify(command), (response) => {
- if (response === '"@undefined@"') {
- fulfill(undefined);
- } else {
- try {
- fulfill(response == '' ? true : JSON.parse(response));
- } catch (e) {
- console.error(`Failed to parse "${response}" due to ${e}`);
- fulfill(false);
- }
- }
- });
- });
- }
- /** @override */
- async waitForWindow(windowIdPrefix) {
- if (!this.isSwaMode()) {
- return super.waitForWindow(windowIdPrefix);
- }
- return this.waitForSwaWindow();
- }
- async getWindows() {
- if (!this.isSwaMode()) {
- return this.callRemoteTestUtil('getWindows', null, []);
- }
- return JSON.parse(
- await sendTestMessage({name: 'getWindowsSWA', isSWA: true}));
- }
- /**
- * Wait for a SWA window to be open.
- * @return {!Promise<string>}
- */
- async waitForSwaWindow() {
- const caller = getCaller();
- const appId = await repeatUntil(async () => {
- const ret = await sendTestMessage({name: 'findSwaWindow'});
- if (ret === 'none') {
- return pending(caller, 'Wait for SWA window');
- }
- return ret;
- });
- return appId;
- }
- /**
- * Executes a script in the context of a <preview-tag> element contained in
- * the window.
- * For SWA: It's the first chrome-untrusted://file-manager <iframe>.
- * For legacy: It's the first elements based on the `query`.
- * Responds with its output.
- *
- * @param {string} appId App window Id.
- * @param {!Array<string>} query Query to the <preview-tag> element (this is
- * ignored for SWA).
- * @param {string} statement Javascript statement to be executed within the
- * <preview-tag>.
- * @return {!Promise<*>} resolved with the return value of the `statement`.
- */
- async executeJsInPreviewTag(appId, query, statement) {
- if (this.isSwaMode()) {
- return this.executeJsInPreviewTagSwa_(statement);
- }
- return this.callRemoteTestUtil(
- 'deepExecuteScriptInWebView', appId, [query, statement]);
- }
- /**
- * Inject javascript statemenent in the first chrome-untrusted://file-manager
- * page found and respond with its output.
- * @private
- * @param {string} statement
- * @return {!Promise}
- */
- async executeJsInPreviewTagSwa_(statement) {
- const script = `try {
- let result = ${statement};
- result = result === undefined ? '@undefined@' : [result];
- window.domAutomationController.send(JSON.stringify(result));
- } catch (error) {
- const errorInfo = {'@error@': error.message, '@stack@': error.stack};
- window.domAutomationController.send(JSON.stringify(errorInfo));
- }`;
- const command = {
- name: 'executeScriptInChromeUntrusted',
- data: script,
- };
- const response = await sendTestMessage(command);
- if (response === '"@undefined@"') {
- return undefined;
- }
- const output = JSON.parse(response);
- if ('@error@' in output) {
- console.error(output['@error@']);
- console.error('Original StackTrace:\n' + output['@stack@']);
- throw new ExecuteScriptError(
- 'Error executing JS in Preview: ' + output['@error@']);
- } else {
- return output;
- }
- }
- /**
- * Waits until the expected URL shows in the last opened browser tab.
- * @param {string} expectedUrl
- * @return {!Promise} Promise to be fulfilled when the expected URL is shown
- * in a browser window.
- */
- async waitForLastOpenedBrowserTabUrl(expectedUrl) {
- const caller = getCaller();
- return repeatUntil(async () => {
- const command = {name: 'getLastActiveTabURL'};
- const activeBrowserTabURL = await sendTestMessage(command);
- if (activeBrowserTabURL !== expectedUrl) {
- return pending(
- caller, 'waitForActiveBrowserTabUrl: expected %j actual %j.',
- expectedUrl, activeBrowserTabURL);
- }
- });
- }
- /**
- * Returns whether an window exists with the expected URL.
- * @param {string} expectedUrl
- * @return {!Promise<boolean>} Promise resolved with true or false depending
- * on whether such window exists.
- */
- async windowUrlExists(expectedUrl) {
- const command = {name: 'expectWindowURL', expectedUrl: expectedUrl};
- const windowExists = await sendTestMessage(command);
- return windowExists == 'true';
- }
- /**
- * Waits for the file list turns to the given contents.
- * @param {string} appId App window Id.
- * @param {Array<Array<string>>} expected Expected contents of file list.
- * @param {{orderCheck:(?boolean|undefined), ignoreFileSize:
- * (?boolean|undefined), ignoreLastModifiedTime:(?boolean|undefined)}=}
- * opt_options Options of the comparison. If orderCheck is true, it also
- * compares the order of files. If ignoreLastModifiedTime is true, it
- * compares the file without its last modified time.
- * @return {Promise} Promise to be fulfilled when the file list turns to the
- * given contents.
- */
- waitForFiles(appId, expected, opt_options) {
- const options = opt_options || {};
- const caller = getCaller();
- return repeatUntil(async () => {
- const files = await this.callRemoteTestUtil('getFileList', appId, []);
- if (!options.orderCheck) {
- files.sort();
- expected.sort();
- }
- for (let i = 0; i < Math.min(files.length, expected.length); i++) {
- // Change the value received from the UI to match when comparing.
- if (options.ignoreFileSize) {
- files[i][1] = expected[i][1];
- }
- if (options.ignoreLastModifiedTime) {
- if (expected[i].length < 4) {
- // expected sometimes doesn't include the modified time at all, so
- // just remove from the data from UI.
- files[i].splice(3, 1);
- } else {
- files[i][3] = expected[i][3];
- }
- }
- }
- if (!chrome.test.checkDeepEq(expected, files)) {
- return pending(
- caller, 'waitForFiles: expected: %j actual %j.', expected, files);
- }
- });
- }
- /**
- * Waits until the number of files in the file list is changed from the given
- * number.
- * TODO(hirono): Remove the function.
- *
- * @param {string} appId App window Id.
- * @param {number} lengthBefore Number of items visible before.
- * @return {Promise} Promise to be fulfilled with the contents of files.
- */
- waitForFileListChange(appId, lengthBefore) {
- const caller = getCaller();
- return repeatUntil(async () => {
- const files = await this.callRemoteTestUtil('getFileList', appId, []);
- files.sort();
- const notReadyRows =
- files.filter((row) => row.filter((cell) => cell == '...').length);
- if (notReadyRows.length === 0 && files.length !== lengthBefore &&
- files.length !== 0) {
- return files;
- } else {
- return pending(
- caller, 'The number of file is %d. Not changed.', lengthBefore);
- }
- });
- }
- /**
- * Waits until the given taskId appears in the executed task list.
- * @param {string} appId App window Id.
- * @param {!chrome.fileManagerPrivate.FileTaskDescriptor} descriptor Task to
- * watch.
- * @param {Array<Object>=} opt_replyArgs arguments to reply to executed task.
- * @return {Promise} Promise to be fulfilled when the task appears in the
- * executed task list.
- */
- waitUntilTaskExecutes(appId, descriptor, opt_replyArgs) {
- const caller = getCaller();
- return repeatUntil(async () => {
- if (!await this.callRemoteTestUtil(
- 'taskWasExecuted', appId, [descriptor])) {
- const executedTasks =
- (await this.callRemoteTestUtil('getExecutedTasks', appId, []))
- .map(
- ({appId, taskType, actionId}) =>
- `${appId}|${taskType}|${actionId}`);
- return pending(caller, 'Executed task is %j', executedTasks);
- }
- if (opt_replyArgs) {
- await this.callRemoteTestUtil(
- 'replyExecutedTask', appId, [descriptor, opt_replyArgs]);
- }
- });
- }
- /**
- * Check if the next tabforcus'd element has the given ID or not.
- * @param {string} appId App window Id.
- * @param {string} elementId String of 'id' attribute which the next
- * tabfocus'd element should have.
- * @return {Promise} Promise to be fulfilled with the result.
- */
- async checkNextTabFocus(appId, elementId) {
- const result = await sendTestMessage({name: 'dispatchTabKey'});
- chrome.test.assertEq(
- result, 'tabKeyDispatched', 'Tab key dispatch failure');
- const caller = getCaller();
- return repeatUntil(async () => {
- let element =
- await this.callRemoteTestUtil('getActiveElement', appId, []);
- if (element && element.attributes['id'] === elementId) {
- return true;
- }
- // Try to check the shadow root.
- element =
- await this.callRemoteTestUtil('deepGetActiveElement', appId, []);
- if (element && element.attributes['id'] === elementId) {
- return true;
- }
- return pending(
- caller,
- 'Waiting for active element with id: "' + elementId +
- '", but current is: "' + element.attributes['id'] + '"');
- });
- }
- /**
- * Returns a promise that repeatedly checks for a file with the given
- * name to be selected in the app window with the given ID. Typical
- * use
- *
- * await remoteCall.waitUntilSelected('file#0', 'hello.txt');
- * ... // either the test timed out or hello.txt is currently selected.
- *
- * @param {string} appId App window Id.
- * @param {string} fileName the name of the file to be selected.
- * @return {Promise<boolean>} Promise that indicates if selection was
- * successful.
- */
- waitUntilSelected(appId, fileName) {
- const caller = getCaller();
- return repeatUntil(async () => {
- const selected =
- await this.callRemoteTestUtil('selectFile', appId, [fileName]);
- if (!selected) {
- return pending(caller, `File ${fileName} not yet selected`);
- }
- });
- }
- /**
- * Waits until the current directory is changed.
- * @param {string} appId App window Id.
- * @param {string} expectedPath Path to be changed to.
- * @return {Promise} Promise to be fulfilled when the current directory is
- * changed to expectedPath.
- */
- waitUntilCurrentDirectoryIsChanged(appId, expectedPath) {
- const caller = getCaller();
- return repeatUntil(async () => {
- const path =
- await this.callRemoteTestUtil('getBreadcrumbPath', appId, []);
- if (path !== expectedPath) {
- return pending(
- caller, 'Expected path is %s got %s', expectedPath, path);
- }
- });
- }
- /**
- * Expands tree item.
- * @param {string} appId App window Id.
- * @param {string} query Query to the <tree-item> element.
- */
- async expandTreeItemInDirectoryTree(appId, query) {
- await this.waitForElement(appId, query);
- const elements = await this.callRemoteTestUtil(
- 'queryAllElements', appId, [`${query}[expanded]`]);
- // If it's already expanded just set the focus on directory tree.
- if (elements.length > 0) {
- return this.callRemoteTestUtil('focus', appId, ['#directory-tree']);
- }
- // We must wait until <tree-item> has attribute [has-children=true]
- // otherwise it won't expand. We must also to account for the case
- // :not([expanded]) to ensure it has NOT been expanded by some async
- // operation since the [expanded] checks above.
- const expandIcon =
- query + ':not([expanded]) > .tree-row[has-children=true] .expand-icon';
- await this.waitAndClickElement(appId, expandIcon);
- // Wait for the expansion to finish.
- await this.waitForElement(appId, query + '[expanded]');
- // Force the focus on directory tree.
- await this.callRemoteTestUtil('focus', appId, ['#directory-tree']);
- }
- /**
- * Expands directory tree for specified path.
- */
- expandDirectoryTreeFor(appId, path, volumeType = 'downloads') {
- return this.expandDirectoryTreeForInternal_(
- appId, path.split('/'), 0, volumeType);
- }
- /**
- * Internal function for expanding directory tree for specified path.
- */
- async expandDirectoryTreeForInternal_(appId, components, index, volumeType) {
- if (index >= components.length - 1) {
- return;
- }
- // First time we should expand the root/volume first.
- if (index === 0) {
- await this.expandVolumeInDirectoryTree(appId, volumeType);
- return this.expandDirectoryTreeForInternal_(
- appId, components, index + 1, volumeType);
- }
- const path = '/' + components.slice(1, index + 1).join('/');
- await this.expandTreeItemInDirectoryTree(
- appId, `[full-path-for-testing="${path}"]`);
- await this.expandDirectoryTreeForInternal_(
- appId, components, index + 1, volumeType);
- }
- /**
- * Expands download volume in directory tree.
- */
- expandDownloadVolumeInDirectoryTree(appId) {
- return this.expandVolumeInDirectoryTree(appId, 'downloads');
- }
- /**
- * Expands download volume in directory tree.
- */
- expandVolumeInDirectoryTree(appId, volumeType) {
- return this.expandTreeItemInDirectoryTree(
- appId, `[volume-type-for-testing="${volumeType}"]`);
- }
- /**
- * Navigates to specified directory on the specified volume by using directory
- * tree.
- * DEPRECATED: Use background.js:navigateWithDirectoryTree instead
- * crbug.com/996626.
- */
- async navigateWithDirectoryTree(
- appId, path, rootLabel, volumeType = 'downloads') {
- await this.expandDirectoryTreeFor(appId, path, volumeType);
- // Select target path.
- await this.callRemoteTestUtil(
- 'fakeMouseClick', appId, [`[full-path-for-testing="${path}"]`]);
- // Entries within Drive starts with /root/ but it isn't displayed in the
- // breadcrubms used by waitUntilCurrentDirectoryIsChanged.
- path = path.replace(/^\/root/, '')
- .replace(/^\/team_drives/, '')
- .replace(/^\/Computers/, '');
- // TODO(lucmult): Remove this once MyFilesVolume is rolled out.
- // Remove /Downloads duplication when MyFilesVolume is enabled.
- if (volumeType == 'downloads' && path.startsWith('/Downloads') &&
- rootLabel.endsWith('/Downloads')) {
- rootLabel = rootLabel.replace('/Downloads', '');
- }
- // Wait until the Files app is navigated to the path.
- return this.waitUntilCurrentDirectoryIsChanged(
- appId, `/${rootLabel}${path}`);
- }
- /**
- * Wait until the expected number of volumes is mounted.
- * @param {number} expectedVolumesCount Expected number of mounted volumes.
- * @return {Promise} promise Promise to be fulfilled.
- */
- async waitForVolumesCount(expectedVolumesCount) {
- const caller = getCaller();
- return repeatUntil(async () => {
- const volumesCount = await sendTestMessage({name: 'getVolumesCount'});
- if (volumesCount === expectedVolumesCount.toString()) {
- return;
- }
- const msg =
- 'Expected number of mounted volumes: ' + expectedVolumesCount +
- '. Actual: ' + volumesCount;
- return pending(caller, msg);
- });
- }
- /**
- * Isolates the specified banner to test. The banner is still checked against
- * it's filters, but is now the top priority banner.
- * @param {string} appId App window Id
- * @param {string} bannerTagName Banner tag name in lowercase to isolate.
- */
- async isolateBannerForTesting(appId, bannerTagName) {
- await this.waitFor('isFileManagerLoaded', appId, true);
- chrome.test.assertTrue(await this.callRemoteTestUtil(
- 'isolateBannerForTesting', appId, [bannerTagName]));
- }
- /**
- * Disables banners from attaching to the DOM.
- * @param {string} appId App window Id
- */
- async disableBannersForTesting(appId) {
- await this.waitFor('isFileManagerLoaded', appId, true);
- chrome.test.assertTrue(
- await this.callRemoteTestUtil('disableBannersForTesting', appId, []));
- }
- /**
- * Sends text to the search box in the Files app.
- * @param {string} appId App window Id
- * @param {string} text The text to type in the search box.
- */
- async typeSearchText(appId, text) {
- const searchBoxInput = ['#search-box cr-input'];
- // Focus the search box.
- await this.waitAndClickElement(appId, '#search-button');
- // Input the text.
- await this.inputText(appId, searchBoxInput, text);
- // Notify the element of the input.
- chrome.test.assertTrue(await this.callRemoteTestUtil(
- 'fakeEvent', appId, ['#search-box cr-input', 'input']));
- }
- /**
- * Waits for the search box auto complete list to appear.
- * @param {string} appId
- * @return {!Promise<!Array<string>>} Array of the names in the auto complete
- * list.
- */
- async waitForSearchAutoComplete(appId) {
- // Wait for the list to appear.
- await this.waitForElement(appId, '#autocomplete-list li');
- // Return the result.
- const elements = await this.callRemoteTestUtil(
- 'deepQueryAllElements', appId, ['#autocomplete-list li']);
- return elements.map((element) => element.text);
- }
- }
|