@xenon-device-management/xenon 1.17.2 → 1.17.4
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/lib/package.json
CHANGED
|
@@ -319,8 +319,7 @@ let SessionLifecycleService = class SessionLifecycleService {
|
|
|
319
319
|
const wdaUrl = `http://127.0.0.1:${streamInfo.wdaPort}`;
|
|
320
320
|
yield (0, device_service_1.updateDeviceProgress)(device.udid, device.host, 'WDA active, finalizing session...');
|
|
321
321
|
yield new Promise((resolve) => setTimeout(resolve, 2000));
|
|
322
|
-
|
|
323
|
-
this.injectWDAUrl(caps, wdaUrl, bundleId);
|
|
322
|
+
this.injectWDAUrl(caps, wdaUrl);
|
|
324
323
|
}
|
|
325
324
|
catch (err) {
|
|
326
325
|
this.logger.warn(`⚠️ Artisan WDA: Pre-session boot failed: ${err.message}`);
|
|
@@ -329,12 +328,7 @@ let SessionLifecycleService = class SessionLifecycleService {
|
|
|
329
328
|
}
|
|
330
329
|
else if (isWdaActive) {
|
|
331
330
|
const wdaUrl = `http://127.0.0.1:${streamStatus.wdaPort}`;
|
|
332
|
-
|
|
333
|
-
// is the branch that runs in the ordinary case — a preview already
|
|
334
|
-
// open when a test starts. Omitting it told Appium to manage "the
|
|
335
|
-
// preinstalled WDA" without saying which, and it removed ours.
|
|
336
|
-
const bundleId = yield streamService.detectWDABundleId(device.udid);
|
|
337
|
-
this.injectWDAUrl(caps, wdaUrl, bundleId);
|
|
331
|
+
this.injectWDAUrl(caps, wdaUrl);
|
|
338
332
|
}
|
|
339
333
|
const hasWdaUrl = lodash_1.default.has(caps.alwaysMatch, 'appium:webDriverAgentUrl') ||
|
|
340
334
|
lodash_1.default.has((_a = caps.firstMatch) === null || _a === void 0 ? void 0 : _a[0], 'appium:webDriverAgentUrl');
|
|
@@ -347,7 +341,7 @@ let SessionLifecycleService = class SessionLifecycleService {
|
|
|
347
341
|
}
|
|
348
342
|
});
|
|
349
343
|
}
|
|
350
|
-
injectWDAUrl(caps, wdaUrl
|
|
344
|
+
injectWDAUrl(caps, wdaUrl) {
|
|
351
345
|
var _a, _b, _c;
|
|
352
346
|
// W3C capability rules forbid a property from appearing in BOTH alwaysMatch
|
|
353
347
|
// and a firstMatch entry; appium rejects the session with "property
|
|
@@ -359,34 +353,35 @@ let SessionLifecycleService = class SessionLifecycleService {
|
|
|
359
353
|
if (!target)
|
|
360
354
|
return;
|
|
361
355
|
const other = target === caps.alwaysMatch ? (_c = caps.firstMatch) === null || _c === void 0 ? void 0 : _c[0] : caps.alwaysMatch;
|
|
356
|
+
// The URL, and only the URL. Xenon owns this WebDriverAgent: it launched
|
|
357
|
+
// it through go-ios and keeps the XCTest session alive for the stream. The
|
|
358
|
+
// driver is a guest here, and `webDriverAgentUrl` is precisely how you say
|
|
359
|
+
// that — `selectWdaStartupStrategyName` returns 'existing-url' on it before
|
|
360
|
+
// considering anything else, and that strategy's own quit() reads
|
|
361
|
+
// "Stopping neither xcodebuild nor XCTest session since WDA lifecycle is
|
|
362
|
+
// not managed by this driver".
|
|
362
363
|
target['appium:webDriverAgentUrl'] = wdaUrl;
|
|
363
|
-
// `usePreinstalledWDA` and
|
|
364
|
-
// all. On its own, the first one sends the driver down
|
|
365
|
-
// `preparePreinstalled`, which calls `cleanupApps(driver,
|
|
366
|
-
// [driver.wda.bundleIdForXctest])` — that removes every installed app
|
|
367
|
-
// whose CFBundleName is WebDriverAgentRunner except the one bundle id it
|
|
368
|
-
// was told to keep. Without `updatedWDABundleId` that keep-list is the
|
|
369
|
-
// driver's own default, `com.facebook.WebDriverAgentRunner.xctrunner`, so
|
|
370
|
-
// the WDA Xenon just launched (`com.qasecret.…` here) is not on it and is
|
|
371
|
-
// uninstalled from the device mid-session. Observed twice on a real
|
|
372
|
-
// iPhone: "Removing WebDriverAgent runner app
|
|
373
|
-
// 'com.qasecret.WebDriverAgentRunner.xctrunner'", after which every iOS
|
|
374
|
-
// feature fails with "WebDriverAgent is not installed" until someone
|
|
375
|
-
// re-signs and reinstalls it by hand.
|
|
364
|
+
// `usePreinstalledWDA` is removed, never added, and this is the whole fix.
|
|
376
365
|
//
|
|
377
|
-
//
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
366
|
+
// It does not change which strategy runs — the URL already won that — it
|
|
367
|
+
// only makes `launchOnce` call `preparePreinstalled` first, which is:
|
|
368
|
+
//
|
|
369
|
+
// await driver.mobileKillApp(driver.wda.bundleIdForXctest);
|
|
370
|
+
// await cleanupApps(driver, [driver.wda.bundleIdForXctest]);
|
|
371
|
+
//
|
|
372
|
+
// Kill the app, then uninstall every WebDriverAgentRunner not on a
|
|
373
|
+
// one-entry keep-list. Against a WDA that Xenon is hosting that is
|
|
374
|
+
// exactly wrong twice over, and both halves were observed on a real
|
|
375
|
+
// iPhone: the uninstall as "Removing WebDriverAgent runner app
|
|
376
|
+
// 'com.qasecret.WebDriverAgentRunner.xctrunner'", leaving the device with
|
|
377
|
+
// no WDA until someone re-signs one by hand; and the kill as the driver's
|
|
378
|
+
// own next request failing with ECONNRESET, because go-ios's runwda was
|
|
379
|
+
// gone while iproxy still held the port.
|
|
380
|
+
//
|
|
381
|
+
// Getting the keep-list right only converts the uninstall into the kill.
|
|
382
|
+
// The driver must not be invited to manage this app at all.
|
|
383
|
+
delete target['appium:usePreinstalledWDA'];
|
|
384
|
+
delete target['appium:updatedWDABundleId'];
|
|
390
385
|
// These conflict with a pre-installed WDA URL — strip from both buckets.
|
|
391
386
|
for (const bucket of [target, other]) {
|
|
392
387
|
if (!bucket)
|
|
@@ -19,34 +19,31 @@ describe('SessionLifecycleService.injectWDAUrl — W3C capability composition',
|
|
|
19
19
|
alwaysMatch: { 'appium:automationName': 'XCUITest' },
|
|
20
20
|
firstMatch: [{ 'appium:udid': 'abc' }],
|
|
21
21
|
};
|
|
22
|
-
injectWDAUrl(caps, URL
|
|
22
|
+
injectWDAUrl(caps, URL);
|
|
23
23
|
// Present in alwaysMatch...
|
|
24
24
|
expect(caps.alwaysMatch['appium:webDriverAgentUrl']).to.equal(URL);
|
|
25
|
-
expect(caps.alwaysMatch['appium:usePreinstalledWDA']).to.equal(true);
|
|
26
|
-
expect(caps.alwaysMatch['appium:updatedWDABundleId']).to.equal('com.qasecret.WebDriverAgentRunner');
|
|
27
25
|
// ...and NOT in firstMatch — the bug was duplicating into both, which appium
|
|
28
26
|
// rejects with "property 'webDriverAgentUrl' should not exist on both
|
|
29
27
|
// primary and secondary object".
|
|
30
28
|
expect(caps.firstMatch[0]).to.not.have.property('appium:webDriverAgentUrl');
|
|
31
|
-
expect(caps.firstMatch[0]).to.not.have.property('appium:usePreinstalledWDA');
|
|
32
|
-
expect(caps.firstMatch[0]).to.not.have.property('appium:updatedWDABundleId');
|
|
33
29
|
});
|
|
34
30
|
it('falls back to firstMatch[0] when there is no alwaysMatch', () => {
|
|
35
31
|
const caps = { firstMatch: [{ 'appium:udid': 'abc' }] };
|
|
36
|
-
injectWDAUrl(caps, URL
|
|
32
|
+
injectWDAUrl(caps, URL);
|
|
37
33
|
expect(caps.firstMatch[0]['appium:webDriverAgentUrl']).to.equal(URL);
|
|
38
|
-
expect(caps.firstMatch[0]['appium:usePreinstalledWDA']).to.equal(true);
|
|
39
34
|
});
|
|
40
|
-
it('
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
// `
|
|
44
|
-
//
|
|
35
|
+
it('never invites the driver to manage the WDA, in either bucket', () => {
|
|
36
|
+
// Two earlier versions of this case asserted the opposite, and each
|
|
37
|
+
// pinned a behaviour that damaged a real device: `usePreinstalledWDA`
|
|
38
|
+
// makes `launchOnce` run `preparePreinstalled`, which kills the runner
|
|
39
|
+
// and uninstalls every sibling not on a one-entry keep-list. Xenon hosts
|
|
40
|
+
// this WDA itself, so the driver must never be invited to manage it. See
|
|
45
41
|
// test/unit/wda-cap-injection.spec.ts for the full chain.
|
|
46
42
|
const caps = { firstMatch: [{ 'appium:udid': 'abc' }] };
|
|
47
43
|
injectWDAUrl(caps, URL);
|
|
48
44
|
expect(caps.firstMatch[0]['appium:webDriverAgentUrl']).to.equal(URL);
|
|
49
45
|
expect(caps.firstMatch[0]).to.not.have.property('appium:usePreinstalledWDA');
|
|
46
|
+
expect(caps.firstMatch[0]).to.not.have.property('appium:updatedWDABundleId');
|
|
50
47
|
});
|
|
51
48
|
it('scrubs conflicting WDA caps from both buckets', () => {
|
|
52
49
|
const caps = {
|
|
@@ -4,84 +4,90 @@ require("reflect-metadata");
|
|
|
4
4
|
const chai_1 = require("chai");
|
|
5
5
|
const SessionLifecycleService_1 = require("../../src/services/SessionLifecycleService");
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `cleanupApps(driver, [driver.wda.bundleIdForXctest])` — removing every
|
|
10
|
-
* installed app whose CFBundleName is WebDriverAgentRunner except the single
|
|
11
|
-
* bundle id on that keep-list.
|
|
7
|
+
* Xenon owns this WebDriverAgent. It launches it through go-ios and keeps the
|
|
8
|
+
* XCTest session alive for the live stream; the Appium driver is a guest.
|
|
12
9
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
10
|
+
* `appium:webDriverAgentUrl` is exactly how that is expressed —
|
|
11
|
+
* `selectWdaStartupStrategyName` returns 'existing-url' on it before
|
|
12
|
+
* considering anything else, and that strategy's `quit()` reads "Stopping
|
|
13
|
+
* neither xcodebuild nor XCTest session since WDA lifecycle is not managed by
|
|
14
|
+
* this driver".
|
|
18
15
|
*
|
|
19
|
-
*
|
|
16
|
+
* `appium:usePreinstalledWDA` invites the opposite. It does not change the
|
|
17
|
+
* strategy — the URL already won — it only makes `launchOnce` run
|
|
18
|
+
* `preparePreinstalled` first:
|
|
20
19
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* await driver.mobileKillApp(driver.wda.bundleIdForXctest);
|
|
21
|
+
* await cleanupApps(driver, [driver.wda.bundleIdForXctest]);
|
|
23
22
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* Both halves were observed against a real iPhone: the uninstall as "Removing
|
|
24
|
+
* WebDriverAgent runner app 'com.qasecret.WebDriverAgentRunner.xctrunner'",
|
|
25
|
+
* leaving the device with no WDA at all, and the kill as the driver's own next
|
|
26
|
+
* request failing with ECONNRESET because go-ios's runwda was gone while
|
|
27
|
+
* iproxy still held the port.
|
|
26
28
|
*/
|
|
27
29
|
describe('WDA capability injection', () => {
|
|
28
30
|
// The method is private by design; this is the one place its contract is
|
|
29
31
|
// asserted, so it is reached deliberately rather than widening the API.
|
|
30
|
-
const inject = (caps, url
|
|
32
|
+
const inject = (caps, url) => SessionLifecycleService_1.SessionLifecycleService.prototype.injectWDAUrl.call(Object.create(SessionLifecycleService_1.SessionLifecycleService.prototype), caps, url);
|
|
31
33
|
const WDA_URL = 'http://127.0.0.1:8100';
|
|
32
|
-
|
|
33
|
-
it('names the bundle whenever it claims a preinstalled WDA', () => {
|
|
34
|
+
it('reuses the running WDA by URL', () => {
|
|
34
35
|
const caps = { alwaysMatch: {}, firstMatch: [{}] };
|
|
35
|
-
inject(caps, WDA_URL
|
|
36
|
-
(0, chai_1.expect)(caps.alwaysMatch['appium:usePreinstalledWDA']).to.equal(true);
|
|
37
|
-
(0, chai_1.expect)(caps.alwaysMatch['appium:updatedWDABundleId']).to.equal(BUNDLE);
|
|
38
|
-
});
|
|
39
|
-
it('never claims a preinstalled WDA it cannot name', () => {
|
|
40
|
-
// The destructive combination. `webDriverAgentUrl` alone still reuses the
|
|
41
|
-
// running WDA and does not enter the cleanup path at all.
|
|
42
|
-
const caps = { alwaysMatch: {}, firstMatch: [{}] };
|
|
43
|
-
inject(caps, WDA_URL, null);
|
|
36
|
+
inject(caps, WDA_URL);
|
|
44
37
|
(0, chai_1.expect)(caps.alwaysMatch['appium:webDriverAgentUrl']).to.equal(WDA_URL);
|
|
45
|
-
(0, chai_1.expect)(caps.alwaysMatch['appium:usePreinstalledWDA']).to.equal(undefined);
|
|
46
|
-
(0, chai_1.expect)(caps.alwaysMatch['appium:updatedWDABundleId']).to.equal(undefined);
|
|
47
38
|
});
|
|
48
|
-
it('never
|
|
39
|
+
it('never invites the driver to manage the app', () => {
|
|
49
40
|
const caps = { alwaysMatch: {}, firstMatch: [{}] };
|
|
50
41
|
inject(caps, WDA_URL);
|
|
51
|
-
(0, chai_1.expect)(caps.alwaysMatch
|
|
42
|
+
(0, chai_1.expect)(caps.alwaysMatch).to.not.have.property('appium:usePreinstalledWDA');
|
|
43
|
+
(0, chai_1.expect)(caps.alwaysMatch).to.not.have.property('appium:updatedWDABundleId');
|
|
52
44
|
});
|
|
53
|
-
it('
|
|
54
|
-
//
|
|
55
|
-
//
|
|
45
|
+
it('strips the invitation even when the caller supplied it', () => {
|
|
46
|
+
// Honouring it would kill the WDA Xenon is hosting. On this path the
|
|
47
|
+
// capability is not the caller's to give.
|
|
56
48
|
const caps = {
|
|
57
|
-
alwaysMatch: { 'appium:usePreinstalledWDA': true, 'appium:updatedWDABundleId': '
|
|
49
|
+
alwaysMatch: { 'appium:usePreinstalledWDA': true, 'appium:updatedWDABundleId': 'com.x.WDA' },
|
|
58
50
|
firstMatch: [{}],
|
|
59
51
|
};
|
|
60
|
-
inject(caps, WDA_URL
|
|
61
|
-
(0, chai_1.expect)(caps.alwaysMatch
|
|
62
|
-
(0, chai_1.expect)(caps.alwaysMatch
|
|
52
|
+
inject(caps, WDA_URL);
|
|
53
|
+
(0, chai_1.expect)(caps.alwaysMatch).to.not.have.property('appium:usePreinstalledWDA');
|
|
54
|
+
(0, chai_1.expect)(caps.alwaysMatch).to.not.have.property('appium:updatedWDABundleId');
|
|
63
55
|
});
|
|
64
|
-
it('
|
|
56
|
+
it('strips it from the other bucket too', () => {
|
|
57
|
+
const caps = {
|
|
58
|
+
alwaysMatch: {},
|
|
59
|
+
firstMatch: [{ 'appium:usePreinstalledWDA': true, 'appium:updatedWDABundleId': 'com.x.WDA' }],
|
|
60
|
+
};
|
|
61
|
+
inject(caps, WDA_URL);
|
|
62
|
+
(0, chai_1.expect)(caps.firstMatch[0]).to.not.have.property('appium:usePreinstalledWDA');
|
|
63
|
+
(0, chai_1.expect)(caps.firstMatch[0]).to.not.have.property('appium:updatedWDABundleId');
|
|
64
|
+
});
|
|
65
|
+
it('keeps the injected url out of the other capability bucket', () => {
|
|
65
66
|
// W3C forbids a property in both alwaysMatch and a firstMatch entry;
|
|
66
67
|
// appium rejects the whole session if it happens.
|
|
67
68
|
const caps = {
|
|
68
69
|
alwaysMatch: {},
|
|
69
|
-
firstMatch: [{ 'appium:webDriverAgentUrl': 'http://stale'
|
|
70
|
+
firstMatch: [{ 'appium:webDriverAgentUrl': 'http://stale' }],
|
|
70
71
|
};
|
|
71
|
-
inject(caps, WDA_URL
|
|
72
|
-
(0, chai_1.expect)(caps.firstMatch[0]
|
|
73
|
-
(0, chai_1.expect)(caps.firstMatch[0]['appium:usePreinstalledWDA']).to.equal(undefined);
|
|
72
|
+
inject(caps, WDA_URL);
|
|
73
|
+
(0, chai_1.expect)(caps.firstMatch[0]).to.not.have.property('appium:webDriverAgentUrl');
|
|
74
74
|
(0, chai_1.expect)(caps.alwaysMatch['appium:webDriverAgentUrl']).to.equal(WDA_URL);
|
|
75
75
|
});
|
|
76
|
+
it('falls back to firstMatch[0] when there is no alwaysMatch', () => {
|
|
77
|
+
const caps = { firstMatch: [{ 'appium:udid': 'abc' }] };
|
|
78
|
+
inject(caps, WDA_URL);
|
|
79
|
+
(0, chai_1.expect)(caps.firstMatch[0]['appium:webDriverAgentUrl']).to.equal(WDA_URL);
|
|
80
|
+
(0, chai_1.expect)(caps.firstMatch[0]).to.not.have.property('appium:usePreinstalledWDA');
|
|
81
|
+
});
|
|
76
82
|
it('strips the caps that conflict with reusing a WDA by URL', () => {
|
|
77
83
|
const caps = {
|
|
78
84
|
alwaysMatch: { 'appium:wdaLocalPort': 8100, 'appium:derivedDataPath': '/tmp/dd' },
|
|
79
85
|
firstMatch: [{ 'appium:mjpegServerPort': 9100, 'appium:usePrebuiltWDA': true }],
|
|
80
86
|
};
|
|
81
|
-
inject(caps, WDA_URL
|
|
82
|
-
(0, chai_1.expect)(caps.alwaysMatch
|
|
83
|
-
(0, chai_1.expect)(caps.alwaysMatch
|
|
84
|
-
(0, chai_1.expect)(caps.firstMatch[0]
|
|
85
|
-
(0, chai_1.expect)(caps.firstMatch[0]
|
|
87
|
+
inject(caps, WDA_URL);
|
|
88
|
+
(0, chai_1.expect)(caps.alwaysMatch).to.not.have.property('appium:wdaLocalPort');
|
|
89
|
+
(0, chai_1.expect)(caps.alwaysMatch).to.not.have.property('appium:derivedDataPath');
|
|
90
|
+
(0, chai_1.expect)(caps.firstMatch[0]).to.not.have.property('appium:mjpegServerPort');
|
|
91
|
+
(0, chai_1.expect)(caps.firstMatch[0]).to.not.have.property('appium:usePrebuiltWDA');
|
|
86
92
|
});
|
|
87
93
|
});
|
package/package.json
CHANGED