dceky 1.2.1 → 1.2.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/README.md +23 -17
- package/lib/src/genConfiguration/index.js +0 -9
- package/lib/src/genConfiguration/index.js.map +1 -1
- package/lib/start/constants/DEFAULT_THREADS_PER_COMBO.d.ts +1 -1
- package/lib/start/constants/DEFAULT_THREADS_PER_COMBO.js +1 -1
- package/lib/start/helpers/embedScreenshotsInReportData.d.ts +14 -0
- package/lib/start/helpers/embedScreenshotsInReportData.js +139 -0
- package/lib/start/helpers/embedScreenshotsInReportData.js.map +1 -0
- package/lib/start/helpers/executeCypress.js +1 -1
- package/lib/start/helpers/executeCypress.js.map +1 -1
- package/lib/start/helpers/generateHtmlReport.js +7 -0
- package/lib/start/helpers/generateHtmlReport.js.map +1 -1
- package/lib/start/helpers/getCypressDetectedBrowsersForChooser.d.ts +2 -1
- package/lib/start/helpers/getCypressDetectedBrowsersForChooser.js +34 -22
- package/lib/start/helpers/getCypressDetectedBrowsersForChooser.js.map +1 -1
- package/lib/start/helpers/mergeAllReportsAndGenerateHtml.js +52 -0
- package/lib/start/helpers/mergeAllReportsAndGenerateHtml.js.map +1 -1
- package/lib/start/helpers/reportHomepage.ejs +18 -7
- package/lib/start/index.d.ts +2 -2
- package/lib/start/index.js +214 -175
- package/lib/start/index.js.map +1 -1
- package/package.json +2 -1
- package/src/genConfiguration/index.ts +0 -13
- package/start/constants/DEFAULT_THREADS_PER_COMBO.ts +1 -1
- package/start/helpers/embedScreenshotsInReportData.ts +171 -0
- package/start/helpers/executeCypress.ts +1 -1
- package/start/helpers/generateHtmlReport.ts +12 -0
- package/start/helpers/getCypressDetectedBrowsersForChooser.ts +41 -23
- package/start/helpers/mergeAllReportsAndGenerateHtml.ts +60 -0
- package/start/helpers/reportHomepage.ejs +18 -7
- package/start/index.ts +218 -181
- package/start/helpers/extractArgValue.ts +0 -42
package/start/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
/**
|
|
3
|
-
* Checks
|
|
4
|
-
* asks user for missing selections, and launches Cypress
|
|
3
|
+
* Checks environment variables (for PROFILE, BROWSER, and HEADLESS),
|
|
4
|
+
* asks the user for missing selections, and launches Cypress
|
|
5
5
|
* @author Gardenia Liu
|
|
6
6
|
* @author Yuen Ler Chow
|
|
7
7
|
*/
|
|
@@ -9,14 +9,17 @@
|
|
|
9
9
|
// Import libs
|
|
10
10
|
import fs from 'fs';
|
|
11
11
|
import path from 'path';
|
|
12
|
+
import dotenv from 'dotenv';
|
|
13
|
+
import clear from 'clear';
|
|
12
14
|
|
|
13
15
|
// Import helpers
|
|
14
16
|
import showChooser from './helpers/showChooser';
|
|
15
17
|
import parseCommaSeparated from './helpers/parseCommaSeparated';
|
|
16
18
|
import findProfilesByNames from './helpers/findProfilesByNames';
|
|
17
19
|
import validateBrowsers from './helpers/validateBrowsers';
|
|
18
|
-
import extractArgValue from './helpers/extractArgValue';
|
|
19
20
|
import getRootPath from './helpers/getRootPath';
|
|
21
|
+
import prompt from './helpers/prompt';
|
|
22
|
+
import print from './helpers/print';
|
|
20
23
|
import executeCypress from './helpers/executeCypress';
|
|
21
24
|
import getCypressDetectedBrowsersForChooser from './helpers/getCypressDetectedBrowsersForChooser';
|
|
22
25
|
import getE2ETestFoldersForChooser from './helpers/getE2ETestFoldersForChooser';
|
|
@@ -30,6 +33,9 @@ import ALL_E2E_TESTS_SPEC_PATTERN from './constants/ALL_E2E_TESTS_SPEC_PATTERN';
|
|
|
30
33
|
// Get the project directory
|
|
31
34
|
const pwd = getRootPath();
|
|
32
35
|
|
|
36
|
+
// Load .env vars before reading startup configuration
|
|
37
|
+
dotenv.config({ path: path.join(pwd, '.env') });
|
|
38
|
+
|
|
33
39
|
// Find available profiles under /cypress/profiles
|
|
34
40
|
const profilesDir = path.join(pwd, 'cypress/profiles');
|
|
35
41
|
if (!fs.existsSync(profilesDir)) {
|
|
@@ -58,232 +64,263 @@ if (profiles.length === 0) {
|
|
|
58
64
|
process.exit(1);
|
|
59
65
|
}
|
|
60
66
|
|
|
61
|
-
//
|
|
62
|
-
const args = process.argv.slice(2);
|
|
63
|
-
|
|
64
|
-
// Get selections from environment variables or CLI arguments
|
|
67
|
+
// Get selections from environment variables
|
|
65
68
|
let selectedProfiles: { file: string; profileName: string }[] = [];
|
|
66
69
|
let selectedBrowsers: string[] = [];
|
|
67
70
|
let isHeadless = false;
|
|
68
71
|
let selectedE2ETestFolder = ALL_E2E_TESTS_LABEL;
|
|
69
72
|
let selectedE2ESpecPattern = ALL_E2E_TESTS_SPEC_PATTERN;
|
|
70
73
|
|
|
71
|
-
// Check for PROFILE
|
|
72
|
-
const
|
|
73
|
-
const argProfileRaw = extractArgValue(args, '--profile');
|
|
74
|
-
const profileRaw = envProfileRaw || argProfileRaw;
|
|
74
|
+
// Check for PROFILE
|
|
75
|
+
const profileRaw = process.env.PROFILE;
|
|
75
76
|
|
|
76
77
|
if (profileRaw) {
|
|
77
78
|
const profileNames = parseCommaSeparated(profileRaw);
|
|
78
79
|
selectedProfiles = findProfilesByNames(profileNames, profiles);
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
// Check for BROWSER
|
|
82
|
-
const
|
|
83
|
-
const argBrowserRaw = extractArgValue(args, '--browser');
|
|
84
|
-
const browserRaw = envBrowserRaw || argBrowserRaw;
|
|
82
|
+
// Check for BROWSER
|
|
83
|
+
const browserRaw = process.env.BROWSER;
|
|
85
84
|
|
|
86
85
|
if (browserRaw) {
|
|
87
86
|
const browserNames = parseCommaSeparated(browserRaw);
|
|
88
87
|
selectedBrowsers = validateBrowsers(browserNames);
|
|
89
88
|
}
|
|
90
89
|
|
|
91
|
-
// Check for E2E_TEST_FOLDER
|
|
92
|
-
const
|
|
93
|
-
const argE2ETestFolderRaw = extractArgValue(args, '--e2eFolder');
|
|
94
|
-
const e2eTestFolderRaw = envE2ETestFolderRaw || argE2ETestFolderRaw;
|
|
90
|
+
// Check for E2E_TEST_FOLDER
|
|
91
|
+
const e2eTestFolderRaw = process.env.E2E_TEST_FOLDER;
|
|
95
92
|
|
|
96
|
-
// Check for HEADLESS
|
|
93
|
+
// Check for HEADLESS
|
|
97
94
|
const envHeadlessRaw = process.env.HEADLESS;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// Check for THREADS_PER_COMBO (env var or CLI arg)
|
|
103
|
-
const envThreadsRaw = process.env.THREADS_PER_COMBO;
|
|
104
|
-
const argThreadsRaw = extractArgValue(args, '--threadsPerCombo');
|
|
105
|
-
const threadsRaw = envThreadsRaw || argThreadsRaw;
|
|
95
|
+
isHeadless = !!(envHeadlessRaw && envHeadlessRaw.toLowerCase() !== 'false');
|
|
96
|
+
|
|
97
|
+
// Check for THREADS_PER_COMBO
|
|
98
|
+
const threadsRaw = process.env.THREADS_PER_COMBO;
|
|
106
99
|
let threadsPerCombo = DEFAULT_THREADS_PER_COMBO;
|
|
107
|
-
if (threadsRaw) {
|
|
108
|
-
const parsed = Number.parseInt(threadsRaw, 10);
|
|
109
|
-
if (!Number.isNaN(parsed) && parsed > 0) {
|
|
110
|
-
threadsPerCombo = parsed;
|
|
111
|
-
} else {
|
|
112
|
-
console.warn(`⚠️ Invalid THREADS_PER_COMBO value: "${threadsRaw}". Using default (${DEFAULT_THREADS_PER_COMBO}).`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
100
|
|
|
101
|
+
const start = async (): Promise<void> => {
|
|
116
102
|
// If headless wasn't specified, ask the user to choose
|
|
117
|
-
if (!envHeadlessRaw
|
|
118
|
-
const choices = showChooser({
|
|
119
|
-
question: 'Run in headless mode?',
|
|
120
|
-
options: [
|
|
121
|
-
{ tag: 'Y', description: 'Yes (headless)' },
|
|
122
|
-
{ tag: 'N', description: 'No (interactive)' },
|
|
123
|
-
],
|
|
124
|
-
title: 'Select mode',
|
|
125
|
-
allowMulti: false,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
isHeadless = choices[0].tag === 'Y';
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// If running headless, optionally narrow tests to one e2e folder
|
|
132
|
-
if (isHeadless) {
|
|
133
|
-
const choosableE2ETestFolders = getE2ETestFoldersForChooser();
|
|
134
|
-
|
|
135
|
-
if (e2eTestFolderRaw) {
|
|
136
|
-
const e2eSelection = findE2ETestFolderByName(e2eTestFolderRaw, choosableE2ETestFolders);
|
|
137
|
-
selectedE2ETestFolder = e2eSelection.description;
|
|
138
|
-
selectedE2ESpecPattern = e2eSelection.specPattern;
|
|
139
|
-
} else if (choosableE2ETestFolders.length > 0) {
|
|
103
|
+
if (!envHeadlessRaw) {
|
|
140
104
|
const choices = showChooser({
|
|
141
|
-
question: '
|
|
105
|
+
question: 'Run in headless mode?',
|
|
142
106
|
options: [
|
|
143
|
-
{
|
|
144
|
-
|
|
145
|
-
description: ALL_E2E_TESTS_LABEL,
|
|
146
|
-
},
|
|
147
|
-
...choosableE2ETestFolders,
|
|
107
|
+
{ tag: 'Y', description: 'Yes (headless)' },
|
|
108
|
+
{ tag: 'N', description: 'No (interactive)' },
|
|
148
109
|
],
|
|
149
|
-
title: 'Select
|
|
110
|
+
title: 'Select mode',
|
|
150
111
|
allowMulti: false,
|
|
151
112
|
});
|
|
152
113
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
114
|
+
isHeadless = choices[0].tag === 'Y';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// This takes 5+ seconds to run, so start it now while we wait for the user to answer other questions
|
|
118
|
+
const cypressDetectedBrowsersPromise = (
|
|
119
|
+
isHeadless && selectedBrowsers.length === 0
|
|
120
|
+
? getCypressDetectedBrowsersForChooser()
|
|
121
|
+
: null
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
if (isHeadless) {
|
|
125
|
+
if (threadsRaw) {
|
|
126
|
+
const trimmedThreads = threadsRaw.trim();
|
|
127
|
+
const parsed = Number.parseInt(trimmedThreads, 10);
|
|
128
|
+
if (/^\d+$/.test(trimmedThreads) && parsed > 0) {
|
|
129
|
+
threadsPerCombo = parsed;
|
|
130
|
+
} else {
|
|
131
|
+
console.warn(
|
|
132
|
+
`⚠️ Invalid THREADS_PER_COMBO value: "${threadsRaw}". `
|
|
133
|
+
+ 'Use a positive whole number. '
|
|
134
|
+
+ `Using default (${DEFAULT_THREADS_PER_COMBO}).`,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
let parsedThreads: number | null = null;
|
|
139
|
+
clear();
|
|
140
|
+
print.title('Num Test Files in Parallel Per Combo');
|
|
141
|
+
console.log('');
|
|
142
|
+
print.subtitle('How many test files should run in parallel per profile/browser combo? (Press Enter for 1)');
|
|
143
|
+
while (parsedThreads === null) {
|
|
144
|
+
const response = prompt('> ', true).trim();
|
|
145
|
+
if (!response) {
|
|
146
|
+
parsedThreads = DEFAULT_THREADS_PER_COMBO;
|
|
147
|
+
} else {
|
|
148
|
+
const parsed = Number.parseInt(response, 10);
|
|
149
|
+
parsedThreads = /^\d+$/.test(response) && parsed > 0 ? parsed : null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (parsedThreads === null) {
|
|
153
|
+
console.log('Please enter a positive whole number, or press Enter for 1.');
|
|
154
|
+
console.log('');
|
|
155
|
+
}
|
|
163
156
|
}
|
|
157
|
+
|
|
158
|
+
threadsPerCombo = parsedThreads;
|
|
164
159
|
}
|
|
165
160
|
}
|
|
166
|
-
}
|
|
167
161
|
|
|
168
|
-
// If running headless
|
|
169
|
-
if (isHeadless
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
162
|
+
// If running headless, optionally narrow tests to one e2e folder
|
|
163
|
+
if (isHeadless) {
|
|
164
|
+
const choosableE2ETestFolders = getE2ETestFoldersForChooser();
|
|
165
|
+
|
|
166
|
+
if (e2eTestFolderRaw) {
|
|
167
|
+
const e2eSelection = findE2ETestFolderByName(e2eTestFolderRaw, choosableE2ETestFolders);
|
|
168
|
+
selectedE2ETestFolder = e2eSelection.description;
|
|
169
|
+
selectedE2ESpecPattern = e2eSelection.specPattern;
|
|
170
|
+
} else if (choosableE2ETestFolders.length > 0) {
|
|
171
|
+
const choices = showChooser({
|
|
172
|
+
question: 'Choose e2e test folder:',
|
|
173
|
+
options: [
|
|
174
|
+
{
|
|
175
|
+
tag: 'A',
|
|
176
|
+
description: ALL_E2E_TESTS_LABEL,
|
|
177
|
+
},
|
|
178
|
+
...choosableE2ETestFolders,
|
|
179
|
+
],
|
|
180
|
+
title: 'Select E2E Tests',
|
|
181
|
+
allowMulti: false,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const allTestsChosen = choices.some((choice) => {
|
|
185
|
+
return choice.tag === 'A';
|
|
186
|
+
});
|
|
187
|
+
if (!allTestsChosen) {
|
|
188
|
+
const selectedFolder = choosableE2ETestFolders.find((folder) => {
|
|
189
|
+
return folder.tag === choices[0].tag;
|
|
190
|
+
});
|
|
191
|
+
if (selectedFolder) {
|
|
192
|
+
selectedE2ETestFolder = selectedFolder.relativePath;
|
|
193
|
+
selectedE2ESpecPattern = selectedFolder.specPattern;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
174
197
|
}
|
|
175
|
-
const options = choosableBrowsers.map(({ tag, name }) => {
|
|
176
|
-
return {
|
|
177
|
-
tag,
|
|
178
|
-
description: name,
|
|
179
|
-
};
|
|
180
|
-
});
|
|
181
198
|
|
|
182
|
-
//
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
199
|
+
// If running headless and no browsers were selected, ask the user to choose
|
|
200
|
+
if (isHeadless && selectedBrowsers.length === 0) {
|
|
201
|
+
const choosableBrowsers = await cypressDetectedBrowsersPromise;
|
|
202
|
+
if (choosableBrowsers.length === 0) {
|
|
203
|
+
console.error('No browsers detected. Run `npx cypress info`, install a supported browser, and try again.');
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
const options = choosableBrowsers.map(({ tag, name }) => {
|
|
207
|
+
return {
|
|
208
|
+
tag,
|
|
209
|
+
description: name,
|
|
210
|
+
};
|
|
187
211
|
});
|
|
188
|
-
}
|
|
189
212
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
213
|
+
// Add "All" option if there are multiple browsers
|
|
214
|
+
if (choosableBrowsers.length > 1) {
|
|
215
|
+
options.unshift({
|
|
216
|
+
tag: 'A',
|
|
217
|
+
description: 'All browsers',
|
|
218
|
+
});
|
|
219
|
+
}
|
|
196
220
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
221
|
+
const choices = showChooser({
|
|
222
|
+
question: 'Choose browser(s) (commas for multiple: C,F)',
|
|
223
|
+
options,
|
|
224
|
+
title: 'Select Browser(s)',
|
|
225
|
+
allowMulti: true,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const allBrowsersChosen = choices.some((choice) => {
|
|
229
|
+
return choice.tag === 'A';
|
|
203
230
|
});
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
231
|
+
if (allBrowsersChosen) {
|
|
232
|
+
selectedBrowsers = choosableBrowsers.map((b) => {
|
|
233
|
+
return b.name;
|
|
234
|
+
});
|
|
235
|
+
} else {
|
|
236
|
+
selectedBrowsers = (
|
|
237
|
+
choices
|
|
238
|
+
.map((choice) => {
|
|
239
|
+
const selectedBrowser = choosableBrowsers.find((browser) => {
|
|
240
|
+
return browser.tag.toLowerCase() === choice.tag.toLowerCase();
|
|
241
|
+
});
|
|
242
|
+
return selectedBrowser?.name ?? null;
|
|
243
|
+
})
|
|
244
|
+
.filter((name) => {
|
|
245
|
+
return name !== null;
|
|
246
|
+
})
|
|
247
|
+
);
|
|
248
|
+
}
|
|
217
249
|
}
|
|
218
|
-
}
|
|
219
250
|
|
|
220
|
-
// If no profiles were selected, ask the user to choose
|
|
221
|
-
// - In headless mode, allow selecting multiple profiles
|
|
222
|
-
// - In visible mode, only allow selecting a single profile
|
|
223
|
-
// In the case where their environment selected more than one profile, but they are not headless, make them re-choose
|
|
224
|
-
if (
|
|
225
|
-
|
|
251
|
+
// If no profiles were selected, ask the user to choose
|
|
252
|
+
// - In headless mode, allow selecting multiple profiles
|
|
253
|
+
// - In visible mode, only allow selecting a single profile
|
|
254
|
+
// In the case where their environment selected more than one profile, but they are not headless, make them re-choose
|
|
255
|
+
if (
|
|
256
|
+
selectedProfiles.length === 0
|
|
226
257
|
|| (!isHeadless && selectedProfiles.length > 1)
|
|
227
|
-
) {
|
|
258
|
+
) {
|
|
228
259
|
// Filter out "Default" profile from the chooser options
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
260
|
+
const choosableProfiles = profiles.filter((p) => {
|
|
261
|
+
return p.profileName.toLowerCase() !== 'default';
|
|
262
|
+
});
|
|
232
263
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
264
|
+
const options = choosableProfiles.map((p, idx) => {
|
|
265
|
+
return {
|
|
266
|
+
tag: String(idx + 1),
|
|
267
|
+
description: p.profileName,
|
|
268
|
+
};
|
|
269
|
+
});
|
|
239
270
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
271
|
+
// If there are multiple profiles, add an "All" option for headless mode
|
|
272
|
+
if (isHeadless && choosableProfiles.length > 1) {
|
|
273
|
+
options.unshift({
|
|
274
|
+
tag: 'A',
|
|
275
|
+
description: 'All profiles',
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const choices = showChooser({
|
|
280
|
+
question: (
|
|
281
|
+
isHeadless
|
|
282
|
+
? 'Choose profile(s) (commas for multiple: 1,2):'
|
|
283
|
+
: 'Choose profile:'
|
|
284
|
+
),
|
|
285
|
+
options,
|
|
286
|
+
title: `Choose profile${isHeadless ? 's' : ''}`,
|
|
287
|
+
allowMulti: isHeadless,
|
|
245
288
|
});
|
|
246
|
-
}
|
|
247
289
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
290
|
+
const allProfilesChosen = choices.some((choice) => {
|
|
291
|
+
return choice.tag === 'A';
|
|
292
|
+
});
|
|
293
|
+
if (allProfilesChosen) {
|
|
294
|
+
selectedProfiles = choosableProfiles;
|
|
295
|
+
} else {
|
|
296
|
+
selectedProfiles = (
|
|
297
|
+
choices
|
|
298
|
+
.map((choice) => {
|
|
299
|
+
const profileIndex = Number.parseInt(choice.tag, 10) - 1;
|
|
300
|
+
if (Number.isNaN(profileIndex)) {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
return choosableProfiles[profileIndex] ?? null;
|
|
304
|
+
})
|
|
305
|
+
.filter((profile) => {
|
|
306
|
+
return profile !== null;
|
|
307
|
+
})
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
258
311
|
|
|
259
|
-
|
|
260
|
-
|
|
312
|
+
// Execute Cypress with selected configuration
|
|
313
|
+
await executeCypress({
|
|
314
|
+
selectedProfiles,
|
|
315
|
+
selectedBrowsers,
|
|
316
|
+
isHeadless,
|
|
317
|
+
threadsPerCombo,
|
|
318
|
+
e2eTestFolder: selectedE2ETestFolder,
|
|
319
|
+
e2eSpecPattern: selectedE2ESpecPattern,
|
|
261
320
|
});
|
|
262
|
-
|
|
263
|
-
selectedProfiles = choosableProfiles;
|
|
264
|
-
} else {
|
|
265
|
-
selectedProfiles = (
|
|
266
|
-
choices
|
|
267
|
-
.map((choice) => {
|
|
268
|
-
const profileIndex = Number.parseInt(choice.tag, 10) - 1;
|
|
269
|
-
if (Number.isNaN(profileIndex)) {
|
|
270
|
-
return null;
|
|
271
|
-
}
|
|
272
|
-
return choosableProfiles[profileIndex] ?? null;
|
|
273
|
-
})
|
|
274
|
-
.filter((profile) => {
|
|
275
|
-
return profile !== null;
|
|
276
|
-
})
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
321
|
+
};
|
|
280
322
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
selectedBrowsers,
|
|
285
|
-
isHeadless,
|
|
286
|
-
threadsPerCombo,
|
|
287
|
-
e2eTestFolder: selectedE2ETestFolder,
|
|
288
|
-
e2eSpecPattern: selectedE2ESpecPattern,
|
|
323
|
+
start().catch((error) => {
|
|
324
|
+
console.error(error);
|
|
325
|
+
process.exit(1);
|
|
289
326
|
});
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extract CLI argument value or check if flag exists
|
|
3
|
-
* @author Gardenia Liu
|
|
4
|
-
* @author Yuen Ler Chow
|
|
5
|
-
* @param args array of CLI arguments
|
|
6
|
-
* @param argName the argument name to look for (e.g., '--profile' or '--headless')
|
|
7
|
-
* @returns the argument value if found, empty string if flag exists without value, undefined otherwise
|
|
8
|
-
*/
|
|
9
|
-
const extractArgValue = (args: string[], argName: string): string | undefined => {
|
|
10
|
-
let matchedValue: string | undefined;
|
|
11
|
-
args.some((rawArg, index) => {
|
|
12
|
-
const normalizedArg = rawArg.toLowerCase();
|
|
13
|
-
|
|
14
|
-
// Case 1: flag and value combined, e.g. "--profile=stage"
|
|
15
|
-
if (normalizedArg.startsWith(`${argName}=`)) {
|
|
16
|
-
const [, value] = rawArg.split('=');
|
|
17
|
-
matchedValue = value;
|
|
18
|
-
return true; // stop scanning
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Case 2: flag by itself, possibly followed by a separate value
|
|
22
|
-
if (normalizedArg === argName) {
|
|
23
|
-
const nextArg = args[index + 1];
|
|
24
|
-
|
|
25
|
-
// If the next token exists and isn't another flag, treat it as the value
|
|
26
|
-
if (nextArg && !nextArg.startsWith('--')) {
|
|
27
|
-
matchedValue = nextArg;
|
|
28
|
-
} else {
|
|
29
|
-
// Flag exists but no value given (boolean-style flag) (e.g. "--headless")
|
|
30
|
-
matchedValue = '';
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return true; // stop scanning
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return false;
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
return matchedValue;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export default extractArgValue;
|