@appium/test-support 1.3.22 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/build/lib/driver-e2e-suite.d.ts +77 -0
- package/build/lib/driver-e2e-suite.d.ts.map +1 -0
- package/build/lib/driver-e2e-suite.js +389 -0
- package/build/lib/driver-unit-suite.d.ts +12 -0
- package/build/lib/driver-unit-suite.d.ts.map +1 -0
- package/build/lib/driver-unit-suite.js +564 -0
- package/build/lib/env-utils.d.ts +5 -0
- package/build/lib/env-utils.d.ts.map +1 -0
- package/build/lib/env-utils.js +3 -6
- package/build/lib/helpers.d.ts +19 -0
- package/build/lib/helpers.d.ts.map +1 -0
- package/build/lib/helpers.js +49 -0
- package/build/lib/index.d.ts +12 -0
- package/build/lib/index.d.ts.map +1 -0
- package/build/lib/index.js +59 -2
- package/build/lib/log-utils.d.ts +34 -0
- package/build/lib/log-utils.d.ts.map +1 -0
- package/build/lib/log-utils.js +4 -8
- package/build/lib/logger.d.ts +3 -0
- package/build/lib/logger.d.ts.map +1 -0
- package/build/lib/logger.js +2 -2
- package/build/lib/mock-utils.d.ts +47 -0
- package/build/lib/mock-utils.d.ts.map +1 -0
- package/build/lib/mock-utils.js +57 -29
- package/build/lib/plugin-e2e-harness.d.ts +67 -0
- package/build/lib/plugin-e2e-harness.d.ts.map +1 -0
- package/build/lib/plugin-e2e-harness.js +144 -0
- package/build/lib/sandbox-utils.d.ts +41 -0
- package/build/lib/sandbox-utils.d.ts.map +1 -0
- package/build/lib/sandbox-utils.js +46 -29
- package/build/lib/time-utils.d.ts +9 -0
- package/build/lib/time-utils.d.ts.map +1 -0
- package/build/lib/time-utils.js +1 -1
- package/build/lib/unhandled-rejection.d.ts +2 -0
- package/build/lib/unhandled-rejection.d.ts.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- package/lib/driver-e2e-suite.js +465 -0
- package/lib/driver-unit-suite.js +642 -0
- package/lib/env-utils.js +5 -3
- package/lib/helpers.js +68 -0
- package/lib/index.js +17 -7
- package/lib/log-utils.js +25 -8
- package/lib/logger.js +1 -1
- package/lib/mock-utils.js +89 -25
- package/lib/plugin-e2e-harness.js +163 -0
- package/lib/sandbox-utils.js +70 -26
- package/lib/time-utils.js +1 -0
- package/package.json +15 -6
package/lib/helpers.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import getPort from 'get-port';
|
|
2
|
+
import {curry} from 'lodash';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Default test host
|
|
6
|
+
*/
|
|
7
|
+
const TEST_HOST = '127.0.0.1';
|
|
8
|
+
|
|
9
|
+
let testPort;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Returns a free port; one per process
|
|
13
|
+
* @param {boolean} [force] - If true, do not reuse the port (if it already exists)
|
|
14
|
+
* @returns {Promise<number>} a free port
|
|
15
|
+
*/
|
|
16
|
+
async function getTestPort(force = false) {
|
|
17
|
+
if (force || !testPort) {
|
|
18
|
+
let port = await getPort();
|
|
19
|
+
if (!testPort) {
|
|
20
|
+
testPort = port;
|
|
21
|
+
}
|
|
22
|
+
return port;
|
|
23
|
+
}
|
|
24
|
+
return testPort;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Build an Appium URL from components.
|
|
29
|
+
*
|
|
30
|
+
* **All** parameters are required. Provide an empty string (`''`) if you don't need one.
|
|
31
|
+
* To rearrange arguments (if needed), use the placeholder from Lodash (`_`).
|
|
32
|
+
*
|
|
33
|
+
*/
|
|
34
|
+
const createAppiumURL = curry(
|
|
35
|
+
/**
|
|
36
|
+
* @param {string} address - Base address (w/ optional protocol)
|
|
37
|
+
* @param {string|number} port - Port number
|
|
38
|
+
* @param {string?} session - Session ID
|
|
39
|
+
* @param {string} pathname - Extra path
|
|
40
|
+
* @returns {string} New URL
|
|
41
|
+
* @example
|
|
42
|
+
*
|
|
43
|
+
* import _ from 'lodash';
|
|
44
|
+
*
|
|
45
|
+
* // http://127.0.0.1:31337/session
|
|
46
|
+
* createAppiumURL('127.0.0.1', 31337, '', 'session')
|
|
47
|
+
*
|
|
48
|
+
* // http://127.0.0.1:31337/session/asdfgjkl
|
|
49
|
+
* const createSessionURL = createAppiumURL('127.0.0.1', 31337, _, 'session')
|
|
50
|
+
* createSessionURL('asdfgjkl')
|
|
51
|
+
*
|
|
52
|
+
* // http://127.0.0.1:31337/session/asdfgjkl/appium/execute
|
|
53
|
+
* const createURLWithPath = createAppiumURL('127.0.0.1', 31337, 'asdfgjkl');
|
|
54
|
+
* createURLWithPath('appium/execute')
|
|
55
|
+
*/
|
|
56
|
+
(address, port, session, pathname) => {
|
|
57
|
+
if (!/^https?:\/\//.test(address)) {
|
|
58
|
+
address = `http://${address}`;
|
|
59
|
+
}
|
|
60
|
+
let path = session ? `session/${session}` : '';
|
|
61
|
+
if (pathname) {
|
|
62
|
+
path = `${path}/${pathname}`;
|
|
63
|
+
}
|
|
64
|
+
return new URL(path, `${address}:${port}`).href;
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
export {TEST_HOST, getTestPort, createAppiumURL};
|
package/lib/index.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
import {stubEnv} from './env-utils';
|
|
2
|
-
import {stubLog} from './log-utils';
|
|
3
|
-
import {fakeTime} from './time-utils';
|
|
4
|
-
import {withMocks, verifyMocks} from './mock-utils';
|
|
5
|
-
import {withSandbox, verifySandbox} from './sandbox-utils';
|
|
6
|
-
|
|
7
1
|
// this just needs to be imported, for the functionality to be injected
|
|
8
2
|
import './unhandled-rejection';
|
|
9
3
|
|
|
10
|
-
export {stubEnv
|
|
4
|
+
export {stubEnv} from './env-utils';
|
|
5
|
+
export {stubLog} from './log-utils';
|
|
6
|
+
export {fakeTime} from './time-utils';
|
|
7
|
+
export {withMocks, verifyMocks} from './mock-utils';
|
|
8
|
+
export {withSandbox, verifySandbox} from './sandbox-utils';
|
|
9
|
+
export {pluginE2EHarness} from './plugin-e2e-harness';
|
|
10
|
+
export {driverUnitTestSuite} from './driver-unit-suite';
|
|
11
|
+
export {driverE2ETestSuite, createSessionHelpers} from './driver-e2e-suite';
|
|
12
|
+
|
|
13
|
+
export * from './helpers';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {import('./driver-e2e-suite').SessionHelpers} SessionHelpers
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {import('./plugin-e2e-harness').E2ESetupOpts} E2ESetupOpts
|
|
20
|
+
*/
|
package/lib/log-utils.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
function stripColors(msg) {
|
|
3
|
-
let code = /\u001b\[(\d+(;\d+)*)?m/g; // eslint-disable-line no-control-regex
|
|
4
|
-
msg = ('' + msg).replace(code, '');
|
|
5
|
-
return msg;
|
|
6
|
-
}
|
|
1
|
+
import '@colors/colors';
|
|
7
2
|
|
|
8
3
|
class LogStub {
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {LogStubOptions} [opts]
|
|
7
|
+
*/
|
|
9
8
|
constructor(opts = {}) {
|
|
10
9
|
this.output = '';
|
|
11
|
-
this.stripColors = opts.stripColors;
|
|
10
|
+
this.stripColors = Boolean(opts.stripColors);
|
|
12
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param {string} level
|
|
15
|
+
* @param {any} message
|
|
16
|
+
*/
|
|
13
17
|
log(level, message) {
|
|
14
18
|
if (this.stripColors) {
|
|
15
|
-
message = stripColors
|
|
19
|
+
message = message.stripColors;
|
|
16
20
|
}
|
|
17
21
|
if (this.output.length > 0) {
|
|
18
22
|
this.output += '\n';
|
|
@@ -21,6 +25,13 @@ class LogStub {
|
|
|
21
25
|
}
|
|
22
26
|
}
|
|
23
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Instantiates a {@linkcode LogStub} object
|
|
30
|
+
* @param {import('sinon').SinonSandbox} sandbox
|
|
31
|
+
* @param {import('@appium/types').AppiumLogger} log
|
|
32
|
+
* @param {LogStubOptions} [opts]
|
|
33
|
+
* @returns {LogStub}
|
|
34
|
+
*/
|
|
24
35
|
function stubLog(sandbox, log, opts = {}) {
|
|
25
36
|
let logStub = new LogStub(opts);
|
|
26
37
|
for (let l of log.levels) {
|
|
@@ -32,3 +43,9 @@ function stubLog(sandbox, log, opts = {}) {
|
|
|
32
43
|
}
|
|
33
44
|
|
|
34
45
|
export {stubLog};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Options for {@linkcode LogStub} constructor
|
|
49
|
+
* @typedef LogStubOptions
|
|
50
|
+
* @property {boolean} [stripColors] - If `true`, strip ANSI colors from output
|
|
51
|
+
*/
|
package/lib/logger.js
CHANGED
package/lib/mock-utils.js
CHANGED
|
@@ -1,36 +1,100 @@
|
|
|
1
|
+
import B from 'bluebird';
|
|
1
2
|
import sinon from 'sinon';
|
|
2
|
-
import _ from 'lodash';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Creates a function which creates Mocha "before each" and "after each" hooks to
|
|
6
|
+
* automatically mock (and un-mock) the mocks provided by `libs`.
|
|
7
|
+
*
|
|
8
|
+
* The values of `libs` are provided directly to {@linkcode SinonSandbox.mock}.
|
|
9
|
+
*
|
|
10
|
+
* _Synchronously_ calls `fn` with the {@linkcode MockStore} after hooks have been created, but not before they have been run.
|
|
11
|
+
*
|
|
12
|
+
* @param {Record<string|symbol,any>} mockDefs
|
|
13
|
+
* @param {(mocks: MockStore) => void} fn
|
|
14
|
+
* @returns {() => void}
|
|
15
|
+
*/
|
|
16
|
+
export function withMocks(mockDefs, fn) {
|
|
7
17
|
return () => {
|
|
8
|
-
const mocks =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
},
|
|
12
|
-
get sandbox() {
|
|
13
|
-
return this[SANDBOX];
|
|
14
|
-
},
|
|
15
|
-
set sandbox(sandbox) {
|
|
16
|
-
this[SANDBOX] = sandbox;
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
beforeEach(function beforeEach() {
|
|
20
|
-
mocks[SANDBOX] = sinon.createSandbox();
|
|
21
|
-
for (let [key, value] of _.toPairs(libs)) {
|
|
22
|
-
mocks[key] = mocks.sandbox.mock(value);
|
|
23
|
-
}
|
|
18
|
+
const mocks = new MockStore();
|
|
19
|
+
beforeEach(function withMocksBeforeEach() {
|
|
20
|
+
mocks.createMocks(mockDefs);
|
|
24
21
|
});
|
|
25
|
-
afterEach(function
|
|
26
|
-
mocks.
|
|
22
|
+
afterEach(function withMocksAfterEach() {
|
|
23
|
+
mocks.reset();
|
|
27
24
|
});
|
|
28
25
|
fn(mocks);
|
|
29
26
|
};
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Convenience function for calling `mocks.verify()`.
|
|
31
|
+
* @param {MockStore} mocks - Returned by callback from {@linkcode withMocks}
|
|
32
|
+
*/
|
|
33
|
+
export function verifyMocks(mocks) {
|
|
34
|
+
mocks.verify();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @template {Record<string,any>} Mocks
|
|
39
|
+
* @extends {Mocks}
|
|
40
|
+
*/
|
|
41
|
+
export class MockStore {
|
|
42
|
+
/**
|
|
43
|
+
* Temporary sandbox; will be `undefined` until `beforeEach` is called
|
|
44
|
+
* @type {SinonSandbox|undefined}
|
|
45
|
+
*/
|
|
46
|
+
sandbox;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Original k/v pair provided to `createMocks`
|
|
50
|
+
* @type {Mocks|undefined}
|
|
51
|
+
*/
|
|
52
|
+
#mocks;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Uses a sandbox if one is provided
|
|
56
|
+
* @param {SinonSandbox} [sandbox]
|
|
57
|
+
*/
|
|
58
|
+
constructor(sandbox) {
|
|
59
|
+
this.sandbox = sandbox;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {Mocks} mockDefs
|
|
64
|
+
*/
|
|
65
|
+
createMocks(mockDefs) {
|
|
66
|
+
if (this.#mocks) {
|
|
67
|
+
throw new ReferenceError('Cannot create mocks twice; call `reset()` first.');
|
|
68
|
+
}
|
|
69
|
+
this.sandbox = this.sandbox ?? sinon.createSandbox().usingPromise(B);
|
|
70
|
+
for (const [key, value] of Object.entries(mockDefs)) {
|
|
71
|
+
this[key] = this.sandbox.mock(value);
|
|
72
|
+
}
|
|
73
|
+
this.#mocks = mockDefs;
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Calls {@linkcode SinonSandbox.verify} on the `sandbox` prop, if it exists
|
|
79
|
+
*/
|
|
80
|
+
verify() {
|
|
81
|
+
if (!this.sandbox) {
|
|
82
|
+
throw new ReferenceError(
|
|
83
|
+
'Cannot verify mocks before they are created; call `createMocks()` first'
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
this.sandbox.verify();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
reset() {
|
|
90
|
+
for (const key of Object.keys(this.#mocks ?? {})) {
|
|
91
|
+
delete this[key];
|
|
92
|
+
}
|
|
93
|
+
this.sandbox?.restore();
|
|
94
|
+
this.#mocks = undefined;
|
|
95
|
+
}
|
|
34
96
|
}
|
|
35
97
|
|
|
36
|
-
|
|
98
|
+
/**
|
|
99
|
+
* @typedef {import('sinon').SinonSandbox} SinonSandbox
|
|
100
|
+
*/
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import {fs} from 'appium/support';
|
|
3
|
+
import {main as appiumServer} from 'appium';
|
|
4
|
+
import getPort from 'get-port';
|
|
5
|
+
import {info, success, warning} from 'log-symbols';
|
|
6
|
+
import {exec} from 'teen_process';
|
|
7
|
+
|
|
8
|
+
const APPIUM_BIN = require.resolve('appium');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates hooks to install a driver and a plugin and starts an Appium server w/ the given extensions.
|
|
12
|
+
* @param {E2ESetupOpts} opts
|
|
13
|
+
* @returns {void}
|
|
14
|
+
*/
|
|
15
|
+
export function pluginE2EHarness(opts) {
|
|
16
|
+
let {
|
|
17
|
+
appiumHome,
|
|
18
|
+
before,
|
|
19
|
+
after,
|
|
20
|
+
serverArgs = {},
|
|
21
|
+
driverSource,
|
|
22
|
+
driverPackage,
|
|
23
|
+
driverName,
|
|
24
|
+
driverSpec,
|
|
25
|
+
pluginSource,
|
|
26
|
+
pluginPackage,
|
|
27
|
+
pluginSpec,
|
|
28
|
+
pluginName,
|
|
29
|
+
port,
|
|
30
|
+
host,
|
|
31
|
+
} = opts;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @type {AppiumServer}
|
|
35
|
+
*/
|
|
36
|
+
let server;
|
|
37
|
+
|
|
38
|
+
before(async function () {
|
|
39
|
+
const setupAppiumHome = async () => {
|
|
40
|
+
/**
|
|
41
|
+
* @type {AppiumEnv}
|
|
42
|
+
*/
|
|
43
|
+
const env = {...process.env};
|
|
44
|
+
|
|
45
|
+
if (appiumHome) {
|
|
46
|
+
env.APPIUM_HOME = appiumHome;
|
|
47
|
+
await fs.mkdirp(appiumHome);
|
|
48
|
+
console.log(`${info} Set \`APPIUM_HOME\` to ${appiumHome}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return env;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
*
|
|
56
|
+
* @param {AppiumEnv} env
|
|
57
|
+
*/
|
|
58
|
+
const installDriver = async (env) => {
|
|
59
|
+
console.log(`${info} Checking if driver "${driverName}" is installed...`);
|
|
60
|
+
const driverListArgs = [APPIUM_BIN, 'driver', 'list', '--json'];
|
|
61
|
+
console.log(`${info} Running: ${process.execPath} ${driverListArgs.join(' ')}`);
|
|
62
|
+
const {stdout: driverListJson} = await exec(process.execPath, driverListArgs, {
|
|
63
|
+
env,
|
|
64
|
+
});
|
|
65
|
+
const installedDrivers = JSON.parse(driverListJson);
|
|
66
|
+
|
|
67
|
+
if (!installedDrivers[driverName]?.installed) {
|
|
68
|
+
console.log(`${warning} Driver "${driverName}" not installed; installing...`);
|
|
69
|
+
const driverArgs = [APPIUM_BIN, 'driver', 'install', '--source', driverSource, driverSpec];
|
|
70
|
+
if (driverPackage) {
|
|
71
|
+
driverArgs.push('--package', driverPackage);
|
|
72
|
+
}
|
|
73
|
+
console.log(`${info} Running: ${process.execPath} ${driverArgs.join(' ')}`);
|
|
74
|
+
await exec(process.execPath, driverArgs, {
|
|
75
|
+
env,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
console.log(`${success} Installed driver "${driverName}"`);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
*
|
|
83
|
+
* @param {AppiumEnv} env
|
|
84
|
+
*/
|
|
85
|
+
const installPlugin = async (env) => {
|
|
86
|
+
console.log(`${info} Checking if plugin "${pluginName}" is installed...`);
|
|
87
|
+
const pluginListArgs = [APPIUM_BIN, 'plugin', 'list', '--json'];
|
|
88
|
+
const {stdout: pluginListJson} = await exec(process.execPath, pluginListArgs, {
|
|
89
|
+
env,
|
|
90
|
+
});
|
|
91
|
+
const installedPlugins = JSON.parse(pluginListJson);
|
|
92
|
+
|
|
93
|
+
if (!installedPlugins[pluginName]?.installed) {
|
|
94
|
+
console.log(`${warning} Plugin "${pluginName}" not installed; installing...`);
|
|
95
|
+
const pluginArgs = [APPIUM_BIN, 'plugin', 'install', '--source', pluginSource, pluginSpec];
|
|
96
|
+
if (pluginPackage) {
|
|
97
|
+
pluginArgs.push('--package', pluginPackage);
|
|
98
|
+
}
|
|
99
|
+
console.log(`${info} Running: ${process.execPath} ${pluginArgs.join(' ')}`);
|
|
100
|
+
await exec(process.execPath, pluginArgs, {
|
|
101
|
+
env,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
console.log(`${success} Installed plugin "${pluginName}"`);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const createServer = async () => {
|
|
108
|
+
if (!port) {
|
|
109
|
+
port = await getPort();
|
|
110
|
+
}
|
|
111
|
+
console.log(`${info} Will use port ${port} for Appium server`);
|
|
112
|
+
this.port = port;
|
|
113
|
+
|
|
114
|
+
/** @type {import('appium').Args} */
|
|
115
|
+
const args = {
|
|
116
|
+
port,
|
|
117
|
+
address: host,
|
|
118
|
+
usePlugins: [pluginName],
|
|
119
|
+
useDrivers: [driverName],
|
|
120
|
+
appiumHome,
|
|
121
|
+
...serverArgs,
|
|
122
|
+
};
|
|
123
|
+
server = /** @type {AppiumServer} */ (await appiumServer(args));
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const env = await setupAppiumHome();
|
|
127
|
+
await installDriver(env);
|
|
128
|
+
await installPlugin(env);
|
|
129
|
+
await createServer();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
after(async function () {
|
|
133
|
+
if (server) {
|
|
134
|
+
await server.close();
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @typedef E2ESetupOpts
|
|
141
|
+
* @property {string} [appiumHome] - Path to Appium home directory
|
|
142
|
+
* @property {Mocha.before} before - Mocha "before all" hook function
|
|
143
|
+
* @property {Mocha.after} after - Mocha "after all" hook function
|
|
144
|
+
* @property {Partial<import('appium').Args>} [serverArgs] - Arguments to pass to Appium server
|
|
145
|
+
* @property {import('appium/types').InstallType & string} driverSource - Source of driver to install
|
|
146
|
+
* @property {string} [driverPackage] - Package name of driver to install
|
|
147
|
+
* @property {string} driverName - Name of driver to install
|
|
148
|
+
* @property {string} driverSpec - Spec of driver to install
|
|
149
|
+
* @property {import('appium/types').InstallType & string} pluginSource - Source of plugin to install
|
|
150
|
+
* @property {string} [pluginPackage] - Package name of plugin to install
|
|
151
|
+
* @property {string} pluginSpec - Spec of plugin to install
|
|
152
|
+
* @property {string} pluginName - Name of plugin to install
|
|
153
|
+
* @property {number} [port] - Port to use for Appium server
|
|
154
|
+
* @property {string} [host] - Host to use for Appium server
|
|
155
|
+
*/
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @typedef {import('@appium/types').AppiumServer} AppiumServer
|
|
159
|
+
*/
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @typedef {import('appium/types').AppiumEnv} AppiumEnv
|
|
163
|
+
*/
|
package/lib/sandbox-utils.js
CHANGED
|
@@ -1,40 +1,84 @@
|
|
|
1
1
|
import sinon from 'sinon';
|
|
2
2
|
import _ from 'lodash';
|
|
3
3
|
import B from 'bluebird';
|
|
4
|
+
import {MockStore} from './mock-utils';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
/**
|
|
7
|
+
* @template {Record<string,any>|{mocks: Record<string,any>}} Mocks
|
|
8
|
+
* @param {Mocks} mockDefs
|
|
9
|
+
* @param {(sandboxStore: SandboxStore) => void} fn
|
|
10
|
+
* @returns {() => void}
|
|
11
|
+
*/
|
|
12
|
+
export function withSandbox(mockDefs, fn) {
|
|
13
|
+
// backwards-compat
|
|
14
|
+
if (!_.isEmpty(mockDefs.mocks)) {
|
|
15
|
+
mockDefs = mockDefs.mocks;
|
|
16
|
+
}
|
|
9
17
|
return () => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
verify() {
|
|
13
|
-
return this.sandbox.verify();
|
|
14
|
-
},
|
|
15
|
-
};
|
|
18
|
+
/** @type {SandboxStore} */
|
|
19
|
+
const sbx = new SandboxStore();
|
|
16
20
|
beforeEach(function beforeEach() {
|
|
17
|
-
|
|
18
|
-
S.sandbox.usingPromise(B);
|
|
19
|
-
S.mocks[SANDBOX] = S.sandbox;
|
|
20
|
-
for (let [key, value] of _.toPairs(config.mocks)) {
|
|
21
|
-
S.mocks[key] = S.sandbox.mock(value);
|
|
22
|
-
}
|
|
21
|
+
sbx.createSandbox(mockDefs);
|
|
23
22
|
});
|
|
24
23
|
afterEach(function afterEach() {
|
|
25
|
-
|
|
26
|
-
for (let k of _.keys(S.mocks)) {
|
|
27
|
-
delete S.mocks[k];
|
|
28
|
-
}
|
|
29
|
-
delete S.mocks[SANDBOX];
|
|
24
|
+
sbx.reset();
|
|
30
25
|
});
|
|
31
|
-
fn(
|
|
26
|
+
fn(sbx);
|
|
32
27
|
};
|
|
33
28
|
}
|
|
34
29
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Convenience function for calling {@linkcode SandboxStore.verify}.
|
|
32
|
+
* @param {SandboxStore|MockStore} sbxOrMocks
|
|
33
|
+
*/
|
|
34
|
+
export function verifySandbox(sbxOrMocks) {
|
|
35
|
+
sbxOrMocks.verify();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @template {Record<string,any>} Mocks
|
|
40
|
+
*/
|
|
41
|
+
export class SandboxStore {
|
|
42
|
+
/** @type {MockStore<Record<string,any>>} */
|
|
43
|
+
mocks;
|
|
44
|
+
|
|
45
|
+
/** @type {SinonSandbox|undefined} */
|
|
46
|
+
sandbox;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Uses a sandbox if one is provided
|
|
50
|
+
* @param {SinonSandbox} [sandbox]
|
|
51
|
+
*/
|
|
52
|
+
constructor(sandbox) {
|
|
53
|
+
this.sandbox = sandbox;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {Mocks} mocks
|
|
58
|
+
*/
|
|
59
|
+
createSandbox(mocks = /** @type {Mocks} */ ({})) {
|
|
60
|
+
this.sandbox = this.sandbox ?? sinon.createSandbox().usingPromise(B);
|
|
61
|
+
this.mocks = new MockStore(this.sandbox).createMocks(mocks);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Calls {@linkcode SinonSandbox.verify} on the `sandbox` prop, if it exists
|
|
66
|
+
*/
|
|
67
|
+
verify() {
|
|
68
|
+
if (!this.sandbox) {
|
|
69
|
+
throw new ReferenceError(
|
|
70
|
+
'Cannot verify mocks before they are created; call `createMocks()` first'
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
this.sandbox.verify();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
reset() {
|
|
77
|
+
this.mocks?.reset();
|
|
78
|
+
delete this.sandbox;
|
|
79
|
+
}
|
|
38
80
|
}
|
|
39
81
|
|
|
40
|
-
|
|
82
|
+
/**
|
|
83
|
+
* @typedef {import('sinon').SinonSandbox} SinonSandbox
|
|
84
|
+
*/
|
package/lib/time-utils.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appium/test-support",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "A collection of test utilities used across Appium packages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"automation",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"license": "Apache-2.0",
|
|
25
25
|
"author": "https://github.com/appium",
|
|
26
|
+
"types": "./build/lib/index.d.ts",
|
|
26
27
|
"bin": {
|
|
27
28
|
"android-emu-travis-post": "./bin/android-emu-travis-post.sh",
|
|
28
29
|
"android-emu-travis-pre": "./bin/android-emu-travis-pre.sh"
|
|
@@ -43,23 +44,31 @@
|
|
|
43
44
|
"lint": "eslint -c ../../.eslintrc --ignore-path ../../.eslintignore .",
|
|
44
45
|
"prepare": "npm run build",
|
|
45
46
|
"test": "npm run test:unit",
|
|
47
|
+
"test:smoke": "node ./index.js",
|
|
46
48
|
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
|
47
49
|
},
|
|
48
50
|
"dependencies": {
|
|
49
|
-
"@
|
|
50
|
-
"@
|
|
51
|
+
"@babel/runtime": "7.18.9",
|
|
52
|
+
"@colors/colors": "1.5.0",
|
|
51
53
|
"bluebird": "3.7.2",
|
|
54
|
+
"chai": "4.3.6",
|
|
55
|
+
"get-port": "5.1.1",
|
|
52
56
|
"lodash": "4.17.21",
|
|
57
|
+
"log-symbols": "4.1.0",
|
|
53
58
|
"loud-rejection": "2.2.0",
|
|
54
59
|
"sinon": "14.0.0",
|
|
55
|
-
"source-map-support": "0.5.21"
|
|
60
|
+
"source-map-support": "0.5.21",
|
|
61
|
+
"teen_process": "1.16.0"
|
|
62
|
+
},
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"appium": "^2.0.0-beta.35"
|
|
56
65
|
},
|
|
57
66
|
"engines": {
|
|
58
67
|
"node": ">=14",
|
|
59
|
-
"npm": ">=
|
|
68
|
+
"npm": ">=8"
|
|
60
69
|
},
|
|
61
70
|
"publishConfig": {
|
|
62
71
|
"access": "public"
|
|
63
72
|
},
|
|
64
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "c58f32441daba3aae9768a57d0a412fe92f4b587"
|
|
65
74
|
}
|