Files
Edgars Eglītis 93115d1957 feat(base-driver)!: align timeouts endpoints closer to W3C standard (#22776)
* feat!: align timeouts endpoints closer to W3C standard

* chore: remove unnecessary wrappers
2026-09-14 22:57:53 +03:00

188 lines
6.5 KiB
TypeScript

import assert from 'node:assert/strict';
import type {AddressInfo} from 'node:net';
import path from 'node:path';
import {after, before, describe, it} from 'node:test';
import {fileURLToPath} from 'node:url';
import {pluginE2EHarness} from '@appium/plugin-test-support';
import {fs, node} from '@appium/support';
import {exec} from 'teen_process';
import {remote as wdio} from 'webdriverio';
import type {Browser} from 'webdriverio';
import {W3C_ELEMENT_KEY} from '../../lib/execute-child.js';
const THIS_PLUGIN_DIR = node.getModuleRootSync('@appium/execute-driver-plugin', fileURLToPath(import.meta.url))!;
const APPIUM_HOME = path.join(THIS_PLUGIN_DIR, 'local_appium_home');
const FAKE_DRIVER_DIR = path.join(THIS_PLUGIN_DIR, '..', 'fake-driver');
const TEST_HOST = '127.0.0.1';
const TEST_FAKE_APP = path.join(APPIUM_HOME, 'node_modules', '@appium', 'fake-driver', 'test', 'fixtures', 'app.xml');
const TEST_CAPS = {
platformName: 'Fake',
'appium:automationName': 'Fake',
'appium:deviceName': 'Fake',
'appium:app': TEST_FAKE_APP,
};
type WebdriverIOConfig = Parameters<typeof wdio>[0];
const WDIO_OPTS: WebdriverIOConfig = {
hostname: TEST_HOST,
connectionRetryCount: 0,
capabilities: TEST_CAPS,
};
describe('ExecuteDriverPlugin', function () {
let driver: Browser;
const basicScript = `return 'foo'`;
const e2eSetupOpts = {
host: TEST_HOST,
driverName: 'fake',
driverSource: 'local' as const,
driverSpec: FAKE_DRIVER_DIR,
pluginName: 'execute-driver',
pluginSource: 'local' as const,
pluginSpec: THIS_PLUGIN_DIR,
appiumHome: APPIUM_HOME,
};
after(async function () {
await fs.rimraf(APPIUM_HOME);
});
describe('without --allow-insecure set', function () {
let port: number;
const {setup, teardown} = pluginE2EHarness({...e2eSetupOpts});
before(async function () {
// workaround for https://github.com/nodejs/node/issues/64061
await exec(process.execPath, ['--version']);
const {server} = await setup();
const address = server.address();
port = (address as AddressInfo).port;
driver = await wdio({...WDIO_OPTS, port});
});
after(async function () {
try {
await driver?.deleteSession();
} finally {
await teardown();
}
});
it('should not work unless the allowInsecure feature flag is set', async function () {
await assert.rejects(driver.executeDriverScript(basicScript), /allow-insecure.+execute_driver_script/i);
});
});
describe('with --allow-insecure set', function () {
let port: number;
const {setup, teardown} = pluginE2EHarness({
...e2eSetupOpts,
serverArgs: {allowInsecure: ['*:execute_driver_script']},
});
before(async function () {
const {server} = await setup();
const address = server.address();
port = (address as AddressInfo).port;
driver = await wdio({...WDIO_OPTS, port});
});
after(async function () {
try {
await driver?.deleteSession();
} finally {
await teardown();
}
});
it('should execute a webdriverio script in the context of session', async function () {
const script = `
const timeouts = await driver.getTimeouts();
const status = await driver.status();
return [timeouts, status];
`;
const expectedTimeouts = {script: 30000, pageLoad: 300000, implicit: 0, command: 60000};
const {result, logs} = await driver.executeDriverScript(script);
assert.deepStrictEqual((result as any)[0], expectedTimeouts);
assert.ok((result as any)[1].build);
assert.ok((result as any)[1].build.version);
assert.deepStrictEqual(logs, {error: [], warn: [], log: []});
});
it('should fail with any script type other than webdriverio currently', async function () {
const script = `return 'foo'`;
await assert.rejects(driver.executeDriverScript(script, 'wd'), /webdriverio/);
});
it('should execute a webdriverio script that returns elements correctly', async function () {
const script = `
return await driver.$("#Button1");
`;
const {result} = await driver.executeDriverScript(script);
assert.deepStrictEqual(result, {
[W3C_ELEMENT_KEY]: '1',
});
});
it('should execute a webdriverio script that returns elements in deep structure', async function () {
const script = `
const el = await driver.$("#Button1");
return {element: el, elements: [el, el]};
`;
const {result} = await driver.executeDriverScript(script);
const elObj = {
[W3C_ELEMENT_KEY]: '1',
};
assert.deepStrictEqual(result, {element: elObj, elements: [elObj, elObj]});
});
it('should store and return logs to the user', async function () {
const script = `
console.log("foo");
console.log("foo2");
console.warn("bar");
console.error("baz");
return null;
`;
const {logs} = await driver.executeDriverScript(script);
assert.deepStrictEqual(logs, {log: ['foo', 'foo2'], warn: ['bar'], error: ['baz']});
});
it('should have appium specific commands available', async function () {
const script = `
return typeof driver.lock;
`;
const {result} = await driver.executeDriverScript(script);
assert.strictEqual(result, 'function');
});
it('should correctly handle errors that happen in a webdriverio script', async function () {
const script = `
return await driver.$("~notfound");
`;
const {result} = await driver.executeDriverScript(script);
assert.strictEqual((result as any).error.error, 'no such element');
assert.match((result as any).error.message, /element could not be located/);
assert.ok((result as any).error.stacktrace.includes('NoSuchElementError:'));
assert.strictEqual((result as any).selector, '~notfound');
assert.strictEqual((result as any).sessionId, driver.sessionId);
});
it('should correctly handle errors that happen when a script cannot be compiled', async function () {
const script = `
return {;
`;
await assert.rejects(driver.executeDriverScript(script), /Could not execute driver script.+Unexpected token/);
});
it('should be able to use standard promise and timeout functions in a driver script', async function () {
const script = `
await new Promise((resolve) => setTimeout(resolve, 1000));
return true;
`;
await assert.rejects(driver.executeDriverScript(script, 'webdriverio', 50), /.+50.+timeout.+/);
});
});
});