@appium/fake-driver 6.0.2 → 6.1.1
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/LICENSE +201 -0
- package/build/lib/commands/alert.d.ts +10 -13
- package/build/lib/commands/alert.d.ts.map +1 -1
- package/build/lib/commands/alert.js +39 -46
- package/build/lib/commands/alert.js.map +1 -1
- package/build/lib/commands/contexts.d.ts +9 -13
- package/build/lib/commands/contexts.d.ts.map +1 -1
- package/build/lib/commands/contexts.js +55 -62
- package/build/lib/commands/contexts.js.map +1 -1
- package/build/lib/commands/element.d.ts +23 -26
- package/build/lib/commands/element.d.ts.map +1 -1
- package/build/lib/commands/element.js +100 -89
- package/build/lib/commands/element.js.map +1 -1
- package/build/lib/commands/find.d.ts +14 -17
- package/build/lib/commands/find.d.ts.map +1 -1
- package/build/lib/commands/find.js +66 -63
- package/build/lib/commands/find.js.map +1 -1
- package/build/lib/commands/general.d.ts +25 -28
- package/build/lib/commands/general.d.ts.map +1 -1
- package/build/lib/commands/general.js +90 -75
- package/build/lib/commands/general.js.map +1 -1
- package/build/lib/desired-caps.d.ts +14 -0
- package/build/lib/desired-caps.d.ts.map +1 -0
- package/build/lib/desired-caps.js +16 -0
- package/build/lib/desired-caps.js.map +1 -0
- package/build/lib/doctor/common.d.ts +11 -0
- package/build/lib/doctor/common.d.ts.map +1 -0
- package/build/lib/doctor/common.js +25 -0
- package/build/lib/doctor/common.js.map +1 -0
- package/build/lib/doctor/fake1.d.ts +3 -0
- package/build/lib/doctor/fake1.d.ts.map +1 -0
- package/build/lib/doctor/fake1.js +6 -0
- package/build/lib/doctor/fake1.js.map +1 -0
- package/build/lib/doctor/fake2.d.ts +3 -0
- package/build/lib/doctor/fake2.d.ts.map +1 -0
- package/build/lib/doctor/fake2.js +6 -0
- package/build/lib/doctor/fake2.js.map +1 -0
- package/build/lib/driver.d.ts +128 -156
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +146 -154
- package/build/lib/driver.js.map +1 -1
- package/build/lib/fake-app.d.ts +37 -45
- package/build/lib/fake-app.d.ts.map +1 -1
- package/build/lib/fake-app.js +57 -34
- package/build/lib/fake-app.js.map +1 -1
- package/build/lib/fake-driver-schema.d.ts +16 -11
- package/build/lib/fake-driver-schema.d.ts.map +1 -1
- package/build/lib/fake-driver-schema.js +2 -17
- package/build/lib/fake-driver-schema.js.map +1 -1
- package/build/lib/fake-element.d.ts +27 -26
- package/build/lib/fake-element.d.ts.map +1 -1
- package/build/lib/fake-element.js +19 -19
- package/build/lib/fake-element.js.map +1 -1
- package/build/lib/index.d.ts +2 -3
- package/build/lib/index.d.ts.map +1 -1
- package/build/lib/index.js +2 -6
- package/build/lib/index.js.map +1 -1
- package/build/lib/logger.d.ts +1 -2
- package/build/lib/logger.d.ts.map +1 -1
- package/build/lib/logger.js +2 -2
- package/build/lib/logger.js.map +1 -1
- package/build/lib/scripts/fake-error.d.ts.map +1 -1
- package/build/lib/scripts/fake-error.js.map +1 -1
- package/build/lib/scripts/fake-stdin.d.ts.map +1 -1
- package/build/lib/scripts/fake-stdin.js.map +1 -1
- package/build/lib/scripts/fake-success.d.ts.map +1 -1
- package/build/lib/scripts/fake-success.js +3 -6
- package/build/lib/scripts/fake-success.js.map +1 -1
- package/build/lib/server.d.ts +2 -1
- package/build/lib/server.d.ts.map +1 -1
- package/build/lib/server.js +3 -5
- package/build/lib/server.js.map +1 -1
- package/build/lib/types.d.ts +1 -1
- package/build/lib/types.d.ts.map +1 -1
- package/lib/commands/alert.ts +31 -61
- package/lib/commands/contexts.ts +50 -73
- package/lib/commands/element.ts +122 -135
- package/lib/commands/find.ts +100 -115
- package/lib/commands/general.ts +122 -127
- package/lib/desired-caps.ts +16 -0
- package/lib/doctor/common.ts +26 -0
- package/lib/doctor/fake1.ts +3 -0
- package/lib/doctor/fake2.ts +3 -0
- package/lib/driver.ts +321 -0
- package/lib/fake-app.ts +234 -0
- package/lib/fake-driver-schema.ts +43 -0
- package/lib/fake-element.ts +128 -0
- package/lib/{index.js → index.ts} +5 -9
- package/lib/logger.ts +3 -0
- package/lib/scripts/{fake-success.js → fake-success.ts} +1 -1
- package/lib/{server.js → server.ts} +3 -4
- package/lib/types.ts +1 -1
- package/package.json +11 -14
- package/tsconfig.json +2 -1
- package/build/lib/commands/index.d.ts +0 -14
- package/build/lib/commands/index.d.ts.map +0 -1
- package/build/lib/commands/index.js +0 -16
- package/build/lib/commands/index.js.map +0 -1
- package/build/lib/commands/mixin.d.ts +0 -11
- package/build/lib/commands/mixin.d.ts.map +0 -1
- package/build/lib/commands/mixin.js +0 -16
- package/build/lib/commands/mixin.js.map +0 -1
- package/lib/commands/index.ts +0 -14
- package/lib/commands/mixin.ts +0 -13
- package/lib/driver.js +0 -356
- package/lib/fake-app.js +0 -190
- package/lib/fake-driver-schema.js +0 -35
- package/lib/fake-element.js +0 -117
- package/lib/logger.js +0 -5
- package/test/fixtures/app.xml +0 -38
- /package/lib/scripts/{fake-error.js → fake-error.ts} +0 -0
- /package/lib/scripts/{fake-stdin.js → fake-stdin.ts} +0 -0
package/lib/driver.js
DELETED
|
@@ -1,356 +0,0 @@
|
|
|
1
|
-
import B from 'bluebird';
|
|
2
|
-
import {BaseDriver, errors} from 'appium/driver';
|
|
3
|
-
import {FakeApp} from './fake-app';
|
|
4
|
-
|
|
5
|
-
const FAKE_DRIVER_CONSTRAINTS = /** @type {const} */ ({
|
|
6
|
-
app: {
|
|
7
|
-
presence: true,
|
|
8
|
-
isString: true,
|
|
9
|
-
},
|
|
10
|
-
uniqueApp: {
|
|
11
|
-
isBoolean: true,
|
|
12
|
-
},
|
|
13
|
-
runClock: {
|
|
14
|
-
isBoolean: true,
|
|
15
|
-
},
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Constraints for {@linkcode FakeDriver}'s capabilities
|
|
20
|
-
* @typedef {typeof FAKE_DRIVER_CONSTRAINTS} FakeDriverConstraints
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @template [Thing=any]
|
|
25
|
-
* @extends {BaseDriver<FakeDriverConstraints>}
|
|
26
|
-
* @implements {ExternalDriver<FakeDriverConstraints>}
|
|
27
|
-
*/
|
|
28
|
-
export class FakeDriver extends BaseDriver {
|
|
29
|
-
/**
|
|
30
|
-
* @type {FakeDriverConstraints}
|
|
31
|
-
* @readonly
|
|
32
|
-
*/
|
|
33
|
-
desiredCapConstraints;
|
|
34
|
-
|
|
35
|
-
/** @type {string} */
|
|
36
|
-
curContext;
|
|
37
|
-
|
|
38
|
-
/** @type {FakeApp} */
|
|
39
|
-
appModel;
|
|
40
|
-
|
|
41
|
-
/** @type {boolean} */
|
|
42
|
-
_proxyActive;
|
|
43
|
-
|
|
44
|
-
/** @type {boolean} */
|
|
45
|
-
shook;
|
|
46
|
-
|
|
47
|
-
/** @type {string?} */
|
|
48
|
-
focusedElId;
|
|
49
|
-
|
|
50
|
-
/** @type {Thing?} */
|
|
51
|
-
fakeThing;
|
|
52
|
-
|
|
53
|
-
/** @type {number} */
|
|
54
|
-
maxElId;
|
|
55
|
-
|
|
56
|
-
/** @type {Record<string,import('./fake-element').FakeElement>} */
|
|
57
|
-
elMap;
|
|
58
|
-
|
|
59
|
-
/** @type {string|null} */
|
|
60
|
-
_bidiProxyUrl;
|
|
61
|
-
|
|
62
|
-
/** @type {boolean} */
|
|
63
|
-
_clockRunning = false;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
constructor(
|
|
67
|
-
opts = /** @type {import('@appium/types').InitialOpts} */ ({}),
|
|
68
|
-
shouldValidateCaps = true,
|
|
69
|
-
) {
|
|
70
|
-
super(opts, shouldValidateCaps);
|
|
71
|
-
this.curContext = 'NATIVE_APP';
|
|
72
|
-
this.elMap = {};
|
|
73
|
-
this.focusedElId = null;
|
|
74
|
-
this.maxElId = 0;
|
|
75
|
-
this.fakeThing = null;
|
|
76
|
-
this._proxyActive = false;
|
|
77
|
-
this.shook = false;
|
|
78
|
-
this.appModel = new FakeApp();
|
|
79
|
-
this.desiredCapConstraints = FAKE_DRIVER_CONSTRAINTS;
|
|
80
|
-
this._bidiProxyUrl = null;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
proxyActive() {
|
|
84
|
-
return this._proxyActive;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
canProxy() {
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
get bidiProxyUrl() {
|
|
92
|
-
return this._bidiProxyUrl;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
proxyReqRes(req, res) {
|
|
96
|
-
// fake implementation of jwp proxy req res
|
|
97
|
-
res.set('content-type', 'application/json');
|
|
98
|
-
const resBodyObj = {value: 'proxied via proxyReqRes'};
|
|
99
|
-
const match = req.originalUrl.match(/\/session\/([^/]+)/);
|
|
100
|
-
resBodyObj.sessionId = match ? match[1] : null;
|
|
101
|
-
res.status(200).send(JSON.stringify(resBodyObj));
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* @template [T=any]
|
|
106
|
-
* @returns {Promise<T>}
|
|
107
|
-
*/
|
|
108
|
-
async proxyCommand(/*url, method, body*/) {
|
|
109
|
-
return /** @type {T} */ (/** @type {unknown} */ ('proxied via proxyCommand'));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Comment for `createSession` in `FakeDriver`
|
|
114
|
-
* @param {W3CFakeDriverCaps} w3cCapabilities1 W3C Capabilities
|
|
115
|
-
* @param {W3CFakeDriverCaps} [w3cCapabilities2] W3C Capabilities
|
|
116
|
-
* @param {W3CFakeDriverCaps} [w3cCapabilities3] W3C Capabilities
|
|
117
|
-
* @param {import('@appium/types').DriverData[]} [driverData] Other session data
|
|
118
|
-
* @override
|
|
119
|
-
* @returns {Promise<[string,FakeDriverCaps]>}
|
|
120
|
-
*/
|
|
121
|
-
async createSession(w3cCapabilities1, w3cCapabilities2, w3cCapabilities3, driverData = []) {
|
|
122
|
-
// BaseDriver automatically validates capabilities using desiredCapConstraints
|
|
123
|
-
|
|
124
|
-
// check to see if any other sessions have set uniqueApp. If so, emulate
|
|
125
|
-
// not being able to start a session because of system resources
|
|
126
|
-
for (let d of driverData) {
|
|
127
|
-
if (d.isUnique) {
|
|
128
|
-
throw new errors.SessionNotCreatedError(
|
|
129
|
-
'Cannot start session; another ' +
|
|
130
|
-
'unique session is in progress that requires all resources',
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
let [sessionId, caps] = /** @type {[string, FakeDriverCaps]} */ (
|
|
136
|
-
await super.createSession(w3cCapabilities1, w3cCapabilities2, w3cCapabilities3, driverData)
|
|
137
|
-
);
|
|
138
|
-
this.caps = caps;
|
|
139
|
-
await this.appModel.loadApp(caps.app);
|
|
140
|
-
if (this.caps.runClock) {
|
|
141
|
-
this.startClock();
|
|
142
|
-
}
|
|
143
|
-
return [sessionId, caps];
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* @param {string} [sessionId]
|
|
148
|
-
* @returns {Promise<void>}
|
|
149
|
-
*/
|
|
150
|
-
async deleteSession(sessionId) {
|
|
151
|
-
this.stopClock();
|
|
152
|
-
return await super.deleteSession(sessionId);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* @returns {Promise<string>}
|
|
157
|
-
*/
|
|
158
|
-
async getWindowHandle() {
|
|
159
|
-
return '1';
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* @returns {Promise<string[]>}
|
|
164
|
-
*/
|
|
165
|
-
async getWindowHandles() {
|
|
166
|
-
return ['1'];
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
get driverData() {
|
|
170
|
-
return {
|
|
171
|
-
isUnique: !!this.caps.uniqueApp,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
async getFakeThing() {
|
|
176
|
-
await B.delay(1);
|
|
177
|
-
return this.fakeThing;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
async startClock() {
|
|
181
|
-
this._clockRunning = true;
|
|
182
|
-
while (this._clockRunning) {
|
|
183
|
-
await B.delay(500);
|
|
184
|
-
this.eventEmitter.emit('bidiEvent', {
|
|
185
|
-
method: 'appium:clock.currentTime',
|
|
186
|
-
params: {time: Date.now()},
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
stopClock() {
|
|
192
|
-
this._clockRunning = false;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Set the 'thing' value (so that it can be retrieved later)
|
|
197
|
-
*
|
|
198
|
-
* @param {Thing} thing
|
|
199
|
-
* @returns {Promise<null>}
|
|
200
|
-
*/
|
|
201
|
-
async setFakeThing(thing) {
|
|
202
|
-
await B.delay(1);
|
|
203
|
-
this.fakeThing = thing;
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Get the driver args that were sent in via the CLI
|
|
209
|
-
*
|
|
210
|
-
* @returns {Promise<typeof this.cliArgs>}
|
|
211
|
-
*/
|
|
212
|
-
async getFakeDriverArgs() {
|
|
213
|
-
await B.delay(1);
|
|
214
|
-
return this.cliArgs;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* This is a command that will return a list of deprecated command names called
|
|
219
|
-
*
|
|
220
|
-
* @returns {Promise<string[]>}
|
|
221
|
-
*/
|
|
222
|
-
async getDeprecatedCommandsCalled() {
|
|
223
|
-
await B.delay(1);
|
|
224
|
-
// TODO: Properly get deprecatedCommandsLogged list from the base-driver
|
|
225
|
-
return [];
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* This is a command that exists just to be an example of a deprecated command
|
|
230
|
-
*
|
|
231
|
-
* @returns {Promise<void>}
|
|
232
|
-
*/
|
|
233
|
-
async callDeprecatedCommand() {
|
|
234
|
-
await B.delay(1);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* @param {number} num1
|
|
239
|
-
* @param {number} num2
|
|
240
|
-
*/
|
|
241
|
-
async doSomeMath(num1, num2) {
|
|
242
|
-
await B.delay(1);
|
|
243
|
-
return num1 + num2;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* @param {number} num1
|
|
248
|
-
* @param {number} num2
|
|
249
|
-
*/
|
|
250
|
-
async doSomeMath2(num1, num2) {
|
|
251
|
-
await B.delay(1);
|
|
252
|
-
return num1 + num2;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
static newBidiCommands = /** @type {const} */({
|
|
256
|
-
'appium:fake': {
|
|
257
|
-
getFakeThing: {
|
|
258
|
-
command: 'getFakeThing',
|
|
259
|
-
},
|
|
260
|
-
setFakeThing: {
|
|
261
|
-
command: 'setFakeThing',
|
|
262
|
-
params: {
|
|
263
|
-
required: ['thing'],
|
|
264
|
-
},
|
|
265
|
-
},
|
|
266
|
-
doSomeMath: {
|
|
267
|
-
command: 'doSomeMath',
|
|
268
|
-
params: {
|
|
269
|
-
required: ['num1', 'num2'],
|
|
270
|
-
},
|
|
271
|
-
},
|
|
272
|
-
doSomeMath2: {
|
|
273
|
-
command: 'doSomeMath2',
|
|
274
|
-
params: {
|
|
275
|
-
required: ['num1', 'num2'],
|
|
276
|
-
},
|
|
277
|
-
},
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
static newMethodMap = /** @type {const} */ ({
|
|
282
|
-
'/session/:sessionId/fakedriver': {
|
|
283
|
-
GET: {command: 'getFakeThing'},
|
|
284
|
-
/**
|
|
285
|
-
* Sets a fake thing
|
|
286
|
-
*/
|
|
287
|
-
POST: {command: 'setFakeThing', payloadParams: {required: ['thing']}},
|
|
288
|
-
},
|
|
289
|
-
'/session/:sessionId/fakedriverargs': {
|
|
290
|
-
GET: {command: 'getFakeDriverArgs'},
|
|
291
|
-
},
|
|
292
|
-
'/session/:sessionId/deprecated': {
|
|
293
|
-
POST: {command: 'callDeprecatedCommand', deprecated: true},
|
|
294
|
-
},
|
|
295
|
-
// this next one exists to override a deprecated method
|
|
296
|
-
'/session/:sessionId/doubleclick': {
|
|
297
|
-
POST: {command: 'doubleClick'},
|
|
298
|
-
},
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
static executeMethodMap = /** @type {const} */ ({
|
|
302
|
-
'fake: addition': {
|
|
303
|
-
command: 'fakeAddition',
|
|
304
|
-
params: {required: ['num1', 'num2'], optional: ['num3']},
|
|
305
|
-
},
|
|
306
|
-
/**
|
|
307
|
-
* Gets a thing (a fake thing)
|
|
308
|
-
*/
|
|
309
|
-
'fake: getThing': {
|
|
310
|
-
command: 'getFakeThing',
|
|
311
|
-
},
|
|
312
|
-
'fake: setThing': {
|
|
313
|
-
command: 'setFakeThing',
|
|
314
|
-
params: {required: ['thing']},
|
|
315
|
-
},
|
|
316
|
-
'fake: getDeprecatedCommandsCalled': {
|
|
317
|
-
command: 'getDeprecatedCommandsCalled',
|
|
318
|
-
},
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
static fakeRoute(req, res) {
|
|
322
|
-
res.send(JSON.stringify({fakedriver: 'fakeResponse'}));
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
static async updateServer(expressApp, httpServer, cliArgs) {
|
|
326
|
-
|
|
327
|
-
expressApp.all('/fakedriver', FakeDriver.fakeRoute);
|
|
328
|
-
expressApp.all('/fakedriverCliArgs', (req, res) => {
|
|
329
|
-
res.send(JSON.stringify(cliArgs));
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
import './commands';
|
|
335
|
-
|
|
336
|
-
export default FakeDriver;
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* @typedef {import('./types').FakeDriverCaps} FakeDriverCaps
|
|
340
|
-
* @typedef {import('./types').W3CFakeDriverCaps} W3CFakeDriverCaps
|
|
341
|
-
* @typedef {import('@appium/types').Element} Element
|
|
342
|
-
*/
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* @template {import('@appium/types').Driver} D
|
|
346
|
-
* @typedef {import('@appium/types').DriverClass<D>} DriverClass
|
|
347
|
-
*/
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* @template {import('@appium/types').Constraints} C
|
|
351
|
-
* @typedef {import('@appium/types').ExternalDriver<C>} ExternalDriver
|
|
352
|
-
*/
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* @typedef {import('@appium/types').Orientation} Orientation
|
|
356
|
-
*/
|
package/lib/fake-app.js
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import {fs} from 'appium/support';
|
|
2
|
-
import {readFileSync} from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import XMLDom from '@xmldom/xmldom';
|
|
5
|
-
import * as xpath from 'xpath';
|
|
6
|
-
import log from './logger';
|
|
7
|
-
import _ from 'lodash';
|
|
8
|
-
import {FakeElement} from './fake-element';
|
|
9
|
-
|
|
10
|
-
const SCREENSHOT = path.join(__dirname, 'screen.png');
|
|
11
|
-
|
|
12
|
-
export class FakeApp {
|
|
13
|
-
constructor() {
|
|
14
|
-
this.dom = null;
|
|
15
|
-
this.activeDom = null;
|
|
16
|
-
this.activeWebview = null;
|
|
17
|
-
this.activeFrame = null;
|
|
18
|
-
this.activeAlert = null;
|
|
19
|
-
this.lat = 0;
|
|
20
|
-
this.long = 0;
|
|
21
|
-
this._width = null;
|
|
22
|
-
this._height = null;
|
|
23
|
-
this.rawXml = '';
|
|
24
|
-
/** @type {import('./driver').Orientation} */
|
|
25
|
-
this.currentOrientation = 'PORTRAIT';
|
|
26
|
-
/** @type {import('@appium/types').ActionSequence[][]} */
|
|
27
|
-
this.actionLog = [];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
get title() {
|
|
31
|
-
let nodes = this.xpathQuery('//title');
|
|
32
|
-
if (!_.isArray(nodes) || nodes.length < 1) {
|
|
33
|
-
throw new Error('No title!');
|
|
34
|
-
}
|
|
35
|
-
const node = /** @type {Node} */ (nodes[0]);
|
|
36
|
-
return /** @type {ElementWithData} */ (node.firstChild).data;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* @type {import('@appium/types').Location}
|
|
41
|
-
*/
|
|
42
|
-
get currentGeoLocation() {
|
|
43
|
-
return {
|
|
44
|
-
latitude: this.lat,
|
|
45
|
-
longitude: this.long,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
get orientation() {
|
|
50
|
-
return this.currentOrientation;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
set orientation(o) {
|
|
54
|
-
this.currentOrientation = o;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* @type {number}
|
|
59
|
-
*/
|
|
60
|
-
get width() {
|
|
61
|
-
if (this._width === null) {
|
|
62
|
-
this.setDims();
|
|
63
|
-
}
|
|
64
|
-
return /** @type {number} */ (this._width);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* @type {number}
|
|
69
|
-
*/
|
|
70
|
-
get height() {
|
|
71
|
-
if (this._width === null) {
|
|
72
|
-
this.setDims();
|
|
73
|
-
}
|
|
74
|
-
return /** @type {number} */ (this._width);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
setDims() {
|
|
78
|
-
const nodes = this.xpathQuery('//app');
|
|
79
|
-
if (!_.isArray(nodes)) {
|
|
80
|
-
throw new Error(
|
|
81
|
-
'Cannot fetch app dimensions because no corresponding node has benn found in the source'
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
const app = new FakeElement(nodes[0], this);
|
|
85
|
-
this._width = parseInt(app.nodeAttrs.width, 10);
|
|
86
|
-
this._height = parseInt(app.nodeAttrs.height, 10);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async loadApp(appPath) {
|
|
90
|
-
log.info(`Loading Mock app model at ${appPath}`);
|
|
91
|
-
let data = await fs.readFile(appPath);
|
|
92
|
-
log.info('Parsing Mock app XML');
|
|
93
|
-
this.rawXml = data.toString();
|
|
94
|
-
this.rawXml = this.rawXml.replace('<app ', '<AppiumAUT><app ');
|
|
95
|
-
this.rawXml = this.rawXml.replace('<app>', '<AppiumAUT><app>');
|
|
96
|
-
this.rawXml = this.rawXml.replace('</app>', '</app></AppiumAUT>');
|
|
97
|
-
this.dom = new XMLDom.DOMParser().parseFromString(this.rawXml, XMLDom.MIME_TYPE.XML_TEXT);
|
|
98
|
-
this.activeDom = this.dom;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
getWebviews() {
|
|
102
|
-
const nodes = this.xpathQuery('//MockWebView/*[1]');
|
|
103
|
-
return _.isArray(nodes) ? nodes.map((n) => new FakeWebView(n)) : [];
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
activateWebview(wv) {
|
|
107
|
-
this.activeWebview = wv;
|
|
108
|
-
let fragment = new XMLDom.XMLSerializer().serializeToString(wv.node);
|
|
109
|
-
this.activeDom = new XMLDom.DOMParser().parseFromString(fragment, XMLDom.MIME_TYPE.XML_TEXT);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
deactivateWebview() {
|
|
113
|
-
this.activeWebview = null;
|
|
114
|
-
this.activeDom = this.dom;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
activateFrame(frame) {
|
|
118
|
-
this.activeFrame = frame;
|
|
119
|
-
let fragment = new XMLDom.XMLSerializer().serializeToString(frame);
|
|
120
|
-
this.activeDom = new XMLDom.DOMParser().parseFromString(fragment, XMLDom.MIME_TYPE.XML_TEXT);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
deactivateFrame() {
|
|
124
|
-
this.activeFrame = null;
|
|
125
|
-
this.activateWebview(this.activeWebview);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
xpathQuery(sel, ctx) {
|
|
129
|
-
return xpath.select(sel, ctx || this.activeDom);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
idQuery(id, ctx) {
|
|
133
|
-
return this.xpathQuery(`//*[@id="${id}"]`, ctx);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
classQuery(className, ctx) {
|
|
137
|
-
return this.xpathQuery(`//${className}`, ctx);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
cssQuery(css, ctx) {
|
|
141
|
-
if (css.startsWith('#')) {
|
|
142
|
-
return this.idQuery(css.slice(1), ctx);
|
|
143
|
-
}
|
|
144
|
-
if (css.startsWith('.')) {
|
|
145
|
-
return this.classQuery(css.slice(1), ctx);
|
|
146
|
-
}
|
|
147
|
-
return this.classQuery(css, ctx);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
hasAlert() {
|
|
151
|
-
return this.activeAlert !== null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
setAlertText(text) {
|
|
155
|
-
if (!this.activeAlert?.hasPrompt()) {
|
|
156
|
-
throw new Error('No prompt to set text of');
|
|
157
|
-
}
|
|
158
|
-
this.activeAlert?.setAttr('prompt', text);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
showAlert(alertId) {
|
|
162
|
-
let nodes = this.xpathQuery(`//alert[@id="${alertId}"]`);
|
|
163
|
-
if (!_.isArray(nodes) || _.isEmpty(nodes)) {
|
|
164
|
-
throw new Error(`Alert ${alertId} doesn't exist!`);
|
|
165
|
-
}
|
|
166
|
-
this.activeAlert = new FakeElement(nodes[0], this);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
alertText() {
|
|
170
|
-
return this.activeAlert?.getAttr('prompt') || this.activeAlert?.nodeAttrs.text;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
handleAlert() {
|
|
174
|
-
this.activeAlert = null;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
getScreenshot() {
|
|
178
|
-
return readFileSync(SCREENSHOT, 'base64');
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
class FakeWebView {
|
|
183
|
-
constructor(node) {
|
|
184
|
-
this.node = node;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* @typedef {Element & {data: any}} ElementWithData
|
|
190
|
-
*/
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// this could be a .json file, too:
|
|
2
|
-
// {
|
|
3
|
-
// "$schema": "http://json-schema.org/draft-07/schema",
|
|
4
|
-
// "$id": "https://appium.io/fake-driver.json",
|
|
5
|
-
// "type": "object",
|
|
6
|
-
// "title": "Fake Driver Configuration",
|
|
7
|
-
// "description": "A schema for Fake Driver arguments",
|
|
8
|
-
// "properties": {
|
|
9
|
-
// "answer": {
|
|
10
|
-
// "type": "number",
|
|
11
|
-
// "description": "The answer to life, the universe, and everything",
|
|
12
|
-
// "default": 42
|
|
13
|
-
// }
|
|
14
|
-
// }
|
|
15
|
-
// }
|
|
16
|
-
|
|
17
|
-
export default {
|
|
18
|
-
type: 'object',
|
|
19
|
-
title: 'Fake Driver Configuration',
|
|
20
|
-
description: 'A schema for Fake Driver arguments',
|
|
21
|
-
properties: {
|
|
22
|
-
// the prop name will always be normalized to camelCase
|
|
23
|
-
'silly-web-server-port': {
|
|
24
|
-
type: 'integer',
|
|
25
|
-
minimum: 1,
|
|
26
|
-
maximum: 65535,
|
|
27
|
-
description: 'The port to use for the fake web server',
|
|
28
|
-
},
|
|
29
|
-
sillyWebServerHost: {
|
|
30
|
-
type: 'string',
|
|
31
|
-
description: 'The host to use for the fake web server',
|
|
32
|
-
default: 'sillyhost',
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
};
|
package/lib/fake-element.js
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
|
-
import XMLDom from '@xmldom/xmldom';
|
|
3
|
-
|
|
4
|
-
export class FakeElement {
|
|
5
|
-
/** @type {FakeApp} */
|
|
6
|
-
app;
|
|
7
|
-
|
|
8
|
-
/** @type {string} */
|
|
9
|
-
type;
|
|
10
|
-
|
|
11
|
-
/** @type {Record<string,string>} */
|
|
12
|
-
nodeAttrs;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @param {any} xmlNode
|
|
16
|
-
* @param {FakeApp} app
|
|
17
|
-
*/
|
|
18
|
-
constructor(xmlNode, app) {
|
|
19
|
-
this.app = app;
|
|
20
|
-
this.node = xmlNode;
|
|
21
|
-
this.nodeAttrs = {};
|
|
22
|
-
this.type = this.node.tagName;
|
|
23
|
-
this.attrs = {};
|
|
24
|
-
this.css = {};
|
|
25
|
-
for (let {name, value} of _.values(this.node.attributes)) {
|
|
26
|
-
this.nodeAttrs[name] = value;
|
|
27
|
-
}
|
|
28
|
-
this.parseCss();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
parseCss() {
|
|
32
|
-
if (this.nodeAttrs.style) {
|
|
33
|
-
let segments = this.nodeAttrs.style.split(';');
|
|
34
|
-
for (let s of segments) {
|
|
35
|
-
let [prop, val] = s.split(':');
|
|
36
|
-
prop = prop.trim();
|
|
37
|
-
val = val.trim();
|
|
38
|
-
this.css[prop] = val;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
get tagName() {
|
|
44
|
-
return this.node.tagName;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
setAttr(k, v) {
|
|
48
|
-
this.attrs[k] = v;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
getAttr(k) {
|
|
52
|
-
return this.attrs[k] || '';
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
isVisible() {
|
|
56
|
-
return this.nodeAttrs.visible !== 'false';
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
isEnabled() {
|
|
60
|
-
return this.nodeAttrs.enabled !== 'false';
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
isSelected() {
|
|
64
|
-
return this.nodeAttrs.selected === 'true';
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
getLocation() {
|
|
68
|
-
return {
|
|
69
|
-
x: parseFloat(this.nodeAttrs.left || '0'),
|
|
70
|
-
y: parseFloat(this.nodeAttrs.top || '0'),
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
getElementRect() {
|
|
75
|
-
return {...this.getLocation(), ...this.getSize()};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
getSize() {
|
|
79
|
-
return {
|
|
80
|
-
width: parseFloat(this.nodeAttrs.width || '0'),
|
|
81
|
-
height: parseFloat(this.nodeAttrs.height || '0'),
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
click() {
|
|
86
|
-
let curClicks = this.getAttr('clicks') || 0;
|
|
87
|
-
this.setAttr('clicks', curClicks + 1);
|
|
88
|
-
let alertId = this.nodeAttrs.showAlert;
|
|
89
|
-
if (alertId) {
|
|
90
|
-
this.app.showAlert(alertId);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
equals(other) {
|
|
95
|
-
return this.node === other.node;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
hasPrompt() {
|
|
99
|
-
return this.nodeAttrs.hasPrompt === 'true';
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
getCss(prop) {
|
|
103
|
-
if (_.has(this.css, prop)) {
|
|
104
|
-
return this.css[prop];
|
|
105
|
-
}
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
get xmlFragment() {
|
|
110
|
-
let frag = new XMLDom.XMLSerializer().serializeToString(this.node);
|
|
111
|
-
return new XMLDom.DOMParser().parseFromString(frag, XMLDom.MIME_TYPE.XML_TEXT);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* @typedef {import('./fake-app').FakeApp} FakeApp
|
|
117
|
-
*/
|