cypress-cli-select 1.0.0
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/.github/workflows/jest.yml +12 -0
- package/CONTRIBUTING.md +39 -0
- package/LICENSE.md +21 -0
- package/README.md +197 -0
- package/assets/choose-spec-pattern-demo.gif +0 -0
- package/assets/cypress-cli-select-animated.gif +0 -0
- package/assets/print-selected-demo.png +0 -0
- package/assets/run-help.gif +0 -0
- package/assets/run-spec-tag.gif +0 -0
- package/assets/run-spec-title.gif +0 -0
- package/cypress/e2e/1-getting-started/todo.cy.js +143 -0
- package/cypress/e2e/2-advanced-examples/actions.cy.js +321 -0
- package/cypress/e2e/2-advanced-examples/aliasing.cy.js +39 -0
- package/cypress/e2e/2-advanced-examples/assertions.cy.js +176 -0
- package/cypress/e2e/2-advanced-examples/connectors.cy.js +98 -0
- package/cypress/e2e/2-advanced-examples/cookies.cy.js +118 -0
- package/cypress/e2e/2-advanced-examples/cypress_api.cy.js +211 -0
- package/cypress/e2e/2-advanced-examples/location.cy.js +32 -0
- package/cypress/e2e/2-advanced-examples/misc.cy.js +90 -0
- package/cypress/e2e/2-advanced-examples/navigation.cy.js +55 -0
- package/cypress/e2e/2-advanced-examples/network_requests.cy.js +163 -0
- package/cypress/e2e/2-advanced-examples/querying.cy.js +114 -0
- package/cypress/e2e/2-advanced-examples/spies_stubs_clocks.cy.js +220 -0
- package/cypress/e2e/2-advanced-examples/storage.cy.js +117 -0
- package/cypress/e2e/2-advanced-examples/traversal.cy.js +126 -0
- package/cypress/e2e/2-advanced-examples/utilities.cy.js +109 -0
- package/cypress/e2e/2-advanced-examples/viewport.cy.js +58 -0
- package/cypress/e2e/2-advanced-examples/waiting.cy.js +30 -0
- package/cypress/e2e/2-advanced-examples/window.cy.js +22 -0
- package/cypress/support/commands.js +25 -0
- package/cypress/support/component-index.html +14 -0
- package/cypress/support/component.js +24 -0
- package/cypress/support/e2e.js +19 -0
- package/cypress.config.js +25 -0
- package/cypress.new.config.js +26 -0
- package/index.js +586 -0
- package/package.json +46 -0
- package/src/components/Clock.cy.js +13 -0
- package/src/components/Stepper.cy.js +13 -0
- package/src/sortable-list.js +82 -0
- package/tapes/run-help.tape +94 -0
- package/tapes/run-spec-tag.tape +163 -0
- package/tapes/run-spec-title.tape +191 -0
- package/tests/cli-component.spec.js +409 -0
- package/tests/cli-e2e.spec.js +439 -0
package/index.js
ADDED
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { getSpecs } = require("find-cypress-specs");
|
|
4
|
+
const cypress = require("cypress");
|
|
5
|
+
const Fuse = require("fuse.js");
|
|
6
|
+
const { select } = require("inquirer-select-pro");
|
|
7
|
+
const { getTestNames } = require("find-test-names");
|
|
8
|
+
const { sortingList } = require("./src/sortable-list");
|
|
9
|
+
const yarg = require("yargs");
|
|
10
|
+
const pc = require("picocolors");
|
|
11
|
+
const fs = require("fs");
|
|
12
|
+
const path = require("path");
|
|
13
|
+
|
|
14
|
+
// Used when walking the getTestNames array of objects with nested suites
|
|
15
|
+
// Grabs spec name, parent suite names, and test title
|
|
16
|
+
function iterateObject(obj, arr, continuedArr, tagsArr) {
|
|
17
|
+
if (obj.tests.length > 0) {
|
|
18
|
+
obj.tests.forEach((test) => {
|
|
19
|
+
collectTags(tagsArr, test.tags, test.requiredTags);
|
|
20
|
+
let newArr = [...continuedArr, test.name];
|
|
21
|
+
arr.push(newArr);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (obj.suites.length > 0) {
|
|
25
|
+
obj.suites.forEach((suite) => {
|
|
26
|
+
collectTags(tagsArr, suite.tags, suite.requiredTags);
|
|
27
|
+
let newArr = [...continuedArr, suite.name];
|
|
28
|
+
iterateObject(suite, arr, newArr, tagsArr);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Used to remove a process argument before executing Cypress run
|
|
34
|
+
function findAndRemoveArgv(arg) {
|
|
35
|
+
const argToRemove = arg;
|
|
36
|
+
const index = process.argv.indexOf(argToRemove);
|
|
37
|
+
if (index > -1) {
|
|
38
|
+
process.argv.splice(index, 1); // Remove the argument
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Collect tags and requiredTags within suites and tests
|
|
43
|
+
function collectTags(arr, tags, requiredTags) {
|
|
44
|
+
if (tags) {
|
|
45
|
+
if (Array.isArray(tags)) {
|
|
46
|
+
tags.forEach((tag) => {
|
|
47
|
+
// filter out duplicates
|
|
48
|
+
arr.indexOf(tag) === -1 ? arr.push(tag) : null;
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
arr.indexOf(tags) === -1 ? arr.push(tags) : null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (requiredTags) {
|
|
56
|
+
if (Array.isArray(requiredTags)) {
|
|
57
|
+
requiredTags.forEach((requiredTag) => {
|
|
58
|
+
arr.indexOf(requiredTag) === -1 ? arr.push(requiredTag) : null;
|
|
59
|
+
});
|
|
60
|
+
} else {
|
|
61
|
+
arr.indexOf(requiredTags) === -1 ? arr.push(requiredTags) : null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Retrieves Cypress spec files using getSpecs
|
|
67
|
+
// For each spec file, use getTestNames to walk the test array of objects
|
|
68
|
+
const suitesTests = (justTags) => {
|
|
69
|
+
const specs = getSpecs(undefined, process.env.TESTING_TYPE, false);
|
|
70
|
+
|
|
71
|
+
let arr = [];
|
|
72
|
+
let tagArr = [];
|
|
73
|
+
|
|
74
|
+
specs.forEach((element) => {
|
|
75
|
+
const source = fs.readFileSync(element, "utf8");
|
|
76
|
+
const tests = getTestNames(source, true);
|
|
77
|
+
const specFile = path.basename(element);
|
|
78
|
+
|
|
79
|
+
tests.structure.forEach((struct) => {
|
|
80
|
+
const baseDepthArr = [`${specFile}`, `${struct.name}`];
|
|
81
|
+
if (
|
|
82
|
+
struct.type === "test" &&
|
|
83
|
+
struct.pending === false &&
|
|
84
|
+
!struct.exclusive
|
|
85
|
+
) {
|
|
86
|
+
arr.push(baseDepthArr);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
collectTags(tagArr, struct.tags, struct.requiredTags);
|
|
90
|
+
// only walk nested structure for suites
|
|
91
|
+
if (struct.type != "test") {
|
|
92
|
+
iterateObject(struct, arr, baseDepthArr, tagArr);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (justTags) {
|
|
98
|
+
return tagArr;
|
|
99
|
+
} else {
|
|
100
|
+
return arr;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
async function runSelectedSpecs() {
|
|
105
|
+
// allow user to pass --config-file and set as CYPRESS_CONFIG_FILE env variable
|
|
106
|
+
// this is used by find-cypress-specs package to know which config to read specPattern from
|
|
107
|
+
if (process.argv.includes("--config-file")) {
|
|
108
|
+
const index = process.argv.indexOf("--config-file");
|
|
109
|
+
process.env.CYPRESS_CONFIG_FILE = process.argv[index + 1];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// allows the user to simply hit Enter key to submit prompt when no choice has been selected
|
|
113
|
+
if (process.argv.includes("--submit-focused")) {
|
|
114
|
+
findAndRemoveArgv("--submit-focused");
|
|
115
|
+
process.env.SUBMIT_FOCUSED = true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// set the testing type
|
|
119
|
+
// this is used by find-cypress-specs package to get the appropriate spec list
|
|
120
|
+
if (process.argv.includes("--component")) {
|
|
121
|
+
process.env.TESTING_TYPE = "component";
|
|
122
|
+
} else {
|
|
123
|
+
process.env.TESTING_TYPE = "e2e";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
// help menu options
|
|
128
|
+
yarg
|
|
129
|
+
.completion("--print-selected", false)
|
|
130
|
+
.option("print-selected", {
|
|
131
|
+
desc: "Prints selected specs, tests or tags",
|
|
132
|
+
type: "boolean",
|
|
133
|
+
})
|
|
134
|
+
.example("npx cypress-cli-select run --print-selected");
|
|
135
|
+
|
|
136
|
+
yarg
|
|
137
|
+
.completion("--choose-spec-pattern", false)
|
|
138
|
+
.option("choose-spec-pattern", {
|
|
139
|
+
desc: "Uses specPattern to enable spec ordering",
|
|
140
|
+
type: "boolean",
|
|
141
|
+
})
|
|
142
|
+
.example("npx cypress-cli-select run --component --choose-spec-pattern");
|
|
143
|
+
|
|
144
|
+
yarg
|
|
145
|
+
.completion("--submit-focused", false)
|
|
146
|
+
.option("submit-focused", {
|
|
147
|
+
desc: "Selects and submits focused item using enter",
|
|
148
|
+
type: "boolean",
|
|
149
|
+
})
|
|
150
|
+
.example("npx cypress-cli-select run --submit-focused");
|
|
151
|
+
|
|
152
|
+
yarg
|
|
153
|
+
.scriptName("npx cypress-cli-select run")
|
|
154
|
+
.usage(
|
|
155
|
+
"\nInteractive cli prompts to select Cypress specs, tests or tags run\n",
|
|
156
|
+
)
|
|
157
|
+
.usage("$0 [args]")
|
|
158
|
+
.example("npx cypress-cli-select run --browser=firefox")
|
|
159
|
+
.help().argv;
|
|
160
|
+
|
|
161
|
+
// Cypress-cli-select title
|
|
162
|
+
console.log("\n");
|
|
163
|
+
console.log(pc.bgGreen(pc.black(pc.bold(` Cypress-cli-select `))));
|
|
164
|
+
console.log("\n");
|
|
165
|
+
|
|
166
|
+
// NOTE:: if --choose-spec-pattern then disable test title and tag prompt
|
|
167
|
+
let disableTitleTagChoice = false;
|
|
168
|
+
if (process.argv.includes("--choose-spec-pattern")) {
|
|
169
|
+
disableTitleTagChoice = true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/*
|
|
173
|
+
* NOTE:: Choose specs, test titles/tags or both
|
|
174
|
+
* Test titles/tags requires the cy-grep package
|
|
175
|
+
*/
|
|
176
|
+
// Prompt for use to select spec and test titles or tags option
|
|
177
|
+
const specAndTestPrompt = await select({
|
|
178
|
+
message: "Choose to filter by specs, specific test titles or tags: ",
|
|
179
|
+
multiple: disableTitleTagChoice ? false : true,
|
|
180
|
+
defaultValue: disableTitleTagChoice ? "Specs" : null,
|
|
181
|
+
clearInputWhenSelected: true,
|
|
182
|
+
selectFocusedOnSubmit: process.env.SUBMIT_FOCUSED,
|
|
183
|
+
canToggleAll: true,
|
|
184
|
+
options: [
|
|
185
|
+
{
|
|
186
|
+
name: "Specs",
|
|
187
|
+
value: "Specs",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "Test titles or tags (requires cy-grep)",
|
|
191
|
+
value: "Tests or tags",
|
|
192
|
+
disabled: disableTitleTagChoice,
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
required: true,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
/*
|
|
199
|
+
|
|
200
|
+
/*
|
|
201
|
+
* NOTE:: Choose test titles or tags
|
|
202
|
+
* This requires the cy-grep package
|
|
203
|
+
*/
|
|
204
|
+
if (specAndTestPrompt.includes("Tests or tags")) {
|
|
205
|
+
// Prompt for use to select test titles or tags option
|
|
206
|
+
const titleOrTagPrompt = await select({
|
|
207
|
+
message: "Choose to filter by specific test titles or tags: ",
|
|
208
|
+
multiple: false,
|
|
209
|
+
options: [
|
|
210
|
+
{
|
|
211
|
+
name: "Test titles",
|
|
212
|
+
value: "Titles",
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
name: "Test tags",
|
|
216
|
+
value: "Tags",
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
required: true,
|
|
220
|
+
});
|
|
221
|
+
process.env.CY_GREP_FILTER_METHOD = titleOrTagPrompt;
|
|
222
|
+
}
|
|
223
|
+
// Arrays for storing specs and/or tests
|
|
224
|
+
// If user passes --print-selected
|
|
225
|
+
const specArr = [];
|
|
226
|
+
const testArr = [];
|
|
227
|
+
|
|
228
|
+
/*
|
|
229
|
+
* NOTE:: Spec section
|
|
230
|
+
*/
|
|
231
|
+
if (specAndTestPrompt.includes("Specs")) {
|
|
232
|
+
const specs = getSpecs(undefined, process.env.TESTING_TYPE, false);
|
|
233
|
+
|
|
234
|
+
if (specs.length > 0) {
|
|
235
|
+
function specsChoices() {
|
|
236
|
+
let arr = [];
|
|
237
|
+
specs.forEach((element) => {
|
|
238
|
+
const spec = {
|
|
239
|
+
name: element,
|
|
240
|
+
value: element,
|
|
241
|
+
};
|
|
242
|
+
arr.push(spec);
|
|
243
|
+
});
|
|
244
|
+
return arr;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const specSelections = await select({
|
|
248
|
+
message: "Select specs to run:",
|
|
249
|
+
multiple: true,
|
|
250
|
+
required: true,
|
|
251
|
+
clearInputWhenSelected: true,
|
|
252
|
+
selectFocusedOnSubmit: process.env.SUBMIT_FOCUSED,
|
|
253
|
+
canToggleAll: true,
|
|
254
|
+
options: (input = "") => {
|
|
255
|
+
const specs = specsChoices();
|
|
256
|
+
|
|
257
|
+
const fuse = new Fuse(specs, {
|
|
258
|
+
keys: ["value"],
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
if (!input) return specs;
|
|
262
|
+
if (fuse) {
|
|
263
|
+
const result = fuse.search(input).map(({ item }) => item);
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
return [];
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// If user passes --print-selected, print the specs selected
|
|
271
|
+
specSelections.forEach((spec) => {
|
|
272
|
+
specArr.push(`${spec}`);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
/*
|
|
276
|
+
* NOTE:: specPattern section for reordering specs
|
|
277
|
+
* requires --choose-spec-pattern flag be passed
|
|
278
|
+
*/
|
|
279
|
+
if (process.argv.includes("--choose-spec-pattern")) {
|
|
280
|
+
findAndRemoveArgv("--choose-spec-pattern");
|
|
281
|
+
const sortedSpecResult = await sortingList({
|
|
282
|
+
message: "Reorder the specs in desired run order:",
|
|
283
|
+
choices: specSelections,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// If user passes --print-selected, empty array storing specs and print re-ordered
|
|
287
|
+
specArr.length = 0;
|
|
288
|
+
|
|
289
|
+
sortedSpecResult.forEach((spec) => {
|
|
290
|
+
specArr.push(`${spec}`);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
if (process.env.CY_GREP_FILTER_METHOD) {
|
|
294
|
+
// For more info on grepExtraSpecs: https://github.com/bahmutov/cy-grep?tab=readme-ov-file#grepextraspecs
|
|
295
|
+
process.env.CYPRESS_grepExtraSpecs = sortedSpecResult.toString();
|
|
296
|
+
} else {
|
|
297
|
+
// Translate to specPattern string format to be used for Cypress run command line
|
|
298
|
+
function specString() {
|
|
299
|
+
let stringedSpecs = "";
|
|
300
|
+
sortedSpecResult.forEach((spec) => {
|
|
301
|
+
stringedSpecs += `"${spec}", `;
|
|
302
|
+
});
|
|
303
|
+
return stringedSpecs.slice(0, -2);
|
|
304
|
+
}
|
|
305
|
+
if (process.env.TESTING_TYPE === "e2e") {
|
|
306
|
+
process.argv.push("--config");
|
|
307
|
+
process.argv.push(`specPattern=[${specString()}]`);
|
|
308
|
+
} else {
|
|
309
|
+
// TODO: Current component test specPattern workaround
|
|
310
|
+
// See: https://github.com/cypress-io/cypress/issues/29317
|
|
311
|
+
process.argv.push("--config");
|
|
312
|
+
process.argv.push(
|
|
313
|
+
`{ "component": { "specPattern": [${specString()}] }}`,
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
if (process.env.CY_GREP_FILTER_METHOD) {
|
|
319
|
+
process.env.CYPRESS_grepExtraSpecs = specSelections.toString();
|
|
320
|
+
} else {
|
|
321
|
+
process.argv.push(`--spec`);
|
|
322
|
+
process.argv.push(`${specSelections.join().toString()}`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
console.log("\n");
|
|
327
|
+
console.log(
|
|
328
|
+
pc.redBright(pc.bold(` No ${process.env.TESTING_TYPE} specs found `)),
|
|
329
|
+
);
|
|
330
|
+
process.exit();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/*
|
|
335
|
+
* NOTE:: Test Title section
|
|
336
|
+
*/
|
|
337
|
+
if (process.env.CY_GREP_FILTER_METHOD === "Titles") {
|
|
338
|
+
const specs = getSpecs(undefined, process.env.TESTING_TYPE, false);
|
|
339
|
+
|
|
340
|
+
if (specs.length > 0) {
|
|
341
|
+
const testChoices = () => {
|
|
342
|
+
const tests = suitesTests(false);
|
|
343
|
+
let arr = [];
|
|
344
|
+
tests.forEach((element) => {
|
|
345
|
+
if (element.length === 2) {
|
|
346
|
+
const spec = {
|
|
347
|
+
spec: element.shift(),
|
|
348
|
+
test: element.pop(),
|
|
349
|
+
};
|
|
350
|
+
arr.push(spec);
|
|
351
|
+
} else {
|
|
352
|
+
const spec = {
|
|
353
|
+
spec: element.shift(),
|
|
354
|
+
parent: element,
|
|
355
|
+
test: element.pop(),
|
|
356
|
+
};
|
|
357
|
+
arr.push(spec);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
return arr;
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
// Sets up the test select prompt formatted spec > parent(s) > test
|
|
364
|
+
// All selected tests will then remove the ">" separator for use in @bahmutov/cy-grep to run test titles
|
|
365
|
+
const separateStringJson = () => {
|
|
366
|
+
let arr = [];
|
|
367
|
+
const specs = testChoices();
|
|
368
|
+
specs.forEach((element) => {
|
|
369
|
+
const { spec, ...specLess } = element;
|
|
370
|
+
const choices = {
|
|
371
|
+
name: Object.values(element).flat().join(" > "),
|
|
372
|
+
value: Object.values(specLess).flat().join(" "),
|
|
373
|
+
};
|
|
374
|
+
arr.push(choices);
|
|
375
|
+
});
|
|
376
|
+
return arr;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const selectedTests = await select({
|
|
380
|
+
message: "Select tests to run:",
|
|
381
|
+
multiple: true,
|
|
382
|
+
clearInputWhenSelected: true,
|
|
383
|
+
selectFocusedOnSubmit: process.env.SUBMIT_FOCUSED,
|
|
384
|
+
required: true,
|
|
385
|
+
options: (input = "") => {
|
|
386
|
+
const tests = separateStringJson();
|
|
387
|
+
|
|
388
|
+
const fuse = new Fuse(tests, {
|
|
389
|
+
keys: ["name"],
|
|
390
|
+
ignoreLocation: true,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
if (!input) return tests;
|
|
394
|
+
if (fuse) {
|
|
395
|
+
const result = fuse.search(input).map(({ item }) => item);
|
|
396
|
+
return result;
|
|
397
|
+
}
|
|
398
|
+
return [];
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// for --print-selected
|
|
403
|
+
// to couple selected choice back to all tests in json format
|
|
404
|
+
const originalTests = testChoices();
|
|
405
|
+
originalTests.forEach((originalTest) => {
|
|
406
|
+
const { spec, ...specLess } = originalTest;
|
|
407
|
+
const value = Object.values(specLess).flat().join(" ");
|
|
408
|
+
selectedTests.forEach((test) => {
|
|
409
|
+
if (test === value) {
|
|
410
|
+
testArr.push(originalTest);
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// find any tests that aren't selected but include selected
|
|
416
|
+
// filter to an array where we can invert grep to not run
|
|
417
|
+
const allTests = separateStringJson();
|
|
418
|
+
const testsToInvert = [];
|
|
419
|
+
selectedTests.forEach((test) => {
|
|
420
|
+
const testStr = test;
|
|
421
|
+
testsToInvert.push(
|
|
422
|
+
allTests
|
|
423
|
+
.filter(
|
|
424
|
+
(test) =>
|
|
425
|
+
test.value.includes(testStr) && test.value !== testStr,
|
|
426
|
+
)
|
|
427
|
+
.map((t) => t.value),
|
|
428
|
+
);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// Format the tests selected into a string separated by colon
|
|
432
|
+
// This is the test title grep string format used by @bahmutov/cy-grep package
|
|
433
|
+
function formatGrepString() {
|
|
434
|
+
let stringedTests = "";
|
|
435
|
+
selectedTests.forEach((test) => {
|
|
436
|
+
// if a checked test title begins with the grep inverted '-' symbol, remove the '-'
|
|
437
|
+
if (test[0] === "-") {
|
|
438
|
+
stringedTests += `${test.slice(1)}; `;
|
|
439
|
+
} else {
|
|
440
|
+
stringedTests += `${test}; `;
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
// if a non-checked test's title includes a checked test's title, invert grep for unchecked title
|
|
444
|
+
testsToInvert.flat().forEach((test) => {
|
|
445
|
+
stringedTests += `-${test}; `;
|
|
446
|
+
});
|
|
447
|
+
return stringedTests.slice(0, -2);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const stringedTests = formatGrepString();
|
|
451
|
+
|
|
452
|
+
// Set the process.env for @bahmutov/cy-grep package using CYPRESS_*
|
|
453
|
+
process.env.CYPRESS_grep = `${stringedTests}`;
|
|
454
|
+
} else {
|
|
455
|
+
console.log("\n");
|
|
456
|
+
console.log(
|
|
457
|
+
pc.redBright(pc.bold(` No ${process.env.TESTING_TYPE} specs found `)),
|
|
458
|
+
);
|
|
459
|
+
process.exit();
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/*
|
|
464
|
+
* NOTE:: Tags section
|
|
465
|
+
*/
|
|
466
|
+
if (process.env.CY_GREP_FILTER_METHOD === "Tags") {
|
|
467
|
+
const specs = getSpecs(undefined, process.env.TESTING_TYPE, false);
|
|
468
|
+
|
|
469
|
+
if (specs.length > 0) {
|
|
470
|
+
const allTags = suitesTests(true);
|
|
471
|
+
// sort the tags presented
|
|
472
|
+
allTags.sort();
|
|
473
|
+
|
|
474
|
+
if (allTags.length > 0) {
|
|
475
|
+
const separateStringJson = () => {
|
|
476
|
+
let arr = [];
|
|
477
|
+
allTags.forEach((element) => {
|
|
478
|
+
const choices = {
|
|
479
|
+
name: element,
|
|
480
|
+
value: element,
|
|
481
|
+
};
|
|
482
|
+
arr.push(choices);
|
|
483
|
+
});
|
|
484
|
+
return arr;
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
const selectedTags = await select({
|
|
488
|
+
message: "Select tags to run:",
|
|
489
|
+
multiple: true,
|
|
490
|
+
clearInputWhenSelected: true,
|
|
491
|
+
selectFocusedOnSubmit: process.env.SUBMIT_FOCUSED,
|
|
492
|
+
required: true,
|
|
493
|
+
options: (input = "") => {
|
|
494
|
+
const tags = separateStringJson();
|
|
495
|
+
|
|
496
|
+
const fuse = new Fuse(tags, {
|
|
497
|
+
keys: ["name"],
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
if (!input) return tags;
|
|
501
|
+
if (fuse) {
|
|
502
|
+
const result = fuse.search(input).map(({ item }) => item);
|
|
503
|
+
return result;
|
|
504
|
+
}
|
|
505
|
+
return [];
|
|
506
|
+
},
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
process.env.SELECTED_TAGS = selectedTags;
|
|
510
|
+
|
|
511
|
+
// This is the tag grep string format used by @bahmutov/cy-grep package
|
|
512
|
+
// Set the process.env for @bahmutov/cy-grep package using CYPRESS_*
|
|
513
|
+
process.env.CYPRESS_grepTags = `${process.env.SELECTED_TAGS}`;
|
|
514
|
+
} else {
|
|
515
|
+
console.log("\n");
|
|
516
|
+
console.log(
|
|
517
|
+
pc.redBright(
|
|
518
|
+
pc.bold(` No ${process.env.TESTING_TYPE} tags found `),
|
|
519
|
+
),
|
|
520
|
+
);
|
|
521
|
+
process.exit();
|
|
522
|
+
}
|
|
523
|
+
} else {
|
|
524
|
+
console.log("\n");
|
|
525
|
+
console.log(
|
|
526
|
+
pc.redBright(pc.bold(` No ${process.env.TESTING_TYPE} specs found `)),
|
|
527
|
+
);
|
|
528
|
+
process.exit();
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// NOTE : --print-selected used to show all selected specs/titles/tags
|
|
533
|
+
if (process.argv.includes("--print-selected")) {
|
|
534
|
+
findAndRemoveArgv("--print-selected");
|
|
535
|
+
if (specAndTestPrompt.includes("Specs")) {
|
|
536
|
+
console.log("\n");
|
|
537
|
+
console.log(pc.bgGreen(pc.black(pc.bold(` Spec(s) selected: `))));
|
|
538
|
+
console.log("\n");
|
|
539
|
+
console.log(specArr);
|
|
540
|
+
}
|
|
541
|
+
if (process.env.CY_GREP_FILTER_METHOD === "Titles") {
|
|
542
|
+
console.log("\n");
|
|
543
|
+
console.log(pc.bgGreen(pc.black(pc.bold(` Test(s) selected: `))));
|
|
544
|
+
console.log("\n");
|
|
545
|
+
console.log(testArr);
|
|
546
|
+
}
|
|
547
|
+
if (process.env.CY_GREP_FILTER_METHOD === "Tags") {
|
|
548
|
+
console.log("\n");
|
|
549
|
+
console.log(pc.bgGreen(pc.black(pc.bold(` Tag(s) selected: `))));
|
|
550
|
+
console.log("\n");
|
|
551
|
+
console.log(process.env.SELECTED_TAGS);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// both env variables are from cy-grep package
|
|
556
|
+
// used to omit tests and specs that are filtered
|
|
557
|
+
if (process.env.CY_GREP_FILTER_METHOD) {
|
|
558
|
+
process.env.CYPRESS_grepFilterSpecs = true;
|
|
559
|
+
process.env.CYPRESS_grepOmitFiltered = true;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// In case the user passes this option without selecting specs
|
|
563
|
+
if (process.argv.includes("--choose-spec-pattern")) {
|
|
564
|
+
findAndRemoveArgv("--choose-spec-pattern");
|
|
565
|
+
}
|
|
566
|
+
} catch (e) {
|
|
567
|
+
// if user closes prompt send a error message instead of inquirer.js error
|
|
568
|
+
if (e.message.includes("User force closed the prompt")) {
|
|
569
|
+
console.log("\n");
|
|
570
|
+
console.log(pc.redBright(pc.bold("The prompt was closed")));
|
|
571
|
+
process.exit();
|
|
572
|
+
} else {
|
|
573
|
+
console.log(e);
|
|
574
|
+
process.exit();
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
console.log("\n");
|
|
579
|
+
console.log(pc.bgGreen(pc.black(pc.bold(` Running Cypress: `))));
|
|
580
|
+
|
|
581
|
+
// Executing the cypress run
|
|
582
|
+
const runOptions = await cypress.cli.parseRunArguments(process.argv.slice(2));
|
|
583
|
+
await cypress.run(runOptions);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
runSelectedSpecs();
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cypress-cli-select",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A Cypress cli prompt to select and run specs, tests or tags",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"cypress-cli-select": "index.js"
|
|
8
|
+
},
|
|
9
|
+
"devDependencies": {
|
|
10
|
+
"cli-testing-library": "^2.0.2",
|
|
11
|
+
"cypress": "^13.17.0",
|
|
12
|
+
"jest": "^29.7.0"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"cypress",
|
|
16
|
+
"testing",
|
|
17
|
+
"cli",
|
|
18
|
+
"tooling"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"registry": "https://registry.npmjs.org/"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "jest"
|
|
25
|
+
},
|
|
26
|
+
"author": "Dennis Bergevin",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/dennisbergevin/cypress-cli-select.git"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/dennisbergevin/cypress-cli-select/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/dennisbergevin/cypress-cli-select#readme",
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@bahmutov/cy-grep": "^2.0.5",
|
|
38
|
+
"@inquirer/core": "^10.1.2",
|
|
39
|
+
"find-cypress-specs": "^1.47.0",
|
|
40
|
+
"find-test-names": "^1.29.2",
|
|
41
|
+
"fuse.js": "^7.0.0",
|
|
42
|
+
"inquirer-select-pro": "^1.0.0-alpha.9",
|
|
43
|
+
"picocolors": "^1.1.1",
|
|
44
|
+
"yargs": "^17.7.2"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
describe("<Clock>", () => {
|
|
2
|
+
it("mounts", () => {});
|
|
3
|
+
|
|
4
|
+
it("stepper should default to 0", { tags: ["@p3", "@sanity"] }, () => {});
|
|
5
|
+
|
|
6
|
+
it('supports a "count" prop to set the value', () => {});
|
|
7
|
+
|
|
8
|
+
it("when the increment button is pressed, the counter is incremented", () => {});
|
|
9
|
+
|
|
10
|
+
it("when the decrement button is pressed, the counter is decremented", () => {});
|
|
11
|
+
|
|
12
|
+
it("clicking + fires a change event with the incremented value", () => {});
|
|
13
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
describe("<Stepper>", () => {
|
|
2
|
+
it("mounts", { requiredTags: "@nightly" }, () => {});
|
|
3
|
+
|
|
4
|
+
it("clock should do it", () => {});
|
|
5
|
+
|
|
6
|
+
it("clock needs to be set", () => {});
|
|
7
|
+
|
|
8
|
+
it("when the clock is set, life is good", () => {});
|
|
9
|
+
|
|
10
|
+
it("when the clock is decrement", () => {});
|
|
11
|
+
|
|
12
|
+
it("clicking the clock is dangerous", () => {});
|
|
13
|
+
});
|