@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.
Files changed (45) hide show
  1. package/dist/android-utils.mjs +62 -0
  2. package/dist/android-utils.mjs.map +1 -0
  3. package/dist/cache.mjs +54 -70
  4. package/dist/cache.mjs.map +1 -1
  5. package/dist/cli.mjs +378 -403
  6. package/dist/cli.mjs.map +1 -1
  7. package/dist/constants.mjs +3 -2
  8. package/dist/constants.mjs.map +1 -1
  9. package/dist/deps.mjs +52 -64
  10. package/dist/deps.mjs.map +1 -1
  11. package/dist/detox.mjs +220 -202
  12. package/dist/detox.mjs.map +1 -1
  13. package/dist/fingerprint.mjs +35 -33
  14. package/dist/fingerprint.mjs.map +1 -1
  15. package/dist/index.js +10 -10
  16. package/dist/index.mjs +10 -10
  17. package/dist/ios-utils.mjs +318 -0
  18. package/dist/ios-utils.mjs.map +1 -0
  19. package/dist/metro.mjs +137 -139
  20. package/dist/metro.mjs.map +1 -1
  21. package/dist/runner.mjs +62 -65
  22. package/dist/runner.mjs.map +1 -1
  23. package/package.json +4 -4
  24. package/src/cli.ts +3 -3
  25. package/src/constants.ts +1 -1
  26. package/src/detox.ts +1 -0
  27. package/src/index.ts +2 -2
  28. package/src/metro.ts +2 -2
  29. package/src/run-detox-android.ts +1 -1
  30. package/src/run-detox-ios.ts +1 -1
  31. package/types/{android.d.ts → android-utils.d.ts} +1 -1
  32. package/types/android-utils.d.ts.map +1 -0
  33. package/types/cli.d.ts +1 -1
  34. package/types/constants.d.ts +2 -2
  35. package/types/detox.d.ts.map +1 -1
  36. package/types/index.d.ts +2 -2
  37. package/types/index.d.ts.map +1 -1
  38. package/types/{ios.d.ts → ios-utils.d.ts} +1 -1
  39. package/types/ios-utils.d.ts.map +1 -0
  40. package/dist/index.js.map +0 -1
  41. package/dist/index.mjs.map +0 -1
  42. package/types/android.d.ts.map +0 -1
  43. package/types/ios.d.ts.map +0 -1
  44. /package/src/{android.ts → android-utils.ts} +0 -0
  45. /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
- const defaultConfig = platform === "ios" ? "ios.sim.debug" : "android.emu.ci.debug";
5
- const {
6
- values,
7
- positionals
8
- } = parseArgs({
9
- options: {
10
- config: {
11
- type: "string",
12
- default: defaultConfig
13
- },
14
- "project-root": {
15
- type: "string",
16
- default: process.cwd()
17
- },
18
- headless: {
19
- type: "boolean",
20
- default: false
21
- },
22
- "record-logs": {
23
- type: "string",
24
- default: "all"
25
- },
26
- retries: {
27
- type: "string",
28
- default: "0"
29
- },
30
- workers: {
31
- type: "string",
32
- default: "1"
33
- }
34
- },
35
- allowPositionals: true
36
- });
37
- const retriesNum = Number.parseInt(values.retries, 10);
38
- if (Number.isNaN(retriesNum) || retriesNum < 0) {
39
- console.error("Error: retries must be a non-negative integer");
40
- process.exit(1);
41
- }
42
- const workersNum = Number.parseInt(values.workers, 10);
43
- if (Number.isNaN(workersNum) || workersNum < 1) {
44
- console.error("Error: workers must be a positive integer");
45
- process.exit(1);
46
- }
47
- return {
48
- config: values.config,
49
- projectRoot: values["project-root"],
50
- headless: values.headless,
51
- recordLogs: values["record-logs"],
52
- retries: retriesNum,
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
- const args = ["detox", "test", "--configuration", options.config, "--record-logs", options.recordLogs, "--retries", String(options.retries),
59
- // force jest to exit after tests complete (prevents hanging on open handles)
60
- "--forceExit"];
61
- if (options.headless) {
62
- args.push("--headless");
63
- }
64
- if (options.workers && options.workers > 1) {
65
- args.push("--workers", String(options.workers));
66
- }
67
- if (options.testFiles && options.testFiles.length > 0) {
68
- args.push(...options.testFiles);
69
- }
70
- return args;
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 = [/Exceeded timeout of \d+ ?ms for a hook/, /Exceeded timeout of \d+ ?ms while tearing down/, /Exceeded timeout of \d+ ?ms while handling jest-circus/, /\btimed out after \d+ms/, /could not connect to Detox/, /launch recovery deadline exceeded/, /skipping launchApp:/, /skipping reload:/, /FBSOpenApplicationServiceErrorDomain/, /FBSOpenApplicationErrorDomain/, /Detox worker instance has not been installed/];
75
- const REAL_FAILURE_SIGNATURES = [/Exceeded timeout of \d+ ?ms while waiting for/, /expect\([^)]+\)\.to/, /AssertionError:/, /(?<!Runtime)Error: (?!.*timed out after)/];
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
- const output = rawOutput.replace(ANSI_PATTERN, "");
80
- const delimiter = /(PASS|FAIL) (e2e\/[^\s):]+\.test\.ts)/g;
81
- const marks = [];
82
- for (const m of output.matchAll(delimiter)) {
83
- marks.push({
84
- kind: m[1],
85
- file: m[2],
86
- index: m.index ?? 0
87
- });
88
- }
89
- const failedFiles = [];
90
- const flakeFiles = [];
91
- const realFiles = [];
92
- for (let i = 0; i < marks.length; i++) {
93
- const mark = marks[i];
94
- if (mark.kind !== "FAIL") continue;
95
- const nextMark = i + 1 < marks.length ? marks[i + 1].index : output.length;
96
- const daemon = DETOX_DAEMON_LINE.exec(output.slice(mark.index, nextMark));
97
- const blockEnd = daemon ? mark.index + daemon.index : nextMark;
98
- const block = output.slice(mark.index, blockEnd);
99
- if (failedFiles.includes(mark.file)) continue;
100
- failedFiles.push(mark.file);
101
- const isReal = REAL_FAILURE_SIGNATURES.some(sig => sig.test(block));
102
- if (isReal) {
103
- realFiles.push(mark.file);
104
- } else {
105
- const isFlake = CONNECT_FLAKE_SIGNATURES.some(sig => sig.test(block));
106
- if (isFlake) {
107
- flakeFiles.push(mark.file);
108
- } else {
109
- realFiles.push(mark.file);
110
- }
111
- }
112
- }
113
- return {
114
- failedFiles,
115
- flakeFiles,
116
- realFiles
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
- console.info("Resetting Detox lock file...");
121
- const proc = Bun.spawn(["npx", "detox", "reset-lock-file"], {
122
- stdout: "inherit",
123
- stderr: "inherit"
124
- });
125
- await proc.exited;
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
- console.info("\n--- Running Detox tests ---");
129
- console.info(`Using fixed Detox server port: ${DETOX_SERVER_PORT}`);
130
- console.info(`Command: npx ${detoxArgs.join(" ")}`);
131
- const proc = Bun.spawn(["npx", ...detoxArgs], {
132
- env: {
133
- ...process.env,
134
- DETOX_SERVER_PORT: String(DETOX_SERVER_PORT),
135
- TAMAGUI_DETOX_TEST_FILES: testFiles.join(" ")
136
- },
137
- stdout: "pipe",
138
- stderr: "pipe"
139
- });
140
- const decoder = new TextDecoder();
141
- let output = "";
142
- const tee = async (stream, sink) => {
143
- const reader = stream.getReader();
144
- while (true) {
145
- const {
146
- done,
147
- value
148
- } = await reader.read();
149
- if (done) break;
150
- sink.write(value);
151
- output += decoder.decode(value, {
152
- stream: true
153
- });
154
- }
155
- };
156
- const pumps = Promise.all([tee(proc.stdout, process.stdout), tee(proc.stderr, process.stderr)]);
157
- const timeoutPromise = new Promise(resolve => {
158
- setTimeout(() => resolve("timeout"), DETOX_TIMEOUT_MS);
159
- });
160
- const result = await Promise.race([proc.exited, timeoutPromise]);
161
- if (result === "timeout") {
162
- console.info("\nDetox timed out, killing process...");
163
- proc.kill("SIGKILL");
164
- return {
165
- exitCode: 1,
166
- output
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
- await resetDetoxLockFile();
177
- const detoxArgs = buildDetoxArgs(options);
178
- const {
179
- exitCode,
180
- output
181
- } = await spawnDetoxOnce(detoxArgs, options.testFiles ?? []);
182
- if (exitCode === 0) {
183
- console.info("\nAll tests passed!");
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
- const {
189
- flakeFiles,
190
- realFiles
191
- } = classifyDetoxFailures(output);
192
- if (flakeFiles.length === 0) {
193
- return exitCode;
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
- return exitCode;
199
- }
200
- for (let attempt = 1; attempt <= MAX_CONNECT_FLAKE_RETRIES; attempt++) {
201
- console.info(`
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
- await resetDetoxLockFile();
204
- const retryArgs = buildDetoxArgs({
205
- ...options,
206
- testFiles: flakeFiles
207
- });
208
- const retry = await spawnDetoxOnce(retryArgs, flakeFiles);
209
- if (retry.exitCode === 0) {
210
- console.info(`
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
- return 0;
213
- }
214
- const retryClass = classifyDetoxFailures(retry.output);
215
- if (retryClass.realFiles.length > 0 || retryClass.flakeFiles.length === 0) {
216
- console.info("\nRetry surfaced a non-flake failure; surfacing it.");
217
- return retry.exitCode;
218
- }
219
- flakeFiles.length = 0;
220
- flakeFiles.push(...retryClass.flakeFiles);
221
- }
222
- console.info("\nConnect-flake retries exhausted; tests still failing.");
223
- return 1;
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
@@ -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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"}
@@ -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
- const {
7
- platform,
8
- projectRoot = process.cwd(),
9
- debug = false
10
- } = options;
11
- const appJsonPath = join(projectRoot, "app.json");
12
- if (!existsSync(appJsonPath)) {
13
- throw new Error(`No app.json found at ${projectRoot}. Is this an Expo project?`);
14
- }
15
- const execOptions = {
16
- cwd: projectRoot,
17
- encoding: "utf-8",
18
- stdio: debug ? "inherit" : ["pipe", "pipe", "pipe"]
19
- };
20
- try {
21
- const result = execSync(`npx @expo/fingerprint fingerprint:generate --platform ${platform}`, execOptions);
22
- const parsed = JSON.parse(result);
23
- return {
24
- hash: parsed.hash,
25
- sources: parsed.sources || []
26
- };
27
- } catch (error) {
28
- const err = error;
29
- throw new Error(`Failed to generate ${platform} fingerprint: ${err.message}${err.stderr ? `
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
- const hash = createHash("sha256");
35
- for (const file of files) {
36
- const filePath = join(projectRoot, file);
37
- if (existsSync(filePath)) {
38
- hash.update(readFileSync(filePath));
39
- }
40
- }
41
- return hash.digest("hex").slice(0, 16);
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
@@ -1 +1 @@
1
- {"version":3,"names":["execSync","createHash","existsSync","readFileSync","join","generateFingerprint","options","platform","projectRoot","process","cwd","debug","appJsonPath","Error","execOptions","encoding","stdio","result","parsed","JSON","parse","hash","sources","error","err","message","stderr","generatePreFingerprintHash","files","file","filePath","update","digest","slice"],"sources":["../src/fingerprint.ts"],"sourcesContent":[null],"mappings":"AAAA,SAASA,QAAA,QAAsC;AAC/C,SAASC,UAAA,QAAkB;AAC3B,SAASC,UAAA,EAAYC,YAAA,QAAoB;AACzC,SAASC,IAAA,QAAY;AAkBrB,eAAsBC,oBACpBC,OAAA,EAC4B;EAC5B,MAAM;IAAEC,QAAA;IAAUC,WAAA,GAAcC,OAAA,CAAQC,GAAA,CAAI;IAAGC,KAAA,GAAQ;EAAM,IAAIL,OAAA;EAGjE,MAAMM,WAAA,GAAcR,IAAA,CAAKI,WAAA,EAAa,UAAU;EAChD,IAAI,CAACN,UAAA,CAAWU,WAAW,GAAG;IAC5B,MAAM,IAAIC,KAAA,CAAM,wBAAwBL,WAAW,4BAA4B;EACjF;EAEA,MAAMM,WAAA,GAA+B;IACnCJ,GAAA,EAAKF,WAAA;IACLO,QAAA,EAAU;IACVC,KAAA,EAAOL,KAAA,GAAQ,YAAY,CAAC,QAAQ,QAAQ,MAAM;EACpD;EAEA,IAAI;IACF,MAAMM,MAAA,GAASjB,QAAA,CACb,yDAAyDO,QAAQ,IACjEO,WACF;IAEA,MAAMI,MAAA,GAASC,IAAA,CAAKC,KAAA,CAAMH,MAAM;IAEhC,OAAO;MACLI,IAAA,EAAMH,MAAA,CAAOG,IAAA;MACbC,OAAA,EAASJ,MAAA,CAAOI,OAAA,IAAW;IAC7B;EACF,SAASC,KAAA,EAAO;IACd,MAAMC,GAAA,GAAMD,KAAA;IACZ,MAAM,IAAIV,KAAA,CACR,sBAAsBN,QAAQ,iBAAiBiB,GAAA,CAAIC,OAAO,GAAGD,GAAA,CAAIE,MAAA,GAAS;AAAA,EAAKF,GAAA,CAAIE,MAAM,KAAK,EAAE,EAClG;EACF;AACF;AAMO,SAASC,2BACdC,KAAA,EACApB,WAAA,GAAsBC,OAAA,CAAQC,GAAA,CAAI,GAC1B;EACR,MAAMW,IAAA,GAAOpB,UAAA,CAAW,QAAQ;EAEhC,WAAW4B,IAAA,IAAQD,KAAA,EAAO;IACxB,MAAME,QAAA,GAAW1B,IAAA,CAAKI,WAAA,EAAaqB,IAAI;IACvC,IAAI3B,UAAA,CAAW4B,QAAQ,GAAG;MACxBT,IAAA,CAAKU,MAAA,CAAO5B,YAAA,CAAa2B,QAAQ,CAAC;IACpC;EACF;EAEA,OAAOT,IAAA,CAAKW,MAAA,CAAO,KAAK,EAAEC,KAAA,CAAM,GAAG,EAAE;AACvC","ignoreList":[]}
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 { METRO_HOST, METRO_PORT, METRO_URL, DETOX_SERVER_PORT, DEFAULT_METRO_WAIT_ATTEMPTS, DEFAULT_METRO_WAIT_INTERVAL_MS, DEFAULT_METRO_TIMEOUT_MS, DEFAULT_KV_TTL_SECONDS } from "./constants.mjs";
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, saveFingerprintToKV, getFingerprintFromKV, extendKVTTL, saveCache, loadCache } from "./cache.mjs";
4
- import { runWithCache, setGitHubOutput, isGitHubActions, isCI } from "./runner.mjs";
5
- import { waitForMetro, prewarmBundle, startMetro, setupSignalHandlers, withMetro } from "./metro.mjs";
6
- import { parseDetoxArgs, buildDetoxArgs, runDetoxTests } from "./detox.mjs";
7
- import { waitForDevice, setupAdbReverse, setupAndroidDevice, ensureAndroidFolder } from "./android";
8
- import { ensureIOSFolder, ensureIOSApp, cleanupSimulators } from "./ios";
9
- import { checkDeps, ensureIosDeps, ensureAndroidDeps, ensureMaestro, printDepsStatus } from "./deps.mjs";
10
- 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 };
11
- //# sourceMappingURL=index.js.map
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 };