@devicecloud.dev/dcd 5.2.0-beta.1 → 5.2.0-beta.3
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/dist/commands/cloud.d.ts +4 -4
- package/dist/commands/cloud.js +10 -6
- package/dist/config/flags/device.flags.d.ts +4 -4
- package/dist/config/flags/device.flags.js +4 -4
- package/dist/constants.d.ts +4 -4
- package/dist/utils/device-matrix.d.ts +16 -2
- package/dist/utils/device-matrix.js +27 -5
- package/package.json +1 -1
package/dist/commands/cloud.d.ts
CHANGED
|
@@ -185,13 +185,13 @@ export declare const cloudCommand: import("citty").CommandDef<{
|
|
|
185
185
|
readonly type: "string";
|
|
186
186
|
readonly description: `[iOS only] iOS version to run your flow against (options: ${string})`;
|
|
187
187
|
};
|
|
188
|
-
readonly 'ios-
|
|
188
|
+
readonly 'ios-device-matrix': {
|
|
189
189
|
readonly type: "string";
|
|
190
|
-
readonly description: "[iOS only] Device-matrix cell as <device>:<version>, e.g. iphone-16:18. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --android-
|
|
190
|
+
readonly description: "[iOS only] Device-matrix cell as <device>:<version>, e.g. iphone-16:18. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --android-device-matrix.";
|
|
191
191
|
};
|
|
192
|
-
readonly 'android-
|
|
192
|
+
readonly 'android-device-matrix': {
|
|
193
193
|
readonly type: "string";
|
|
194
|
-
readonly description: "[Android only] Device-matrix cell as <device>:<apiLevel> (append :play for Google Play), e.g. pixel-7:34 or pixel-7:34:play. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --ios-
|
|
194
|
+
readonly description: "[Android only] Device-matrix cell as <device>:<apiLevel> (append :play for Google Play), e.g. pixel-7:34 or pixel-7:34:play. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --ios-device-matrix.";
|
|
195
195
|
};
|
|
196
196
|
readonly orientation: {
|
|
197
197
|
readonly type: "string";
|
package/dist/commands/cloud.js
CHANGED
|
@@ -16,7 +16,7 @@ import { TestSubmissionService } from '../services/test-submission.service.js';
|
|
|
16
16
|
import { VersionService } from '../services/version.service.js';
|
|
17
17
|
import { EAndroidApiLevels, EAndroidDevices, EiOSDevices, EiOSVersions, isIosMatrixConfig, } from '../types/domain/device.types.js';
|
|
18
18
|
import { resolveAuth } from '../utils/auth.js';
|
|
19
|
-
import { matrixIsIos, parseDeviceMatrix } from '../utils/device-matrix.js';
|
|
19
|
+
import { assertMatrixSupported, matrixIsIos, parseDeviceMatrix, } from '../utils/device-matrix.js';
|
|
20
20
|
import { detectCiContext, isCI } from '../utils/ci.js';
|
|
21
21
|
import { CliError, coerceArray, collectRepeatedFlag, getCliVersion, getUpgradeCommand, logger, parseIntFlag, validateEnum, } from '../utils/cli.js';
|
|
22
22
|
import { fetchCompatibilityData, } from '../utils/compatibility.js';
|
|
@@ -139,9 +139,9 @@ export const cloudCommand = defineCommand({
|
|
|
139
139
|
const androidDevice = validateEnum(args['android-device'], Object.values(EAndroidDevices), 'android-device');
|
|
140
140
|
const androidNoSnapshot = Boolean(args['android-no-snapshot']);
|
|
141
141
|
// Repeatable device-matrix flags: one validated cell each, no cross-product.
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
const deviceMatrix = parseDeviceMatrix(
|
|
142
|
+
const iosMatrixFlags = collectRepeatedFlag(rawArgs, ['--ios-device-matrix']);
|
|
143
|
+
const androidMatrixFlags = collectRepeatedFlag(rawArgs, ['--android-device-matrix']);
|
|
144
|
+
const deviceMatrix = parseDeviceMatrix(iosMatrixFlags, androidMatrixFlags);
|
|
145
145
|
const json = Boolean(args.json);
|
|
146
146
|
const jsonFileFlag = Boolean(args['json-file']);
|
|
147
147
|
const jsonFileName = args['json-file-name'];
|
|
@@ -433,8 +433,8 @@ export const cloudCommand = defineCommand({
|
|
|
433
433
|
'include-tags': includeTags,
|
|
434
434
|
'exclude-tags': excludeTags,
|
|
435
435
|
'exclude-flows': excludeFlows,
|
|
436
|
-
'ios-
|
|
437
|
-
'android-
|
|
436
|
+
'ios-device-matrix': iosMatrixFlags,
|
|
437
|
+
'android-device-matrix': androidMatrixFlags,
|
|
438
438
|
};
|
|
439
439
|
for (const [k, v] of Object.entries(args)) {
|
|
440
440
|
if (!canonicalFlagKeys.has(k))
|
|
@@ -560,6 +560,10 @@ export const cloudCommand = defineCommand({
|
|
|
560
560
|
// there is no matrix, and tolerant of older APIs that lack the endpoint.
|
|
561
561
|
if (deviceMatrix.length > 0) {
|
|
562
562
|
const estimate = await ApiGateway.estimateMatrix(apiUrl, auth, fields);
|
|
563
|
+
// A null estimate means the API predates the matrix (404/405). It would
|
|
564
|
+
// silently strip deviceMatrix and run one device — refuse rather than
|
|
565
|
+
// hand back a green single-device run the user reads as a matrix.
|
|
566
|
+
assertMatrixSupported(deviceMatrix, estimate);
|
|
563
567
|
if (estimate) {
|
|
564
568
|
const osPrefix = matrixIsIos(deviceMatrix) ? 'iOS' : 'API';
|
|
565
569
|
const rows = ui.fields([
|
|
@@ -27,13 +27,13 @@ export declare const deviceFlags: {
|
|
|
27
27
|
readonly type: "string";
|
|
28
28
|
readonly description: `[iOS only] iOS version to run your flow against (options: ${string})`;
|
|
29
29
|
};
|
|
30
|
-
readonly 'ios-
|
|
30
|
+
readonly 'ios-device-matrix': {
|
|
31
31
|
readonly type: "string";
|
|
32
|
-
readonly description: "[iOS only] Device-matrix cell as <device>:<version>, e.g. iphone-16:18. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --android-
|
|
32
|
+
readonly description: "[iOS only] Device-matrix cell as <device>:<version>, e.g. iphone-16:18. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --android-device-matrix.";
|
|
33
33
|
};
|
|
34
|
-
readonly 'android-
|
|
34
|
+
readonly 'android-device-matrix': {
|
|
35
35
|
readonly type: "string";
|
|
36
|
-
readonly description: "[Android only] Device-matrix cell as <device>:<apiLevel> (append :play for Google Play), e.g. pixel-7:34 or pixel-7:34:play. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --ios-
|
|
36
|
+
readonly description: "[Android only] Device-matrix cell as <device>:<apiLevel> (append :play for Google Play), e.g. pixel-7:34 or pixel-7:34:play. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --ios-device-matrix.";
|
|
37
37
|
};
|
|
38
38
|
readonly orientation: {
|
|
39
39
|
readonly type: "string";
|
|
@@ -32,13 +32,13 @@ export const deviceFlags = {
|
|
|
32
32
|
type: 'string',
|
|
33
33
|
description: `[iOS only] iOS version to run your flow against (options: ${iosVersions})`,
|
|
34
34
|
},
|
|
35
|
-
'ios-
|
|
35
|
+
'ios-device-matrix': {
|
|
36
36
|
type: 'string',
|
|
37
|
-
description: `[iOS only] Device-matrix cell as <device>:<version>, e.g. iphone-16:18. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --android-
|
|
37
|
+
description: `[iOS only] Device-matrix cell as <device>:<version>, e.g. iphone-16:18. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --android-device-matrix.`,
|
|
38
38
|
},
|
|
39
|
-
'android-
|
|
39
|
+
'android-device-matrix': {
|
|
40
40
|
type: 'string',
|
|
41
|
-
description: `[Android only] Device-matrix cell as <device>:<apiLevel> (append :play for Google Play), e.g. pixel-7:34 or pixel-7:34:play. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --ios-
|
|
41
|
+
description: `[Android only] Device-matrix cell as <device>:<apiLevel> (append :play for Google Play), e.g. pixel-7:34 or pixel-7:34:play. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --ios-device-matrix.`,
|
|
42
42
|
},
|
|
43
43
|
orientation: {
|
|
44
44
|
type: 'string',
|
package/dist/constants.d.ts
CHANGED
|
@@ -171,13 +171,13 @@ export declare const flags: {
|
|
|
171
171
|
readonly type: "string";
|
|
172
172
|
readonly description: `[iOS only] iOS version to run your flow against (options: ${string})`;
|
|
173
173
|
};
|
|
174
|
-
readonly 'ios-
|
|
174
|
+
readonly 'ios-device-matrix': {
|
|
175
175
|
readonly type: "string";
|
|
176
|
-
readonly description: "[iOS only] Device-matrix cell as <device>:<version>, e.g. iphone-16:18. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --android-
|
|
176
|
+
readonly description: "[iOS only] Device-matrix cell as <device>:<version>, e.g. iphone-16:18. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --android-device-matrix.";
|
|
177
177
|
};
|
|
178
|
-
readonly 'android-
|
|
178
|
+
readonly 'android-device-matrix': {
|
|
179
179
|
readonly type: "string";
|
|
180
|
-
readonly description: "[Android only] Device-matrix cell as <device>:<apiLevel> (append :play for Google Play), e.g. pixel-7:34 or pixel-7:34:play. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --ios-
|
|
180
|
+
readonly description: "[Android only] Device-matrix cell as <device>:<apiLevel> (append :play for Google Play), e.g. pixel-7:34 or pixel-7:34:play. Repeatable — every flow runs once per cell (no cross-product). Cannot be combined with --ios-device-matrix.";
|
|
181
181
|
};
|
|
182
182
|
readonly orientation: {
|
|
183
183
|
readonly type: "string";
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import { DeviceMatrixConfig } from '../types/domain/device.types.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Refuse to submit a device matrix to an API that cannot honour it.
|
|
4
|
+
*
|
|
5
|
+
* The estimate endpoint is only called when a matrix was actually requested, so
|
|
6
|
+
* a null estimate (the gateway maps 404/405 to null) means the API predates the
|
|
7
|
+
* feature. That API would **silently strip** the unknown `deviceMatrix` field —
|
|
8
|
+
* its ValidationPipe runs `whitelist: true, forbidNonWhitelisted: false` — and
|
|
9
|
+
* run every flow on a single default device, exiting 0. The user would believe
|
|
10
|
+
* they had tested N devices when they tested one: the exact silent
|
|
11
|
+
* under-testing the device matrix exists to prevent. Fail loudly instead.
|
|
12
|
+
*
|
|
13
|
+
* @throws CliError when a matrix was requested but the API does not support it.
|
|
14
|
+
*/
|
|
15
|
+
export declare function assertMatrixSupported(deviceMatrix: DeviceMatrixConfig[], estimate: unknown | null): void;
|
|
16
|
+
/**
|
|
17
|
+
* Parse repeated `--ios-device-matrix <device>:<version>` and
|
|
18
|
+
* `--android-device-matrix <device>:<apiLevel>[:play]` flags into an explicit device
|
|
5
19
|
* matrix. Each entry is one validated cell — there is NO cross-product, because
|
|
6
20
|
* the compatibility matrix is ragged and a cross-product would invent cells the
|
|
7
21
|
* user never asked for.
|
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
import { isIosMatrixConfig, } from '../types/domain/device.types.js';
|
|
2
2
|
import { CliError } from './cli.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Refuse to submit a device matrix to an API that cannot honour it.
|
|
5
|
+
*
|
|
6
|
+
* The estimate endpoint is only called when a matrix was actually requested, so
|
|
7
|
+
* a null estimate (the gateway maps 404/405 to null) means the API predates the
|
|
8
|
+
* feature. That API would **silently strip** the unknown `deviceMatrix` field —
|
|
9
|
+
* its ValidationPipe runs `whitelist: true, forbidNonWhitelisted: false` — and
|
|
10
|
+
* run every flow on a single default device, exiting 0. The user would believe
|
|
11
|
+
* they had tested N devices when they tested one: the exact silent
|
|
12
|
+
* under-testing the device matrix exists to prevent. Fail loudly instead.
|
|
13
|
+
*
|
|
14
|
+
* @throws CliError when a matrix was requested but the API does not support it.
|
|
15
|
+
*/
|
|
16
|
+
export function assertMatrixSupported(deviceMatrix, estimate) {
|
|
17
|
+
if (deviceMatrix.length === 0 || estimate)
|
|
18
|
+
return;
|
|
19
|
+
throw new CliError('This DeviceCloud API does not support device matrices, so ' +
|
|
20
|
+
'--ios-device-matrix / --android-device-matrix cannot be honoured. ' +
|
|
21
|
+
'Submitting anyway would silently run every flow on a single default ' +
|
|
22
|
+
'device and report success. Upgrade the API, or drop the matrix flags ' +
|
|
23
|
+
'and use --ios-device / --android-device for a single-device run.');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Parse repeated `--ios-device-matrix <device>:<version>` and
|
|
27
|
+
* `--android-device-matrix <device>:<apiLevel>[:play]` flags into an explicit device
|
|
6
28
|
* matrix. Each entry is one validated cell — there is NO cross-product, because
|
|
7
29
|
* the compatibility matrix is ragged and a cross-product would invent cells the
|
|
8
30
|
* user never asked for.
|
|
@@ -14,13 +36,13 @@ import { CliError } from './cli.js';
|
|
|
14
36
|
*/
|
|
15
37
|
export function parseDeviceMatrix(iosConfigs, androidConfigs) {
|
|
16
38
|
if (iosConfigs.length > 0 && androidConfigs.length > 0) {
|
|
17
|
-
throw new CliError('A device matrix cannot mix platforms: use either --ios-
|
|
39
|
+
throw new CliError('A device matrix cannot mix platforms: use either --ios-device-matrix or --android-device-matrix, not both. One upload runs one binary.');
|
|
18
40
|
}
|
|
19
41
|
const configs = [];
|
|
20
42
|
for (const raw of iosConfigs) {
|
|
21
43
|
const parts = raw.split(':');
|
|
22
44
|
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
23
|
-
throw new CliError(`Invalid --ios-
|
|
45
|
+
throw new CliError(`Invalid --ios-device-matrix "${raw}". Expected <device>:<version>, e.g. iphone-16:18.`);
|
|
24
46
|
}
|
|
25
47
|
configs.push({ iOSDevice: parts[0], iOSVersion: parts[1] });
|
|
26
48
|
}
|
|
@@ -32,7 +54,7 @@ export function parseDeviceMatrix(iosConfigs, androidConfigs) {
|
|
|
32
54
|
!parts[0] ||
|
|
33
55
|
!parts[1] ||
|
|
34
56
|
(parts.length === 3 && parts[2] !== 'play')) {
|
|
35
|
-
throw new CliError(`Invalid --android-
|
|
57
|
+
throw new CliError(`Invalid --android-device-matrix "${raw}". Expected <device>:<apiLevel> or <device>:<apiLevel>:play, e.g. pixel-7:34 or pixel-7:34:play.`);
|
|
36
58
|
}
|
|
37
59
|
configs.push({
|
|
38
60
|
androidDevice: parts[0],
|