@appium/base-driver 10.5.2 → 10.7.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/build/lib/basedriver/capabilities.d.ts +1 -1
- package/build/lib/basedriver/capabilities.d.ts.map +1 -1
- package/build/lib/basedriver/capabilities.js +58 -50
- package/build/lib/basedriver/capabilities.js.map +1 -1
- package/build/lib/basedriver/commands/bidi.d.ts.map +1 -1
- package/build/lib/basedriver/commands/bidi.js +10 -14
- package/build/lib/basedriver/commands/bidi.js.map +1 -1
- package/build/lib/basedriver/commands/event.d.ts.map +1 -1
- package/build/lib/basedriver/commands/event.js +4 -7
- package/build/lib/basedriver/commands/event.js.map +1 -1
- package/build/lib/basedriver/commands/execute.js +3 -6
- package/build/lib/basedriver/commands/execute.js.map +1 -1
- package/build/lib/basedriver/commands/find.d.ts.map +1 -1
- package/build/lib/basedriver/commands/find.js +2 -1
- package/build/lib/basedriver/commands/find.js.map +1 -1
- package/build/lib/basedriver/commands/log.d.ts.map +1 -1
- package/build/lib/basedriver/commands/log.js +1 -5
- package/build/lib/basedriver/commands/log.js.map +1 -1
- package/build/lib/basedriver/commands/timeout.d.ts.map +1 -1
- package/build/lib/basedriver/commands/timeout.js +9 -13
- package/build/lib/basedriver/commands/timeout.js.map +1 -1
- package/build/lib/basedriver/core.d.ts.map +1 -1
- package/build/lib/basedriver/core.js +17 -14
- package/build/lib/basedriver/core.js.map +1 -1
- package/build/lib/basedriver/device-settings.d.ts.map +1 -1
- package/build/lib/basedriver/device-settings.js +3 -7
- package/build/lib/basedriver/device-settings.js.map +1 -1
- package/build/lib/basedriver/driver.d.ts.map +1 -1
- package/build/lib/basedriver/driver.js +34 -38
- package/build/lib/basedriver/driver.js.map +1 -1
- package/build/lib/basedriver/extension-core.d.ts +4 -1
- package/build/lib/basedriver/extension-core.d.ts.map +1 -1
- package/build/lib/basedriver/extension-core.js +37 -13
- package/build/lib/basedriver/extension-core.js.map +1 -1
- package/build/lib/basedriver/helpers.d.ts.map +1 -1
- package/build/lib/basedriver/helpers.js +47 -33
- package/build/lib/basedriver/helpers.js.map +1 -1
- package/build/lib/basedriver/ipc.d.ts +36 -0
- package/build/lib/basedriver/ipc.d.ts.map +1 -0
- package/build/lib/basedriver/ipc.js +157 -0
- package/build/lib/basedriver/ipc.js.map +1 -0
- package/build/lib/basedriver/validation.d.ts.map +1 -1
- package/build/lib/basedriver/validation.js +27 -29
- package/build/lib/basedriver/validation.js.map +1 -1
- package/build/lib/express/express-logging.d.ts +0 -1
- package/build/lib/express/express-logging.d.ts.map +1 -1
- package/build/lib/express/express-logging.js +11 -11
- package/build/lib/express/express-logging.js.map +1 -1
- package/build/lib/express/idempotency.js +3 -6
- package/build/lib/express/idempotency.js.map +1 -1
- package/build/lib/express/middleware.d.ts.map +1 -1
- package/build/lib/express/middleware.js +6 -10
- package/build/lib/express/middleware.js.map +1 -1
- package/build/lib/express/server.d.ts +1 -1
- package/build/lib/express/server.d.ts.map +1 -1
- package/build/lib/express/server.js +82 -73
- package/build/lib/express/server.js.map +1 -1
- package/build/lib/express/websocket.d.ts.map +1 -1
- package/build/lib/express/websocket.js +6 -9
- package/build/lib/express/websocket.js.map +1 -1
- package/build/lib/helpers/capabilities.d.ts.map +1 -1
- package/build/lib/helpers/capabilities.js +14 -17
- package/build/lib/helpers/capabilities.js.map +1 -1
- package/build/lib/helpers/extension-command-name.js +2 -5
- package/build/lib/helpers/extension-command-name.js.map +1 -1
- package/build/lib/helpers/levenshtein-match.d.ts.map +1 -1
- package/build/lib/helpers/levenshtein-match.js +6 -7
- package/build/lib/helpers/levenshtein-match.js.map +1 -1
- package/build/lib/index.d.ts +2 -1
- package/build/lib/index.d.ts.map +1 -1
- package/build/lib/index.js +6 -16
- package/build/lib/index.js.map +1 -1
- package/build/lib/jsonwp-proxy/protocol-converter.d.ts.map +1 -1
- package/build/lib/jsonwp-proxy/protocol-converter.js +21 -18
- package/build/lib/jsonwp-proxy/protocol-converter.js.map +1 -1
- package/build/lib/jsonwp-proxy/proxy-request.d.ts +2 -2
- package/build/lib/jsonwp-proxy/proxy-request.d.ts.map +1 -1
- package/build/lib/jsonwp-proxy/proxy-request.js +25 -21
- package/build/lib/jsonwp-proxy/proxy-request.js.map +1 -1
- package/build/lib/jsonwp-proxy/proxy.d.ts.map +1 -1
- package/build/lib/jsonwp-proxy/proxy.js +45 -36
- package/build/lib/jsonwp-proxy/proxy.js.map +1 -1
- package/build/lib/protocol/errors.d.ts.map +1 -1
- package/build/lib/protocol/errors.js +33 -37
- package/build/lib/protocol/errors.js.map +1 -1
- package/build/lib/protocol/helpers.d.ts.map +1 -1
- package/build/lib/protocol/helpers.js +9 -8
- package/build/lib/protocol/helpers.js.map +1 -1
- package/build/lib/protocol/protocol.d.ts +1 -1
- package/build/lib/protocol/protocol.d.ts.map +1 -1
- package/build/lib/protocol/protocol.js +73 -61
- package/build/lib/protocol/protocol.js.map +1 -1
- package/build/lib/protocol/routes.d.ts +1 -1
- package/build/lib/protocol/routes.d.ts.map +1 -1
- package/build/lib/protocol/routes.js +16 -17
- package/build/lib/protocol/routes.js.map +1 -1
- package/build/lib/protocol/validators.d.ts.map +1 -1
- package/build/lib/protocol/validators.js +1 -5
- package/build/lib/protocol/validators.js.map +1 -1
- package/build/lib/test-pages/crash.d.ts.map +1 -0
- package/build/lib/test-pages/crash.js.map +1 -0
- package/build/lib/test-pages/env.d.ts +5 -0
- package/build/lib/test-pages/env.d.ts.map +1 -0
- package/build/lib/test-pages/env.js +12 -0
- package/build/lib/test-pages/env.js.map +1 -0
- package/build/lib/{express/static.d.ts → test-pages/handlers.d.ts} +1 -2
- package/build/lib/test-pages/handlers.d.ts.map +1 -0
- package/build/lib/{express/static.js → test-pages/handlers.js} +9 -12
- package/build/lib/test-pages/handlers.js.map +1 -0
- package/build/lib/test-pages/index.d.ts +6 -0
- package/build/lib/test-pages/index.d.ts.map +1 -0
- package/build/lib/test-pages/index.js +35 -0
- package/build/lib/test-pages/index.js.map +1 -0
- package/build/lib/test-pages/static-dir.d.ts +8 -0
- package/build/lib/test-pages/static-dir.d.ts.map +1 -0
- package/build/lib/test-pages/static-dir.js +24 -0
- package/build/lib/test-pages/static-dir.js.map +1 -0
- package/build/lib/test-pages/template.d.ts +3 -0
- package/build/lib/test-pages/template.d.ts.map +1 -0
- package/build/lib/test-pages/template.js +19 -0
- package/build/lib/test-pages/template.js.map +1 -0
- package/build/lib/utils.d.ts +14 -0
- package/build/lib/utils.d.ts.map +1 -0
- package/build/lib/utils.js +55 -0
- package/build/lib/utils.js.map +1 -0
- package/lib/basedriver/capabilities.ts +126 -115
- package/lib/basedriver/commands/bidi.ts +11 -11
- package/lib/basedriver/commands/event.ts +17 -11
- package/lib/basedriver/commands/execute.ts +15 -12
- package/lib/basedriver/commands/find.ts +20 -12
- package/lib/basedriver/commands/log.ts +4 -3
- package/lib/basedriver/commands/timeout.ts +22 -14
- package/lib/basedriver/core.ts +26 -26
- package/lib/basedriver/device-settings.ts +7 -12
- package/lib/basedriver/driver.ts +62 -50
- package/lib/basedriver/extension-core.ts +60 -18
- package/lib/basedriver/helpers.ts +81 -52
- package/lib/basedriver/ipc.ts +198 -0
- package/lib/basedriver/validation.ts +37 -30
- package/lib/express/express-logging.ts +16 -20
- package/lib/express/idempotency.ts +9 -9
- package/lib/express/middleware.ts +14 -18
- package/lib/express/server.ts +118 -120
- package/lib/express/websocket.ts +11 -15
- package/lib/helpers/capabilities.ts +21 -16
- package/lib/helpers/extension-command-name.ts +3 -3
- package/lib/helpers/levenshtein-match.ts +20 -14
- package/lib/index.js +3 -12
- package/lib/jsonwp-proxy/protocol-converter.ts +58 -35
- package/lib/jsonwp-proxy/proxy-request.ts +26 -26
- package/lib/jsonwp-proxy/proxy.ts +74 -75
- package/lib/protocol/errors.ts +69 -88
- package/lib/protocol/helpers.ts +9 -5
- package/lib/protocol/protocol.ts +149 -107
- package/lib/protocol/routes.ts +17 -17
- package/lib/protocol/validators.ts +1 -3
- package/lib/test-pages/env.ts +9 -0
- package/lib/{express/static.ts → test-pages/handlers.ts} +10 -22
- package/lib/test-pages/index.ts +34 -0
- package/lib/test-pages/static-dir.ts +19 -0
- package/lib/test-pages/template.ts +17 -0
- package/lib/utils.ts +65 -0
- package/package.json +10 -13
- package/tsconfig.json +1 -0
- package/build/lib/express/crash.d.ts.map +0 -1
- package/build/lib/express/crash.js.map +0 -1
- package/build/lib/express/static.d.ts.map +0 -1
- package/build/lib/express/static.js.map +0 -1
- /package/build/lib/{express → test-pages}/crash.d.ts +0 -0
- /package/build/lib/{express → test-pages}/crash.js +0 -0
- /package/lib/{express → test-pages}/crash.ts +0 -0
- /package/{static → test-fixtures/static}/appium.png +0 -0
- /package/{static → test-fixtures/static}/favicon.ico +0 -0
- /package/{static → test-fixtures/static}/js/jquery.min.js +0 -0
- /package/{static → test-fixtures/static}/test/frameset.html +0 -0
- /package/{static → test-fixtures/static}/test/guinea-pig-app-banner.html +0 -0
- /package/{static → test-fixtures/static}/test/guinea-pig-scrollable.html +0 -0
- /package/{static → test-fixtures/static}/test/guinea-pig.html +0 -0
- /package/{static → test-fixtures/static}/test/guinea-pig2.html +0 -0
- /package/{static → test-fixtures/static}/test/guinea-pig3.html +0 -0
- /package/{static → test-fixtures/static}/test/guinea-pig4.html +0 -0
- /package/{static → test-fixtures/static}/test/guinea-pig5.html +0 -0
- /package/{static → test-fixtures/static}/test/iframes.html +0 -0
- /package/{static → test-fixtures/static}/test/shadow-dom.html +0 -0
- /package/{static → test-fixtures/static}/test/subframe1.html +0 -0
- /package/{static → test-fixtures/static}/test/subframe2.html +0 -0
- /package/{static → test-fixtures/static}/test/subframe3.html +0 -0
- /package/{static → test-fixtures/static}/test/touch.html +0 -0
- /package/{static → test-fixtures/static}/test/welcome.html +0 -0
|
@@ -5,13 +5,10 @@ import type {
|
|
|
5
5
|
W3CCapabilities,
|
|
6
6
|
StandardCapabilities,
|
|
7
7
|
} from '@appium/types';
|
|
8
|
-
import type {
|
|
9
|
-
KeyAsString,
|
|
10
|
-
MergeExclusive,
|
|
11
|
-
} from 'type-fest';
|
|
12
|
-
import _ from 'lodash';
|
|
8
|
+
import type {KeyAsString, MergeExclusive} from 'type-fest';
|
|
13
9
|
import {validator} from './validation';
|
|
14
10
|
import {util} from '@appium/support';
|
|
11
|
+
import {omit, pickBy} from '../utils';
|
|
15
12
|
import {log} from './logger';
|
|
16
13
|
import {errors} from '../protocol/errors';
|
|
17
14
|
|
|
@@ -39,22 +36,22 @@ export function mergeCaps<
|
|
|
39
36
|
T extends Constraints,
|
|
40
37
|
U extends Constraints,
|
|
41
38
|
Primary extends Capabilities<T>,
|
|
42
|
-
Secondary extends Capabilities<U
|
|
39
|
+
Secondary extends Capabilities<U>,
|
|
43
40
|
>(
|
|
44
41
|
primary: Primary | undefined = {} as Primary,
|
|
45
|
-
secondary: Secondary | undefined = {} as Secondary
|
|
42
|
+
secondary: Secondary | undefined = {} as Secondary,
|
|
46
43
|
): MergeExclusive<Primary, Secondary> {
|
|
47
|
-
const result =
|
|
44
|
+
const result = {
|
|
48
45
|
...primary,
|
|
49
|
-
}
|
|
46
|
+
} as MergeExclusive<Primary, Secondary>;
|
|
50
47
|
|
|
51
48
|
for (const [name, value] of Object.entries(secondary)) {
|
|
52
49
|
// Overwriting is not allowed. Primary and secondary must have different properties (w3c rule 4.4)
|
|
53
|
-
if (
|
|
50
|
+
if (primary[name] !== undefined) {
|
|
54
51
|
throw new errors.InvalidArgumentError(
|
|
55
52
|
`property '${name}' should not exist on both primary (${JSON.stringify(
|
|
56
|
-
primary
|
|
57
|
-
)}) and secondary (${JSON.stringify(secondary)}) object
|
|
53
|
+
primary,
|
|
54
|
+
)}) and secondary (${JSON.stringify(secondary)}) object`,
|
|
58
55
|
);
|
|
59
56
|
}
|
|
60
57
|
result[name as keyof typeof result] = value;
|
|
@@ -69,37 +66,42 @@ export function mergeCaps<
|
|
|
69
66
|
export function validateCaps<C extends Constraints>(
|
|
70
67
|
caps: Capabilities<C>,
|
|
71
68
|
constraints: C | undefined = {} as C,
|
|
72
|
-
opts: ValidateCapsOpts | undefined = {}
|
|
69
|
+
opts: ValidateCapsOpts | undefined = {},
|
|
73
70
|
): Capabilities<C> {
|
|
74
71
|
const {skipPresenceConstraint} = opts;
|
|
75
72
|
|
|
76
|
-
if (!
|
|
73
|
+
if (!util.isPlainObject(caps)) {
|
|
77
74
|
throw new errors.InvalidArgumentError(`must be a JSON object`);
|
|
78
75
|
}
|
|
79
76
|
|
|
80
77
|
// Remove the 'presence' constraint if we're not checking for it
|
|
81
|
-
constraints = (
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
78
|
+
constraints = Object.fromEntries(
|
|
79
|
+
Object.entries(constraints ?? {}).map(([key, constraint]) => {
|
|
80
|
+
if (skipPresenceConstraint) {
|
|
81
|
+
return [key, omit(constraint as Record<string, unknown>, 'presence')];
|
|
82
|
+
}
|
|
83
|
+
if ((constraint as {presence?: unknown}).presence === true) {
|
|
84
|
+
return [
|
|
85
|
+
key,
|
|
86
|
+
{
|
|
87
|
+
...omit(constraint as Record<string, unknown>, 'presence'),
|
|
88
|
+
presence: {allowEmpty: false},
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
return [key, constraint];
|
|
93
|
+
}),
|
|
95
94
|
) as C;
|
|
96
95
|
|
|
97
|
-
const validationErrors = validator.validate(
|
|
96
|
+
const validationErrors = validator.validate(
|
|
97
|
+
pickBy(caps, (value) => util.hasValue(value)),
|
|
98
|
+
constraints,
|
|
99
|
+
);
|
|
98
100
|
|
|
99
101
|
if (validationErrors) {
|
|
100
102
|
const message: string[] = [];
|
|
101
|
-
for (const [attribute, reasons] of
|
|
102
|
-
for (const reason of
|
|
103
|
+
for (const [attribute, reasons] of Object.entries(validationErrors)) {
|
|
104
|
+
for (const reason of reasons as string[]) {
|
|
103
105
|
message.push(`'${attribute}' ${reason}`);
|
|
104
106
|
}
|
|
105
107
|
}
|
|
@@ -115,22 +117,20 @@ export function validateCaps<C extends Constraints>(
|
|
|
115
117
|
* @see https://www.w3.org/TR/webdriver2/#dfn-table-of-standard-capabilities)
|
|
116
118
|
*/
|
|
117
119
|
export const STANDARD_CAPS = Object.freeze(
|
|
118
|
-
new Set(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
]) as KeyAsString<StandardCapabilities>[]
|
|
133
|
-
)
|
|
120
|
+
new Set([
|
|
121
|
+
'browserName',
|
|
122
|
+
'browserVersion',
|
|
123
|
+
'platformName',
|
|
124
|
+
'acceptInsecureCerts',
|
|
125
|
+
'pageLoadStrategy',
|
|
126
|
+
'proxy',
|
|
127
|
+
'setWindowRect',
|
|
128
|
+
'timeouts',
|
|
129
|
+
'strictFileInteractability',
|
|
130
|
+
'unhandledPromptBehavior',
|
|
131
|
+
'userAgent',
|
|
132
|
+
'webSocketUrl',
|
|
133
|
+
] as KeyAsString<StandardCapabilities>[]),
|
|
134
134
|
);
|
|
135
135
|
|
|
136
136
|
const STANDARD_CAPS_LOWER = new Set([...STANDARD_CAPS].map((cap) => cap.toLowerCase()));
|
|
@@ -148,34 +148,40 @@ export function isStandardCap(cap: string): boolean {
|
|
|
148
148
|
* @see https://www.w3.org/TR/webdriver/#dfn-extension-capabilities
|
|
149
149
|
* @internal
|
|
150
150
|
*/
|
|
151
|
-
export function stripAppiumPrefixes<C extends Constraints>(
|
|
151
|
+
export function stripAppiumPrefixes<C extends Constraints>(
|
|
152
|
+
caps: NSCapabilities<C>,
|
|
153
|
+
): Capabilities<C> {
|
|
152
154
|
// split into prefixed and non-prefixed.
|
|
153
155
|
// non-prefixed should be standard caps at this point
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
);
|
|
156
|
+
const capKeys = Object.keys(caps);
|
|
157
|
+
const prefixedCaps = capKeys.filter((cap) => String(cap).startsWith(APPIUM_VENDOR_PREFIX));
|
|
158
|
+
const nonPrefixedCaps = capKeys.filter((cap) => !String(cap).startsWith(APPIUM_VENDOR_PREFIX));
|
|
157
159
|
|
|
158
160
|
// initialize this with the k/v pairs of the non-prefixed caps
|
|
159
|
-
const strippedCaps =
|
|
161
|
+
const strippedCaps: Record<string, unknown> = Object.fromEntries(
|
|
162
|
+
nonPrefixedCaps.map((cap) => [cap, caps[cap as keyof typeof caps]]),
|
|
163
|
+
) as Capabilities<C>;
|
|
160
164
|
const badPrefixedCaps: string[] = [];
|
|
161
165
|
|
|
162
166
|
// Strip out the 'appium:' prefix
|
|
163
167
|
for (const prefixedCap of prefixedCaps) {
|
|
164
|
-
const strippedCapName = prefixedCap.substring(APPIUM_VENDOR_PREFIX.length) as KeyAsString<
|
|
168
|
+
const strippedCapName = prefixedCap.substring(APPIUM_VENDOR_PREFIX.length) as KeyAsString<
|
|
169
|
+
Capabilities<C>
|
|
170
|
+
>;
|
|
165
171
|
|
|
166
172
|
// If it's standard capability that was prefixed, add it to an array of incorrectly prefixed capabilities
|
|
167
173
|
if (isStandardCap(strippedCapName)) {
|
|
168
174
|
badPrefixedCaps.push(strippedCapName);
|
|
169
|
-
if (
|
|
170
|
-
strippedCaps[strippedCapName] = caps[prefixedCap];
|
|
175
|
+
if (strippedCaps[strippedCapName] == null) {
|
|
176
|
+
strippedCaps[strippedCapName] = caps[prefixedCap as keyof typeof caps];
|
|
171
177
|
} else {
|
|
172
178
|
log.warn(
|
|
173
|
-
`Ignoring capability '${prefixedCap}=${caps[prefixedCap]}' and ` +
|
|
174
|
-
`using capability '${strippedCapName}=${strippedCaps[strippedCapName]}'
|
|
179
|
+
`Ignoring capability '${prefixedCap}=${caps[prefixedCap as keyof typeof caps]}' and ` +
|
|
180
|
+
`using capability '${strippedCapName}=${strippedCaps[strippedCapName]}'`,
|
|
175
181
|
);
|
|
176
182
|
}
|
|
177
183
|
} else {
|
|
178
|
-
strippedCaps[strippedCapName] = caps[prefixedCap];
|
|
184
|
+
strippedCaps[strippedCapName] = caps[prefixedCap as keyof typeof caps];
|
|
179
185
|
}
|
|
180
186
|
}
|
|
181
187
|
|
|
@@ -183,32 +189,29 @@ export function stripAppiumPrefixes<C extends Constraints>(caps: NSCapabilities<
|
|
|
183
189
|
if (badPrefixedCaps.length > 0) {
|
|
184
190
|
log.warn(
|
|
185
191
|
`The capabilities ${JSON.stringify(
|
|
186
|
-
badPrefixedCaps
|
|
187
|
-
)} are standard capabilities and do not require "appium:" prefix
|
|
192
|
+
badPrefixedCaps,
|
|
193
|
+
)} are standard capabilities and do not require "appium:" prefix`,
|
|
188
194
|
);
|
|
189
195
|
}
|
|
190
|
-
return strippedCaps
|
|
196
|
+
return strippedCaps as Capabilities<C>;
|
|
191
197
|
}
|
|
192
198
|
|
|
193
199
|
/**
|
|
194
200
|
* Get an array of all the unprefixed caps that are being used in 'alwaysMatch' and all of the 'firstMatch' object
|
|
195
201
|
*/
|
|
196
|
-
export function findNonPrefixedCaps<C extends Constraints>(
|
|
197
|
-
{
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
return _.chain([alwaysMatch, ...firstMatch])
|
|
203
|
-
.reduce(
|
|
202
|
+
export function findNonPrefixedCaps<C extends Constraints>({
|
|
203
|
+
alwaysMatch = {},
|
|
204
|
+
firstMatch = [],
|
|
205
|
+
}: W3CCapabilities<C>): string[] {
|
|
206
|
+
return util.uniq(
|
|
207
|
+
[alwaysMatch, ...firstMatch].reduce<string[]>(
|
|
204
208
|
(unprefixedCaps, caps) => [
|
|
205
209
|
...unprefixedCaps,
|
|
206
210
|
...Object.keys(caps).filter((cap) => !cap.includes(':') && !isStandardCap(cap)),
|
|
207
211
|
],
|
|
208
|
-
[]
|
|
209
|
-
)
|
|
210
|
-
|
|
211
|
-
.value();
|
|
212
|
+
[],
|
|
213
|
+
),
|
|
214
|
+
);
|
|
212
215
|
}
|
|
213
216
|
|
|
214
217
|
/**
|
|
@@ -218,12 +221,12 @@ export function findNonPrefixedCaps<C extends Constraints>(
|
|
|
218
221
|
export function parseCaps<C extends Constraints>(
|
|
219
222
|
caps: W3CCapabilities<C>,
|
|
220
223
|
constraints: C | undefined = {} as C,
|
|
221
|
-
shouldValidateCaps: boolean | undefined = true
|
|
224
|
+
shouldValidateCaps: boolean | undefined = true,
|
|
222
225
|
): ParsedCaps<C> {
|
|
223
226
|
// If capabilities request is not an object, return error (#1.1)
|
|
224
|
-
if (!
|
|
227
|
+
if (!util.isPlainObject(caps)) {
|
|
225
228
|
throw new errors.InvalidArgumentError(
|
|
226
|
-
'The capabilities argument was not valid for the following reason(s): "capabilities" must be a JSON object.'
|
|
229
|
+
'The capabilities argument was not valid for the following reason(s): "capabilities" must be a JSON object.',
|
|
227
230
|
);
|
|
228
231
|
}
|
|
229
232
|
|
|
@@ -235,9 +238,9 @@ export function parseCaps<C extends Constraints>(
|
|
|
235
238
|
} = caps;
|
|
236
239
|
|
|
237
240
|
// Reject 'firstMatch' argument if it's not an array (#3.2)
|
|
238
|
-
if (!
|
|
241
|
+
if (!Array.isArray(allFirstMatchCaps)) {
|
|
239
242
|
throw new errors.InvalidArgumentError(
|
|
240
|
-
'The capabilities.firstMatch argument was not valid for the following reason(s): "capabilities.firstMatch" must be a JSON array or undefined'
|
|
243
|
+
'The capabilities.firstMatch argument was not valid for the following reason(s): "capabilities.firstMatch" must be a JSON array or undefined',
|
|
241
244
|
);
|
|
242
245
|
}
|
|
243
246
|
|
|
@@ -246,16 +249,16 @@ export function parseCaps<C extends Constraints>(
|
|
|
246
249
|
if (allFirstMatchCaps.length === 0) {
|
|
247
250
|
log.warn(
|
|
248
251
|
`The firstMatch array in the given capabilities has no entries. Adding an empty entry for now, ` +
|
|
249
|
-
`but it will require one or more entries as W3C spec
|
|
252
|
+
`but it will require one or more entries as W3C spec.`,
|
|
250
253
|
);
|
|
251
254
|
allFirstMatchCaps.push({});
|
|
252
255
|
}
|
|
253
256
|
|
|
254
257
|
// Check for non-prefixed, non-standard capabilities and log warnings if they are found
|
|
255
258
|
const nonPrefixedCaps = findNonPrefixedCaps(caps);
|
|
256
|
-
if (!
|
|
259
|
+
if (!util.isEmpty(nonPrefixedCaps)) {
|
|
257
260
|
throw new errors.InvalidArgumentError(
|
|
258
|
-
`All non-standard capabilities should have a vendor prefix. The following capabilities did not have one: ${nonPrefixedCaps}
|
|
261
|
+
`All non-standard capabilities should have a vendor prefix. The following capabilities did not have one: ${nonPrefixedCaps}`,
|
|
259
262
|
);
|
|
260
263
|
}
|
|
261
264
|
|
|
@@ -271,22 +274,24 @@ export function parseCaps<C extends Constraints>(
|
|
|
271
274
|
}
|
|
272
275
|
// Remove the 'presence' constraint for any keys that are already present in 'requiredCaps'
|
|
273
276
|
// since we know that this constraint has already passed
|
|
274
|
-
const filteredConstraints =
|
|
277
|
+
const filteredConstraints = Object.fromEntries(
|
|
278
|
+
Object.entries(constraints ?? {}).filter(([key]) => !(key in strippedRequiredCaps)),
|
|
279
|
+
) as C;
|
|
275
280
|
|
|
276
281
|
// Validate all of the first match capabilities and return an array with only the valid caps (see spec #5)
|
|
277
282
|
const validationErrors: string[] = [];
|
|
278
|
-
const validatedFirstMatchCaps =
|
|
279
|
-
|
|
283
|
+
const validatedFirstMatchCaps = strippedAllFirstMatchCaps
|
|
284
|
+
.map((firstMatchCaps) => {
|
|
280
285
|
try {
|
|
281
286
|
// Validate firstMatch caps
|
|
282
287
|
return shouldValidateCaps
|
|
283
288
|
? validateCaps(firstMatchCaps, filteredConstraints)
|
|
284
289
|
: firstMatchCaps;
|
|
285
290
|
} catch (e) {
|
|
286
|
-
validationErrors.push(e.message);
|
|
291
|
+
validationErrors.push((e as Error).message);
|
|
287
292
|
}
|
|
288
293
|
})
|
|
289
|
-
|
|
294
|
+
.filter(Boolean) as Capabilities<C>[];
|
|
290
295
|
|
|
291
296
|
/**
|
|
292
297
|
* Try to merge requiredCaps with first match capabilities, break once it finds its first match
|
|
@@ -300,8 +305,8 @@ export function parseCaps<C extends Constraints>(
|
|
|
300
305
|
break;
|
|
301
306
|
}
|
|
302
307
|
} catch (err) {
|
|
303
|
-
log.warn(err.message);
|
|
304
|
-
validationErrors.push(err.message);
|
|
308
|
+
log.warn((err as Error).message);
|
|
309
|
+
validationErrors.push((err as Error).message);
|
|
305
310
|
}
|
|
306
311
|
}
|
|
307
312
|
|
|
@@ -318,24 +323,21 @@ export function parseCaps<C extends Constraints>(
|
|
|
318
323
|
/**
|
|
319
324
|
* Calls parseCaps and just returns the matchedCaps variable
|
|
320
325
|
*/
|
|
321
|
-
export function processCapabilities<
|
|
322
|
-
C extends Constraints,
|
|
323
|
-
W3CCaps extends W3CCapabilities<C>
|
|
324
|
-
>(
|
|
326
|
+
export function processCapabilities<C extends Constraints, W3CCaps extends W3CCapabilities<C>>(
|
|
325
327
|
w3cCaps: W3CCaps,
|
|
326
328
|
constraints: C | undefined = {} as C,
|
|
327
|
-
shouldValidateCaps: boolean | undefined = true
|
|
329
|
+
shouldValidateCaps: boolean | undefined = true,
|
|
328
330
|
): Capabilities<C> {
|
|
329
331
|
const {matchedCaps, validationErrors} = parseCaps(w3cCaps, constraints, shouldValidateCaps);
|
|
330
332
|
|
|
331
333
|
// If we found an error throw an exception
|
|
332
334
|
if (!util.hasValue(matchedCaps)) {
|
|
333
|
-
if (
|
|
335
|
+
if (Array.isArray(w3cCaps.firstMatch) && w3cCaps.firstMatch.length > 1) {
|
|
334
336
|
// If there was more than one 'firstMatch' cap, indicate that we couldn't find a matching capabilities set and show all the errors
|
|
335
337
|
throw new errors.InvalidArgumentError(
|
|
336
338
|
`Could not find matching capabilities from ${JSON.stringify(
|
|
337
|
-
w3cCaps
|
|
338
|
-
)}:\n ${validationErrors.join('\n')}
|
|
339
|
+
w3cCaps,
|
|
340
|
+
)}:\n ${validationErrors.join('\n')}`,
|
|
339
341
|
);
|
|
340
342
|
} else {
|
|
341
343
|
// Otherwise, just show the singular error message
|
|
@@ -350,23 +352,25 @@ export function processCapabilities<
|
|
|
350
352
|
* Return a copy of a "bare" (single-level, non-W3C) capabilities object which has taken everything
|
|
351
353
|
* within the 'appium:options' capability and promoted it to the top level.
|
|
352
354
|
*/
|
|
353
|
-
export function promoteAppiumOptionsForObject<C extends Constraints>(
|
|
355
|
+
export function promoteAppiumOptionsForObject<C extends Constraints>(
|
|
356
|
+
obj: NSCapabilities<C>,
|
|
357
|
+
): NSCapabilities<C> {
|
|
354
358
|
const appiumOptions = obj[PREFIXED_APPIUM_OPTS_CAP];
|
|
355
359
|
if (!appiumOptions) {
|
|
356
360
|
return obj;
|
|
357
361
|
}
|
|
358
362
|
|
|
359
|
-
if (!
|
|
363
|
+
if (!util.isPlainObject(appiumOptions)) {
|
|
360
364
|
throw new errors.SessionNotCreatedError(
|
|
361
|
-
`The ${PREFIXED_APPIUM_OPTS_CAP} capability must be an object
|
|
365
|
+
`The ${PREFIXED_APPIUM_OPTS_CAP} capability must be an object`,
|
|
362
366
|
);
|
|
363
367
|
}
|
|
364
|
-
if (
|
|
368
|
+
if (util.isEmpty(appiumOptions)) {
|
|
365
369
|
return obj;
|
|
366
370
|
}
|
|
367
371
|
|
|
368
372
|
log.debug(
|
|
369
|
-
`Found ${PREFIXED_APPIUM_OPTS_CAP} capability present; will promote items inside to caps
|
|
373
|
+
`Found ${PREFIXED_APPIUM_OPTS_CAP} capability present; will promote items inside to caps`,
|
|
370
374
|
);
|
|
371
375
|
|
|
372
376
|
/**
|
|
@@ -374,32 +378,37 @@ export function promoteAppiumOptionsForObject<C extends Constraints>(obj: NSCapa
|
|
|
374
378
|
*/
|
|
375
379
|
const shouldAddVendorPrefix = (capName: string) => !capName.startsWith(APPIUM_VENDOR_PREFIX);
|
|
376
380
|
const verifyIfAcceptable = (capName: string) => {
|
|
377
|
-
if (
|
|
381
|
+
if (typeof capName !== 'string') {
|
|
378
382
|
throw new errors.SessionNotCreatedError(
|
|
379
|
-
`Capability names in ${PREFIXED_APPIUM_OPTS_CAP} must be strings. '${capName}' is unexpected
|
|
383
|
+
`Capability names in ${PREFIXED_APPIUM_OPTS_CAP} must be strings. '${capName}' is unexpected`,
|
|
380
384
|
);
|
|
381
385
|
}
|
|
382
386
|
if (isStandardCap(capName)) {
|
|
383
387
|
throw new errors.SessionNotCreatedError(
|
|
384
|
-
`${PREFIXED_APPIUM_OPTS_CAP} must only contain vendor-specific capabilities. '${capName}' is unexpected
|
|
388
|
+
`${PREFIXED_APPIUM_OPTS_CAP} must only contain vendor-specific capabilities. '${capName}' is unexpected`,
|
|
385
389
|
);
|
|
386
390
|
}
|
|
387
391
|
return capName;
|
|
388
392
|
};
|
|
389
|
-
const preprocessedOptions =
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
+
const preprocessedOptions: Record<string, unknown> = {};
|
|
394
|
+
for (const [key, value] of Object.entries(appiumOptions as Record<string, unknown>)) {
|
|
395
|
+
const verifiedKey = verifyIfAcceptable(key);
|
|
396
|
+
const finalKey = shouldAddVendorPrefix(verifiedKey)
|
|
397
|
+
? `${APPIUM_VENDOR_PREFIX}${verifiedKey}`
|
|
398
|
+
: verifiedKey;
|
|
399
|
+
preprocessedOptions[finalKey] = value;
|
|
400
|
+
}
|
|
393
401
|
// warn if we are going to overwrite any keys on the base caps object
|
|
394
|
-
const overwrittenKeys =
|
|
402
|
+
const overwrittenKeys = Object.keys(obj).filter((key) => key in preprocessedOptions);
|
|
395
403
|
if (overwrittenKeys.length > 0) {
|
|
396
404
|
log.warn(
|
|
397
405
|
`Found capabilities inside ${PREFIXED_APPIUM_OPTS_CAP} that will overwrite ` +
|
|
398
|
-
`capabilities at the top level: ${JSON.stringify(overwrittenKeys)}
|
|
406
|
+
`capabilities at the top level: ${JSON.stringify(overwrittenKeys)}`,
|
|
399
407
|
);
|
|
400
408
|
}
|
|
401
|
-
|
|
402
|
-
|
|
409
|
+
const restObj = omit(obj, PREFIXED_APPIUM_OPTS_CAP) as NSCapabilities<C>;
|
|
410
|
+
return structuredClone({
|
|
411
|
+
...restObj,
|
|
403
412
|
...preprocessedOptions,
|
|
404
413
|
});
|
|
405
414
|
}
|
|
@@ -408,15 +417,17 @@ export function promoteAppiumOptionsForObject<C extends Constraints>(obj: NSCapa
|
|
|
408
417
|
* Return a copy of a capabilities object which has taken everything within the 'options'
|
|
409
418
|
* capability and promoted it to the top level.
|
|
410
419
|
*/
|
|
411
|
-
export function promoteAppiumOptions<C extends Constraints>(
|
|
420
|
+
export function promoteAppiumOptions<C extends Constraints>(
|
|
421
|
+
originalCaps: W3CCapabilities<C>,
|
|
422
|
+
): W3CCapabilities<C> {
|
|
412
423
|
const result = {} as W3CCapabilities<C>;
|
|
413
424
|
const {alwaysMatch, firstMatch} = originalCaps;
|
|
414
|
-
if (
|
|
425
|
+
if (util.isPlainObject(alwaysMatch)) {
|
|
415
426
|
result.alwaysMatch = promoteAppiumOptionsForObject(alwaysMatch);
|
|
416
427
|
} else if ('alwaysMatch' in originalCaps) {
|
|
417
428
|
result.alwaysMatch = alwaysMatch;
|
|
418
429
|
}
|
|
419
|
-
if (
|
|
430
|
+
if (Array.isArray(firstMatch)) {
|
|
420
431
|
result.firstMatch = firstMatch.map(promoteAppiumOptionsForObject);
|
|
421
432
|
} else if ('firstMatch' in originalCaps) {
|
|
422
433
|
result.firstMatch = firstMatch;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import {util} from '@appium/support';
|
|
1
2
|
import type {Constraints, DriverStatus, IBidiCommands} from '@appium/types';
|
|
2
3
|
import type {BaseDriver} from '../driver';
|
|
3
4
|
import {mixin} from './mixin';
|
|
4
|
-
import _ from 'lodash';
|
|
5
5
|
|
|
6
6
|
declare module '../driver' {
|
|
7
7
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -36,16 +36,16 @@ const BidiCommands: IBidiCommands = {
|
|
|
36
36
|
|
|
37
37
|
async bidiStatus<C extends Constraints>(this: BaseDriver<C>): Promise<DriverStatus> {
|
|
38
38
|
const result = await this.getStatus();
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
39
|
+
const base: Record<string, unknown> = util.isPlainObject(result) ? {...result} : {};
|
|
40
|
+
return {
|
|
41
|
+
...base,
|
|
42
|
+
ready: 'ready' in base ? (base.ready as boolean) : true,
|
|
43
|
+
message:
|
|
44
|
+
'message' in base
|
|
45
|
+
? (base.message as string)
|
|
46
|
+
: `${this.constructor.name} is ready to accept commands`,
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
mixin(BidiCommands);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {Constraints, IEventCommands} from '@appium/types';
|
|
2
|
-
import
|
|
1
|
+
import type {Constraints, EventHistory, IEventCommands} from '@appium/types';
|
|
2
|
+
import {util} from '@appium/support';
|
|
3
3
|
import type {BaseDriver} from '../driver';
|
|
4
4
|
import {mixin} from './mixin';
|
|
5
5
|
|
|
@@ -16,7 +16,11 @@ const EventCommands: IEventCommands = {
|
|
|
16
16
|
* separation
|
|
17
17
|
* @param event - the event name
|
|
18
18
|
*/
|
|
19
|
-
async logCustomEvent<C extends Constraints>(
|
|
19
|
+
async logCustomEvent<C extends Constraints>(
|
|
20
|
+
this: BaseDriver<C>,
|
|
21
|
+
vendor: string,
|
|
22
|
+
event: string,
|
|
23
|
+
): Promise<void> {
|
|
20
24
|
this.logEvent(`${vendor}:${event}`);
|
|
21
25
|
},
|
|
22
26
|
|
|
@@ -26,23 +30,25 @@ const EventCommands: IEventCommands = {
|
|
|
26
30
|
* It returns all events if the type is not provided or empty string/array.
|
|
27
31
|
* @returns the event history log object
|
|
28
32
|
*/
|
|
29
|
-
async getLogEvents<C extends Constraints>(
|
|
30
|
-
|
|
33
|
+
async getLogEvents<C extends Constraints>(
|
|
34
|
+
this: BaseDriver<C>,
|
|
35
|
+
type: string | string[],
|
|
36
|
+
): Promise<Partial<EventHistory>> {
|
|
37
|
+
if (util.isEmpty(type)) {
|
|
31
38
|
return this.eventHistory;
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
const typeList =
|
|
41
|
+
const typeList = Array.isArray(type) ? type : [type];
|
|
35
42
|
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
(acc, eventTimes, eventType) => {
|
|
43
|
+
return Object.entries(this.eventHistory).reduce<Partial<EventHistory>>(
|
|
44
|
+
(acc, [eventType, eventTimes]) => {
|
|
39
45
|
if (typeList.includes(eventType)) {
|
|
40
46
|
acc[eventType] = eventTimes;
|
|
41
47
|
}
|
|
42
48
|
return acc;
|
|
43
49
|
},
|
|
44
|
-
{}
|
|
45
|
-
)
|
|
50
|
+
{},
|
|
51
|
+
) as Record<string, number>;
|
|
46
52
|
},
|
|
47
53
|
};
|
|
48
54
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {util} from '@appium/support';
|
|
2
2
|
import {errors, validateExecuteMethodParams} from '../../protocol';
|
|
3
3
|
import type {
|
|
4
4
|
Constraints,
|
|
@@ -17,31 +17,34 @@ declare module '../driver' {
|
|
|
17
17
|
interface BaseDriver<C extends Constraints> extends IExecuteCommands {}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const ExecuteCommands
|
|
20
|
+
const ExecuteCommands = {
|
|
21
21
|
async executeMethod<C extends Constraints>(
|
|
22
22
|
this: BaseDriver<C>,
|
|
23
23
|
script: string,
|
|
24
|
-
protoArgs: readonly [StringRecord<unknown>] | readonly unknown[]
|
|
24
|
+
protoArgs: readonly [StringRecord<unknown>] | readonly unknown[],
|
|
25
25
|
) {
|
|
26
26
|
const Driver = this.constructor as DriverClass<Driver<C>>;
|
|
27
27
|
const commandMetadata = {...Driver.executeMethodMap?.[script]};
|
|
28
28
|
if (!commandMetadata.command) {
|
|
29
|
-
const availableScripts =
|
|
30
|
-
if (
|
|
29
|
+
const availableScripts = Object.keys(Driver.executeMethodMap ?? {});
|
|
30
|
+
if (util.isEmpty(availableScripts)) {
|
|
31
31
|
throw new errors.UnsupportedOperationError(
|
|
32
32
|
`Unsupported execute method '${script}'. ` +
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
`Make sure the installed ${Driver.name} is up-to-date. ` +
|
|
34
|
+
`The current driver version does not define any execute methods.`,
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
|
-
const {sorted: sortedMatches, suggestion} = rankLevenshteinCandidates(
|
|
37
|
+
const {sorted: sortedMatches, suggestion} = rankLevenshteinCandidates(
|
|
38
|
+
script,
|
|
39
|
+
availableScripts,
|
|
40
|
+
);
|
|
38
41
|
throw new errors.UnsupportedOperationError(
|
|
39
42
|
(suggestion
|
|
40
43
|
? `Unsupported execute method '${script}', did you mean '${suggestion}'? `
|
|
41
44
|
: `Unsupported execute method '${script}'. `) +
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
`Make sure the installed ${Driver.name} is up-to-date. ` +
|
|
46
|
+
`Execute methods available in the current driver version are: ` +
|
|
47
|
+
sortedMatches.join(', '),
|
|
45
48
|
);
|
|
46
49
|
}
|
|
47
50
|
const args = validateExecuteMethodParams(protoArgs as any[], commandMetadata.params);
|
|
@@ -49,6 +52,6 @@ const ExecuteCommands: IExecuteCommands = {
|
|
|
49
52
|
const command = this[commandName] as DriverCommand;
|
|
50
53
|
return await command.call(this, ...args);
|
|
51
54
|
},
|
|
52
|
-
};
|
|
55
|
+
} as IExecuteCommands;
|
|
53
56
|
|
|
54
57
|
mixin(ExecuteCommands);
|