@tamagui/native-ci 2.7.7 → 3.0.0-beta.637.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/dist/android-utils.mjs +62 -0
- package/dist/android-utils.mjs.map +1 -0
- package/dist/cache.mjs +54 -70
- package/dist/cache.mjs.map +1 -1
- package/dist/cli.mjs +378 -403
- package/dist/cli.mjs.map +1 -1
- package/dist/constants.mjs +3 -2
- package/dist/constants.mjs.map +1 -1
- package/dist/deps.mjs +52 -64
- package/dist/deps.mjs.map +1 -1
- package/dist/detox.mjs +220 -202
- package/dist/detox.mjs.map +1 -1
- package/dist/fingerprint.mjs +35 -33
- package/dist/fingerprint.mjs.map +1 -1
- package/dist/index.js +10 -10
- package/dist/index.mjs +10 -10
- package/dist/ios-utils.mjs +318 -0
- package/dist/ios-utils.mjs.map +1 -0
- package/dist/metro.mjs +137 -139
- package/dist/metro.mjs.map +1 -1
- package/dist/runner.mjs +62 -65
- package/dist/runner.mjs.map +1 -1
- package/package.json +4 -4
- package/src/cli.ts +3 -3
- package/src/constants.ts +1 -1
- package/src/detox.ts +1 -0
- package/src/index.ts +2 -2
- package/src/metro.ts +2 -2
- package/src/run-detox-android.ts +1 -1
- package/src/run-detox-ios.ts +1 -1
- package/types/{android.d.ts → android-utils.d.ts} +1 -1
- package/types/android-utils.d.ts.map +1 -0
- package/types/cli.d.ts +1 -1
- package/types/constants.d.ts +2 -2
- package/types/detox.d.ts.map +1 -1
- package/types/index.d.ts +2 -2
- package/types/index.d.ts.map +1 -1
- package/types/{ios.d.ts → ios-utils.d.ts} +1 -1
- package/types/ios-utils.d.ts.map +1 -0
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/types/android.d.ts.map +0 -1
- package/types/ios.d.ts.map +0 -1
- /package/src/{android.ts → android-utils.ts} +0 -0
- /package/src/{ios.ts → ios-utils.ts} +0 -0
package/dist/detox.mjs
CHANGED
|
@@ -1,226 +1,244 @@
|
|
|
1
1
|
import { parseArgs } from "node:util";
|
|
2
2
|
import { DETOX_SERVER_PORT } from "./constants.mjs";
|
|
3
|
+
|
|
3
4
|
function parseDetoxArgs(platform) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
workers: workersNum,
|
|
54
|
-
testFiles: positionals.length > 0 ? positionals : void 0
|
|
55
|
-
};
|
|
5
|
+
const defaultConfig = platform === "ios" ? "ios.sim.debug" : "android.emu.ci.debug";
|
|
6
|
+
const { values, positionals } = parseArgs({
|
|
7
|
+
options: {
|
|
8
|
+
config: {
|
|
9
|
+
type: "string",
|
|
10
|
+
default: defaultConfig
|
|
11
|
+
},
|
|
12
|
+
"project-root": {
|
|
13
|
+
type: "string",
|
|
14
|
+
default: process.cwd()
|
|
15
|
+
},
|
|
16
|
+
headless: {
|
|
17
|
+
type: "boolean",
|
|
18
|
+
default: false
|
|
19
|
+
},
|
|
20
|
+
"record-logs": {
|
|
21
|
+
type: "string",
|
|
22
|
+
default: "all"
|
|
23
|
+
},
|
|
24
|
+
retries: {
|
|
25
|
+
type: "string",
|
|
26
|
+
default: "0"
|
|
27
|
+
},
|
|
28
|
+
workers: {
|
|
29
|
+
type: "string",
|
|
30
|
+
default: "1"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
allowPositionals: true
|
|
34
|
+
});
|
|
35
|
+
const retriesNum = Number.parseInt(values.retries, 10);
|
|
36
|
+
if (Number.isNaN(retriesNum) || retriesNum < 0) {
|
|
37
|
+
console.error("Error: retries must be a non-negative integer");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
const workersNum = Number.parseInt(values.workers, 10);
|
|
41
|
+
if (Number.isNaN(workersNum) || workersNum < 1) {
|
|
42
|
+
console.error("Error: workers must be a positive integer");
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
config: values.config,
|
|
47
|
+
projectRoot: values["project-root"],
|
|
48
|
+
headless: values.headless,
|
|
49
|
+
recordLogs: values["record-logs"],
|
|
50
|
+
retries: retriesNum,
|
|
51
|
+
workers: workersNum,
|
|
52
|
+
testFiles: positionals.length > 0 ? positionals : void 0
|
|
53
|
+
};
|
|
56
54
|
}
|
|
57
55
|
function buildDetoxArgs(options) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
56
|
+
const args = [
|
|
57
|
+
"detox",
|
|
58
|
+
"test",
|
|
59
|
+
"--configuration",
|
|
60
|
+
options.config,
|
|
61
|
+
"--record-logs",
|
|
62
|
+
options.recordLogs,
|
|
63
|
+
"--retries",
|
|
64
|
+
String(options.retries),
|
|
65
|
+
"--forceExit"
|
|
66
|
+
];
|
|
67
|
+
if (options.headless) {
|
|
68
|
+
args.push("--headless");
|
|
69
|
+
}
|
|
70
|
+
if (options.workers && options.workers > 1) {
|
|
71
|
+
args.push("--workers", String(options.workers));
|
|
72
|
+
}
|
|
73
|
+
if (options.testFiles && options.testFiles.length > 0) {
|
|
74
|
+
args.push(...options.testFiles);
|
|
75
|
+
}
|
|
76
|
+
return args;
|
|
71
77
|
}
|
|
72
78
|
const DETOX_TIMEOUT_MS = 60 * 60 * 1e3;
|
|
73
79
|
const MAX_CONNECT_FLAKE_RETRIES = 1;
|
|
74
|
-
const CONNECT_FLAKE_SIGNATURES = [
|
|
75
|
-
|
|
80
|
+
const CONNECT_FLAKE_SIGNATURES = [
|
|
81
|
+
/Exceeded timeout of \d+ ?ms for a hook/,
|
|
82
|
+
/Exceeded timeout of \d+ ?ms while tearing down/,
|
|
83
|
+
/Exceeded timeout of \d+ ?ms while handling jest-circus/,
|
|
84
|
+
/\btimed out after \d+ms/,
|
|
85
|
+
/could not connect to Detox/,
|
|
86
|
+
/launch recovery deadline exceeded/,
|
|
87
|
+
/skipping launchApp:/,
|
|
88
|
+
/skipping reload:/,
|
|
89
|
+
/FBSOpenApplicationServiceErrorDomain/,
|
|
90
|
+
/FBSOpenApplicationErrorDomain/,
|
|
91
|
+
/Waited for the root of the view hierarchy to have window focus/,
|
|
92
|
+
/Detox worker instance has not been installed/
|
|
93
|
+
];
|
|
94
|
+
const REAL_FAILURE_SIGNATURES = [
|
|
95
|
+
/Exceeded timeout of \d+ ?ms while waiting for/,
|
|
96
|
+
/expect\([^)]+\)\.to/,
|
|
97
|
+
/AssertionError:/,
|
|
98
|
+
/(?<!Runtime)Error: (?!.*timed out after)/
|
|
99
|
+
];
|
|
76
100
|
const ANSI_PATTERN = /\x1b\[[0-9;]*m/g;
|
|
77
101
|
const DETOX_DAEMON_LINE = /^.*\bdetox\[\d+\]\s+[a-zA-Z]\s/m;
|
|
78
102
|
function classifyDetoxFailures(rawOutput) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
103
|
+
const output = rawOutput.replace(ANSI_PATTERN, "");
|
|
104
|
+
const delimiter = /(PASS|FAIL) (e2e\/[^\s):]+\.test\.ts)/g;
|
|
105
|
+
const marks = [];
|
|
106
|
+
for (const m of output.matchAll(delimiter)) {
|
|
107
|
+
marks.push({
|
|
108
|
+
kind: m[1],
|
|
109
|
+
file: m[2],
|
|
110
|
+
index: m.index ?? 0
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const failedFiles = [];
|
|
114
|
+
const flakeFiles = [];
|
|
115
|
+
const realFiles = [];
|
|
116
|
+
for (let i = 0; i < marks.length; i++) {
|
|
117
|
+
const mark = marks[i];
|
|
118
|
+
if (mark.kind !== "FAIL") continue;
|
|
119
|
+
const nextMark = i + 1 < marks.length ? marks[i + 1].index : output.length;
|
|
120
|
+
const daemon = DETOX_DAEMON_LINE.exec(output.slice(mark.index, nextMark));
|
|
121
|
+
const blockEnd = daemon ? mark.index + daemon.index : nextMark;
|
|
122
|
+
const block = output.slice(mark.index, blockEnd);
|
|
123
|
+
if (failedFiles.includes(mark.file)) continue;
|
|
124
|
+
failedFiles.push(mark.file);
|
|
125
|
+
const isReal = REAL_FAILURE_SIGNATURES.some((sig) => sig.test(block));
|
|
126
|
+
if (isReal) {
|
|
127
|
+
realFiles.push(mark.file);
|
|
128
|
+
} else {
|
|
129
|
+
const isFlake = CONNECT_FLAKE_SIGNATURES.some((sig) => sig.test(block));
|
|
130
|
+
if (isFlake) {
|
|
131
|
+
flakeFiles.push(mark.file);
|
|
132
|
+
} else {
|
|
133
|
+
realFiles.push(mark.file);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
failedFiles,
|
|
139
|
+
flakeFiles,
|
|
140
|
+
realFiles
|
|
141
|
+
};
|
|
118
142
|
}
|
|
119
143
|
async function resetDetoxLockFile() {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
144
|
+
console.info("Resetting Detox lock file...");
|
|
145
|
+
const proc = Bun.spawn([
|
|
146
|
+
"npx",
|
|
147
|
+
"detox",
|
|
148
|
+
"reset-lock-file"
|
|
149
|
+
], {
|
|
150
|
+
stdout: "inherit",
|
|
151
|
+
stderr: "inherit"
|
|
152
|
+
});
|
|
153
|
+
await proc.exited;
|
|
126
154
|
}
|
|
127
155
|
async function spawnDetoxOnce(detoxArgs, testFiles = []) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
await pumps;
|
|
170
|
-
return {
|
|
171
|
-
exitCode: result,
|
|
172
|
-
output
|
|
173
|
-
};
|
|
156
|
+
console.info("\n--- Running Detox tests ---");
|
|
157
|
+
console.info(`Using fixed Detox server port: ${DETOX_SERVER_PORT}`);
|
|
158
|
+
console.info(`Command: npx ${detoxArgs.join(" ")}`);
|
|
159
|
+
const proc = Bun.spawn(["npx", ...detoxArgs], {
|
|
160
|
+
env: {
|
|
161
|
+
...process.env,
|
|
162
|
+
DETOX_SERVER_PORT: String(DETOX_SERVER_PORT),
|
|
163
|
+
TAMAGUI_DETOX_TEST_FILES: testFiles.join(" ")
|
|
164
|
+
},
|
|
165
|
+
stdout: "pipe",
|
|
166
|
+
stderr: "pipe"
|
|
167
|
+
});
|
|
168
|
+
const decoder = new TextDecoder();
|
|
169
|
+
let output = "";
|
|
170
|
+
const tee = async (stream, sink) => {
|
|
171
|
+
const reader = stream.getReader();
|
|
172
|
+
while (true) {
|
|
173
|
+
const { done, value } = await reader.read();
|
|
174
|
+
if (done) break;
|
|
175
|
+
sink.write(value);
|
|
176
|
+
output += decoder.decode(value, { stream: true });
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
const pumps = Promise.all([tee(proc.stdout, process.stdout), tee(proc.stderr, process.stderr)]);
|
|
180
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
181
|
+
setTimeout(() => resolve("timeout"), DETOX_TIMEOUT_MS);
|
|
182
|
+
});
|
|
183
|
+
const result = await Promise.race([proc.exited, timeoutPromise]);
|
|
184
|
+
if (result === "timeout") {
|
|
185
|
+
console.info("\nDetox timed out, killing process...");
|
|
186
|
+
proc.kill("SIGKILL");
|
|
187
|
+
return {
|
|
188
|
+
exitCode: 1,
|
|
189
|
+
output
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
await pumps;
|
|
193
|
+
return {
|
|
194
|
+
exitCode: result,
|
|
195
|
+
output
|
|
196
|
+
};
|
|
174
197
|
}
|
|
175
198
|
async function runDetoxTests(options) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
return 0;
|
|
185
|
-
}
|
|
186
|
-
console.info(`
|
|
199
|
+
await resetDetoxLockFile();
|
|
200
|
+
const detoxArgs = buildDetoxArgs(options);
|
|
201
|
+
const { exitCode, output } = await spawnDetoxOnce(detoxArgs, options.testFiles ?? []);
|
|
202
|
+
if (exitCode === 0) {
|
|
203
|
+
console.info("\nAll tests passed!");
|
|
204
|
+
return 0;
|
|
205
|
+
}
|
|
206
|
+
console.info(`
|
|
187
207
|
Tests failed with exit code: ${exitCode}`);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
if (realFiles.length > 0) {
|
|
196
|
-
console.info(`
|
|
208
|
+
const { flakeFiles, realFiles } = classifyDetoxFailures(output);
|
|
209
|
+
if (flakeFiles.length === 0) {
|
|
210
|
+
return exitCode;
|
|
211
|
+
}
|
|
212
|
+
if (realFiles.length > 0) {
|
|
213
|
+
console.info(`
|
|
197
214
|
Real test failure(s) present (${realFiles.join(", ")}); not retrying the connect-flaked file(s).`);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
215
|
+
return exitCode;
|
|
216
|
+
}
|
|
217
|
+
for (let attempt = 1; attempt <= MAX_CONNECT_FLAKE_RETRIES; attempt++) {
|
|
218
|
+
console.info(`
|
|
202
219
|
::warning::Detox launch-connect flake detected (beforeAll could not connect to Detox). Retry ${attempt}/${MAX_CONNECT_FLAKE_RETRIES} of: ${flakeFiles.join(", ")}`);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
220
|
+
await resetDetoxLockFile();
|
|
221
|
+
const retryArgs = buildDetoxArgs({
|
|
222
|
+
...options,
|
|
223
|
+
testFiles: flakeFiles
|
|
224
|
+
});
|
|
225
|
+
const retry = await spawnDetoxOnce(retryArgs, flakeFiles);
|
|
226
|
+
if (retry.exitCode === 0) {
|
|
227
|
+
console.info(`
|
|
211
228
|
\u2713 Recovered ${flakeFiles.length} connect-flaked file(s) on retry ${attempt}.`);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
229
|
+
return 0;
|
|
230
|
+
}
|
|
231
|
+
const retryClass = classifyDetoxFailures(retry.output);
|
|
232
|
+
if (retryClass.realFiles.length > 0 || retryClass.flakeFiles.length === 0) {
|
|
233
|
+
console.info("\nRetry surfaced a non-flake failure; surfacing it.");
|
|
234
|
+
return retry.exitCode;
|
|
235
|
+
}
|
|
236
|
+
flakeFiles.length = 0;
|
|
237
|
+
flakeFiles.push(...retryClass.flakeFiles);
|
|
238
|
+
}
|
|
239
|
+
console.info("\nConnect-flake retries exhausted; tests still failing.");
|
|
240
|
+
return 1;
|
|
224
241
|
}
|
|
242
|
+
|
|
225
243
|
export { buildDetoxArgs, classifyDetoxFailures, parseDetoxArgs, resetDetoxLockFile, runDetoxTests };
|
|
226
244
|
//# sourceMappingURL=detox.mjs.map
|
package/dist/detox.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["parseArgs","DETOX_SERVER_PORT","parseDetoxArgs","platform","defaultConfig","values","positionals","options","config","type","default","process","cwd","headless","retries","workers","allowPositionals","retriesNum","Number","parseInt","isNaN","console","error","exit","workersNum","projectRoot","recordLogs","testFiles","length","buildDetoxArgs","args","String","push","DETOX_TIMEOUT_MS","MAX_CONNECT_FLAKE_RETRIES","CONNECT_FLAKE_SIGNATURES","REAL_FAILURE_SIGNATURES","ANSI_PATTERN","DETOX_DAEMON_LINE","classifyDetoxFailures","rawOutput","output","replace","delimiter","marks","m","matchAll","kind","file","index","failedFiles","flakeFiles","realFiles","i","mark","nextMark","daemon","exec","slice","blockEnd","block","includes","isReal","some","sig","test","isFlake","resetDetoxLockFile","info","proc","Bun","spawn","stdout","stderr","exited","spawnDetoxOnce","detoxArgs","join","env","TAMAGUI_DETOX_TEST_FILES","decoder","TextDecoder","tee","stream","sink","reader","getReader","done","value","read","write","decode","pumps","Promise","all","timeoutPromise","resolve","setTimeout","result","race","kill","exitCode","runDetoxTests","attempt","retryArgs","retry","retryClass"],"sources":["../src/detox.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"detox.js","names":[],"sources":["detox.js"],"sourcesContent":["import { parseArgs } from \"node:util\";\nimport { DETOX_SERVER_PORT } from \"./constants\";\nfunction parseDetoxArgs(platform) {\n const defaultConfig = platform === \"ios\" ? \"ios.sim.debug\" : \"android.emu.ci.debug\";\n const { values, positionals } = parseArgs({\n options: {\n config: { type: \"string\", default: defaultConfig },\n \"project-root\": { type: \"string\", default: process.cwd() },\n headless: { type: \"boolean\", default: false },\n \"record-logs\": { type: \"string\", default: \"all\" },\n retries: { type: \"string\", default: \"0\" },\n workers: { type: \"string\", default: \"1\" }\n },\n allowPositionals: true\n });\n const retriesNum = Number.parseInt(values.retries, 10);\n if (Number.isNaN(retriesNum) || retriesNum < 0) {\n console.error(\"Error: retries must be a non-negative integer\");\n process.exit(1);\n }\n const workersNum = Number.parseInt(values.workers, 10);\n if (Number.isNaN(workersNum) || workersNum < 1) {\n console.error(\"Error: workers must be a positive integer\");\n process.exit(1);\n }\n return {\n config: values.config,\n projectRoot: values[\"project-root\"],\n headless: values.headless,\n recordLogs: values[\"record-logs\"],\n retries: retriesNum,\n workers: workersNum,\n testFiles: positionals.length > 0 ? positionals : void 0\n };\n}\nfunction buildDetoxArgs(options) {\n const args = [\n \"detox\",\n \"test\",\n \"--configuration\",\n options.config,\n \"--record-logs\",\n options.recordLogs,\n \"--retries\",\n String(options.retries),\n // force jest to exit after tests complete (prevents hanging on open handles)\n \"--forceExit\"\n ];\n if (options.headless) {\n args.push(\"--headless\");\n }\n if (options.workers && options.workers > 1) {\n args.push(\"--workers\", String(options.workers));\n }\n if (options.testFiles && options.testFiles.length > 0) {\n args.push(...options.testFiles);\n }\n return args;\n}\nconst DETOX_TIMEOUT_MS = 60 * 60 * 1e3;\nconst MAX_CONNECT_FLAKE_RETRIES = 1;\nconst CONNECT_FLAKE_SIGNATURES = [\n /Exceeded timeout of \\d+ ?ms for a hook/,\n /Exceeded timeout of \\d+ ?ms while tearing down/,\n /Exceeded timeout of \\d+ ?ms while handling jest-circus/,\n /\\btimed out after \\d+ms/,\n /could not connect to Detox/,\n /launch recovery deadline exceeded/,\n /skipping launchApp:/,\n /skipping reload:/,\n /FBSOpenApplicationServiceErrorDomain/,\n /FBSOpenApplicationErrorDomain/,\n /Waited for the root of the view hierarchy to have window focus/,\n /Detox worker instance has not been installed/\n];\nconst REAL_FAILURE_SIGNATURES = [\n /Exceeded timeout of \\d+ ?ms while waiting for/,\n /expect\\([^)]+\\)\\.to/,\n /AssertionError:/,\n /(?<!Runtime)Error: (?!.*timed out after)/\n];\nconst ANSI_PATTERN = /\\x1b\\[[0-9;]*m/g;\nconst DETOX_DAEMON_LINE = /^.*\\bdetox\\[\\d+\\]\\s+[a-zA-Z]\\s/m;\nfunction classifyDetoxFailures(rawOutput) {\n const output = rawOutput.replace(ANSI_PATTERN, \"\");\n const delimiter = /(PASS|FAIL) (e2e\\/[^\\s):]+\\.test\\.ts)/g;\n const marks = [];\n for (const m of output.matchAll(delimiter)) {\n marks.push({ kind: m[1], file: m[2], index: m.index ?? 0 });\n }\n const failedFiles = [];\n const flakeFiles = [];\n const realFiles = [];\n for (let i = 0; i < marks.length; i++) {\n const mark = marks[i];\n if (mark.kind !== \"FAIL\") continue;\n const nextMark = i + 1 < marks.length ? marks[i + 1].index : output.length;\n const daemon = DETOX_DAEMON_LINE.exec(output.slice(mark.index, nextMark));\n const blockEnd = daemon ? mark.index + daemon.index : nextMark;\n const block = output.slice(mark.index, blockEnd);\n if (failedFiles.includes(mark.file)) continue;\n failedFiles.push(mark.file);\n const isReal = REAL_FAILURE_SIGNATURES.some((sig) => sig.test(block));\n if (isReal) {\n realFiles.push(mark.file);\n } else {\n const isFlake = CONNECT_FLAKE_SIGNATURES.some((sig) => sig.test(block));\n if (isFlake) {\n flakeFiles.push(mark.file);\n } else {\n realFiles.push(mark.file);\n }\n }\n }\n return { failedFiles, flakeFiles, realFiles };\n}\nasync function resetDetoxLockFile() {\n console.info(\"Resetting Detox lock file...\");\n const proc = Bun.spawn([\"npx\", \"detox\", \"reset-lock-file\"], {\n stdout: \"inherit\",\n stderr: \"inherit\"\n });\n await proc.exited;\n}\nasync function spawnDetoxOnce(detoxArgs, testFiles = []) {\n console.info(\"\\n--- Running Detox tests ---\");\n console.info(`Using fixed Detox server port: ${DETOX_SERVER_PORT}`);\n console.info(`Command: npx ${detoxArgs.join(\" \")}`);\n const proc = Bun.spawn([\"npx\", ...detoxArgs], {\n env: {\n ...process.env,\n DETOX_SERVER_PORT: String(DETOX_SERVER_PORT),\n TAMAGUI_DETOX_TEST_FILES: testFiles.join(\" \")\n },\n stdout: \"pipe\",\n stderr: \"pipe\"\n });\n const decoder = new TextDecoder();\n let output = \"\";\n const tee = async (stream, sink) => {\n const reader = stream.getReader();\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n sink.write(value);\n output += decoder.decode(value, { stream: true });\n }\n };\n const pumps = Promise.all([\n tee(proc.stdout, process.stdout),\n tee(proc.stderr, process.stderr)\n ]);\n const timeoutPromise = new Promise((resolve) => {\n setTimeout(() => resolve(\"timeout\"), DETOX_TIMEOUT_MS);\n });\n const result = await Promise.race([proc.exited, timeoutPromise]);\n if (result === \"timeout\") {\n console.info(\"\\nDetox timed out, killing process...\");\n proc.kill(\"SIGKILL\");\n return { exitCode: 1, output };\n }\n await pumps;\n return { exitCode: result, output };\n}\nasync function runDetoxTests(options) {\n await resetDetoxLockFile();\n const detoxArgs = buildDetoxArgs(options);\n const { exitCode, output } = await spawnDetoxOnce(detoxArgs, options.testFiles ?? []);\n if (exitCode === 0) {\n console.info(\"\\nAll tests passed!\");\n return 0;\n }\n console.info(`\nTests failed with exit code: ${exitCode}`);\n const { flakeFiles, realFiles } = classifyDetoxFailures(output);\n if (flakeFiles.length === 0) {\n return exitCode;\n }\n if (realFiles.length > 0) {\n console.info(\n `\nReal test failure(s) present (${realFiles.join(\", \")}); not retrying the connect-flaked file(s).`\n );\n return exitCode;\n }\n for (let attempt = 1; attempt <= MAX_CONNECT_FLAKE_RETRIES; attempt++) {\n console.info(\n `\n::warning::Detox launch-connect flake detected (beforeAll could not connect to Detox). Retry ${attempt}/${MAX_CONNECT_FLAKE_RETRIES} of: ${flakeFiles.join(\", \")}`\n );\n await resetDetoxLockFile();\n const retryArgs = buildDetoxArgs({ ...options, testFiles: flakeFiles });\n const retry = await spawnDetoxOnce(retryArgs, flakeFiles);\n if (retry.exitCode === 0) {\n console.info(\n `\n\\u2713 Recovered ${flakeFiles.length} connect-flaked file(s) on retry ${attempt}.`\n );\n return 0;\n }\n const retryClass = classifyDetoxFailures(retry.output);\n if (retryClass.realFiles.length > 0 || retryClass.flakeFiles.length === 0) {\n console.info(\"\\nRetry surfaced a non-flake failure; surfacing it.\");\n return retry.exitCode;\n }\n flakeFiles.length = 0;\n flakeFiles.push(...retryClass.flakeFiles);\n }\n console.info(\"\\nConnect-flake retries exhausted; tests still failing.\");\n return 1;\n}\nexport {\n buildDetoxArgs,\n classifyDetoxFailures,\n parseDetoxArgs,\n resetDetoxLockFile,\n runDetoxTests\n};\n//# sourceMappingURL=detox.js.map\n"],"mappings":"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"}
|
package/dist/fingerprint.mjs
CHANGED
|
@@ -2,43 +2,45 @@ import { execSync } from "node:child_process";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
|
|
5
6
|
async function generateFingerprint(options) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
7
|
+
const { platform, projectRoot = process.cwd(), debug = false } = options;
|
|
8
|
+
const appJsonPath = join(projectRoot, "app.json");
|
|
9
|
+
if (!existsSync(appJsonPath)) {
|
|
10
|
+
throw new Error(`No app.json found at ${projectRoot}. Is this an Expo project?`);
|
|
11
|
+
}
|
|
12
|
+
const execOptions = {
|
|
13
|
+
cwd: projectRoot,
|
|
14
|
+
encoding: "utf-8",
|
|
15
|
+
stdio: debug ? "inherit" : [
|
|
16
|
+
"pipe",
|
|
17
|
+
"pipe",
|
|
18
|
+
"pipe"
|
|
19
|
+
]
|
|
20
|
+
};
|
|
21
|
+
try {
|
|
22
|
+
const result = execSync(`npx @expo/fingerprint fingerprint:generate --platform ${platform}`, execOptions);
|
|
23
|
+
const parsed = JSON.parse(result);
|
|
24
|
+
return {
|
|
25
|
+
hash: parsed.hash,
|
|
26
|
+
sources: parsed.sources || []
|
|
27
|
+
};
|
|
28
|
+
} catch (error) {
|
|
29
|
+
const err = error;
|
|
30
|
+
throw new Error(`Failed to generate ${platform} fingerprint: ${err.message}${err.stderr ? `
|
|
30
31
|
${err.stderr}` : ""}`);
|
|
31
|
-
|
|
32
|
+
}
|
|
32
33
|
}
|
|
33
34
|
function generatePreFingerprintHash(files, projectRoot = process.cwd()) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
const hash = createHash("sha256");
|
|
36
|
+
for (const file of files) {
|
|
37
|
+
const filePath = join(projectRoot, file);
|
|
38
|
+
if (existsSync(filePath)) {
|
|
39
|
+
hash.update(readFileSync(filePath));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return hash.digest("hex").slice(0, 16);
|
|
42
43
|
}
|
|
44
|
+
|
|
43
45
|
export { generateFingerprint, generatePreFingerprintHash };
|
|
44
46
|
//# sourceMappingURL=fingerprint.mjs.map
|
package/dist/fingerprint.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"file":"fingerprint.js","names":[],"sources":["fingerprint.js"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nasync function generateFingerprint(options) {\n const { platform, projectRoot = process.cwd(), debug = false } = options;\n const appJsonPath = join(projectRoot, \"app.json\");\n if (!existsSync(appJsonPath)) {\n throw new Error(`No app.json found at ${projectRoot}. Is this an Expo project?`);\n }\n const execOptions = {\n cwd: projectRoot,\n encoding: \"utf-8\",\n stdio: debug ? \"inherit\" : [\"pipe\", \"pipe\", \"pipe\"]\n };\n try {\n const result = execSync(\n `npx @expo/fingerprint fingerprint:generate --platform ${platform}`,\n execOptions\n );\n const parsed = JSON.parse(result);\n return {\n hash: parsed.hash,\n sources: parsed.sources || []\n };\n } catch (error) {\n const err = error;\n throw new Error(\n `Failed to generate ${platform} fingerprint: ${err.message}${err.stderr ? `\n${err.stderr}` : \"\"}`\n );\n }\n}\nfunction generatePreFingerprintHash(files, projectRoot = process.cwd()) {\n const hash = createHash(\"sha256\");\n for (const file of files) {\n const filePath = join(projectRoot, file);\n if (existsSync(filePath)) {\n hash.update(readFileSync(filePath));\n }\n }\n return hash.digest(\"hex\").slice(0, 16);\n}\nexport {\n generateFingerprint,\n generatePreFingerprintHash\n};\n//# sourceMappingURL=fingerprint.js.map\n"],"mappings":";;;;;;AAIA,eAAe,oBAAoB,SAAS;CAC1C,MAAM,EAAE,UAAU,cAAc,QAAQ,IAAI,GAAG,QAAQ,UAAU;CACjE,MAAM,cAAc,KAAK,aAAa,UAAU;CAChD,IAAI,CAAC,WAAW,WAAW,GAAG;EAC5B,MAAM,IAAI,MAAM,wBAAwB,YAAY,2BAA2B;CACjF;CACA,MAAM,cAAc;EAClB,KAAK;EACL,UAAU;EACV,OAAO,QAAQ,YAAY;GAAC;GAAQ;GAAQ;EAAM;CACpD;CACA,IAAI;EACF,MAAM,SAAS,SACb,yDAAyD,YACzD,WACF;EACA,MAAM,SAAS,KAAK,MAAM,MAAM;EAChC,OAAO;GACL,MAAM,OAAO;GACb,SAAS,OAAO,WAAW,CAAC;EAC9B;CACF,SAAS,OAAO;EACd,MAAM,MAAM;EACZ,MAAM,IAAI,MACR,sBAAsB,SAAS,gBAAgB,IAAI,UAAU,IAAI,SAAS;EAC9E,IAAI,WAAW,IACb;CACF;AACF;AACA,SAAS,2BAA2B,OAAO,cAAc,QAAQ,IAAI,GAAG;CACtE,MAAM,OAAO,WAAW,QAAQ;CAChC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,WAAW,KAAK,aAAa,IAAI;EACvC,IAAI,WAAW,QAAQ,GAAG;GACxB,KAAK,OAAO,aAAa,QAAQ,CAAC;EACpC;CACF;CACA,OAAO,KAAK,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AACvC"}
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DEFAULT_KV_TTL_SECONDS, DEFAULT_METRO_TIMEOUT_MS, DEFAULT_METRO_WAIT_ATTEMPTS, DEFAULT_METRO_WAIT_INTERVAL_MS, DETOX_SERVER_PORT, METRO_HOST, METRO_PORT, METRO_URL } from "./constants.mjs";
|
|
2
2
|
import { generateFingerprint, generatePreFingerprintHash } from "./fingerprint.mjs";
|
|
3
|
-
import { createCacheKey,
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { checkDeps,
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
import { createCacheKey, extendKVTTL, getFingerprintFromKV, loadCache, saveCache, saveFingerprintToKV } from "./cache.mjs";
|
|
4
|
+
import { isCI, isGitHubActions, runWithCache, setGitHubOutput } from "./runner.mjs";
|
|
5
|
+
import { prewarmBundle, setupSignalHandlers, startMetro, waitForMetro, withMetro } from "./metro.mjs";
|
|
6
|
+
import { buildDetoxArgs, parseDetoxArgs, runDetoxTests } from "./detox.mjs";
|
|
7
|
+
import { ensureAndroidFolder, setupAdbReverse, setupAndroidDevice, waitForDevice } from "./android-utils.mjs";
|
|
8
|
+
import { cleanupSimulators, ensureIOSApp, ensureIOSFolder } from "./ios-utils.mjs";
|
|
9
|
+
import { checkDeps, ensureAndroidDeps, ensureIosDeps, ensureMaestro, printDepsStatus } from "./deps.mjs";
|
|
10
|
+
|
|
11
|
+
export { DEFAULT_KV_TTL_SECONDS, DEFAULT_METRO_TIMEOUT_MS, DEFAULT_METRO_WAIT_ATTEMPTS, DEFAULT_METRO_WAIT_INTERVAL_MS, DETOX_SERVER_PORT, METRO_HOST, METRO_PORT, METRO_URL, buildDetoxArgs, checkDeps, cleanupSimulators, createCacheKey, ensureAndroidDeps, ensureAndroidFolder, ensureIOSApp, ensureIOSFolder, ensureIosDeps, ensureMaestro, extendKVTTL, generateFingerprint, generatePreFingerprintHash, getFingerprintFromKV, isCI, isGitHubActions, loadCache, parseDetoxArgs, prewarmBundle, printDepsStatus, runDetoxTests, runWithCache, saveCache, saveFingerprintToKV, setGitHubOutput, setupAdbReverse, setupAndroidDevice, setupSignalHandlers, startMetro, waitForDevice, waitForMetro, withMetro };
|