cypress-plugin-last-failed 1.2.0 → 2.0.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/README.md CHANGED
@@ -18,14 +18,16 @@ A companion Cypress plugin for <code>cy-grep</code> that re-runs the last failed
18
18
 
19
19
  #### Table of Contents
20
20
 
21
- - [Installation](#-installation)
22
- - [Run mode](#-run-mode)
21
+ - [Features](#features)
22
+ - [Table of Contents](#table-of-contents)
23
+ - [📦 Installation](#-installation)
24
+ - [👟 Run mode](#-run-mode)
23
25
  - [Add rule to gitignore](#add-rule-to-gitignore)
24
- - [Setting up a `npm` script](#-setting-up-a-npm-script)
25
- - [Open mode](#-open-mode)
26
+ - [📃 Setting up a `npm` script](#-setting-up-a-npm-script)
27
+ - [Open mode](#-open-mode)
26
28
  - [Recommended open mode env variables](#recommended-open-mode-env-variables)
27
29
  - [Use Required Test Tags Instead Of Skipping Tests](#use-required-test-tags-instead-of-skipping-tests)
28
- - [CI support](#continuous-integration-support)
30
+ - [Continuous integration support](#continuous-integration-support)
29
31
  - [Typescript support](#typescript-support)
30
32
  - [Contributions](#contributions)
31
33
 
@@ -43,9 +45,9 @@ npm install --save-dev cypress-plugin-last-failed
43
45
  2. In `cypress/support/e2e.js` (For E2E tests) and/or `cypress/support/component.js` (For Component tests),
44
46
 
45
47
  ```js
46
- import { failedTestToggle } from 'cypress-plugin-last-failed';
48
+ import { failedTestToggle } from "cypress-plugin-last-failed";
47
49
 
48
- const registerCypressGrep = require('@bahmutov/cy-grep');
50
+ const registerCypressGrep = require("@bahmutov/cy-grep");
49
51
  registerCypressGrep();
50
52
 
51
53
  failedTestToggle();
@@ -54,8 +56,8 @@ failedTestToggle();
54
56
  3. In `cypress.config`, include the following within `setupNodeEvents` for `e2e` and/or `component` testing:
55
57
 
56
58
  ```js
57
- const { defineConfig } = require('cypress');
58
- const { collectFailingTests } = require('cypress-plugin-last-failed');
59
+ const { defineConfig } = require("cypress");
60
+ const { collectFailingTests } = require("cypress-plugin-last-failed");
59
61
 
60
62
  module.exports = defineConfig({
61
63
  screenshotOnRunFailure: false,
@@ -67,7 +69,7 @@ module.exports = defineConfig({
67
69
  setupNodeEvents(on, config) {
68
70
  collectFailingTests(on, config);
69
71
 
70
- require('@bahmutov/cy-grep/src/plugin')(config);
72
+ require("@bahmutov/cy-grep/src/plugin")(config);
71
73
  return config;
72
74
  },
73
75
  },
@@ -75,7 +77,7 @@ module.exports = defineConfig({
75
77
  setupNodeEvents(on, config) {
76
78
  collectFailingTests(on, config);
77
79
 
78
- require('@bahmutov/cy-grep/src/plugin')(config);
80
+ require("@bahmutov/cy-grep/src/plugin")(config);
79
81
  return config;
80
82
  },
81
83
  },
@@ -106,7 +108,14 @@ You can also include more cli arguments similar to `cypress run`, as the command
106
108
  npx cypress-plugin-last-failed run --e2e --browser chrome
107
109
  ```
108
110
 
109
- There will be a folder called `test-results` created in the directory of the `cypress.config`.
111
+ There will be a folder called test-results created in the directory of the cypress.config. If you would like to specify a different folder for test results, use the LAST_FAILED_RESULTS_PATH environment variable:
112
+
113
+ ```bash
114
+ # Example
115
+ LAST_FAILED_RESULTS_PATH=cypress/custom-test-results npx cypress-plugin-last-failed run
116
+ ```
117
+
118
+ This environment variable will direct the plugin to store or retrieve the last run's failed test results from the specified folder.
110
119
 
111
120
  ### Add rule to gitignore
112
121
 
@@ -162,7 +171,7 @@ Normally, any Cypress test or suite of tests marked with a `.skip` will be shown
162
171
  Since this plugin uses `@bahmutov/cy-grep` plugin, we can instead designate skipped tests using a **required tag**:
163
172
 
164
173
  ```js
165
- it('deletes an item', { requiredTags: '@skip' }, () => {
174
+ it("deletes an item", { requiredTags: "@skip" }, () => {
166
175
  expect(1).to.equal(2);
167
176
  });
168
177
  ```
@@ -186,7 +195,7 @@ name: test-last-failed-node-script
186
195
  on:
187
196
  push:
188
197
  branches:
189
- - 'main'
198
+ - "main"
190
199
  pull_request:
191
200
  workflow_dispatch:
192
201
 
@@ -0,0 +1,26 @@
1
+ const { defineConfig } = require("cypress");
2
+ const { collectFailingTests } = require("../../src/index");
3
+
4
+ module.exports = defineConfig({
5
+ env: {
6
+ grepOmitFiltered: true,
7
+ grepFilterSpecs: true,
8
+ },
9
+ screenshotOnRunFailure: false,
10
+ e2e: {
11
+ setupNodeEvents(on, config) {
12
+ require("@bahmutov/cy-grep/src/plugin")(config);
13
+ collectFailingTests(on, config);
14
+
15
+ return config;
16
+ },
17
+ },
18
+ component: {
19
+ setupNodeEvents(on, config) {
20
+ require("@bahmutov/cy-grep/src/plugin")(config);
21
+ collectFailingTests(on, config);
22
+
23
+ return config;
24
+ },
25
+ },
26
+ });
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "cypress-plugin-last-failed",
3
- "version": "1.2.0",
3
+ "version": "2.0.1",
4
4
  "description": "Cypress plugin to rerun last failed tests in cypress run and open mode",
5
5
  "main": "./src/index.js",
6
+ "types": "./src/index.d.ts",
6
7
  "scripts": {
7
8
  "last-failed": "npx cypress-plugin-last-failed run --e2e --browser electron"
8
9
  },
package/runFailed.js CHANGED
@@ -1,20 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const cypress = require('cypress');
4
- const fs = require('fs');
5
- const path = require('path');
6
- const appDir = process.env.INIT_CWD ? process.env.INIT_CWD : path.resolve('.');
3
+ const cypress = require("cypress");
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const appDir = process.env.INIT_CWD ? process.env.INIT_CWD : path.resolve(".");
7
7
 
8
8
  async function runLastFailed() {
9
9
  const noFailedTestsMessage = `No previous failed tests detected
10
10
  Ensure you are in the directory of your cypress config
11
11
  Try running tests again with cypress run`;
12
12
 
13
- const failedTestFilePath = `${appDir}/test-results/last-run.json`;
13
+ const failedTestFilePath = process.env.LAST_FAILED_RESULTS_PATH
14
+ ? path.join(
15
+ appDir,
16
+ process.env.LAST_FAILED_RESULTS_PATH,
17
+ "test-results",
18
+ "last-run.json"
19
+ )
20
+ : path.join(appDir, "test-results", "last-run.json");
14
21
 
15
22
  if (fs.existsSync(failedTestFilePath)) {
16
23
  // Retrieve the failedTests from the file
17
- const failedTests = await fs.promises.readFile(failedTestFilePath, 'utf8');
24
+ const failedTests = await fs.promises.readFile(failedTestFilePath, "utf8");
18
25
 
19
26
  // Retrieve the parent suite and tests in the results from test-results/last-run
20
27
  const parentAndTest = JSON.parse(failedTests).map(({ parent, test }) => ({
@@ -24,13 +31,13 @@ Try running tests again with cypress run`;
24
31
  // Combine parent suite and test together
25
32
  const resultSet = new Set(
26
33
  Object.values(parentAndTest).flatMap(
27
- (parent) => parent.parent + ',' + parent.test + ';'
34
+ (parent) => parent.parent + "," + parent.test + ";"
28
35
  )
29
36
  );
30
37
  // Format string for use in grep functionality
31
38
  const stringedTests = Array.from(resultSet)
32
39
  .toString()
33
- .replaceAll(',', ' ')
40
+ .replaceAll(",", " ")
34
41
  .slice(0, -1);
35
42
 
36
43
  if (stringedTests.length > 0) {
package/src/index.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ // index.d.ts
2
+ declare module "cypress-plugin-last-failed" {
3
+ /**
4
+ * Collects failed tests from the most recent Cypress test run.
5
+ *
6
+ * After each run, a file will store failed test titles within a test-results directory.
7
+ *
8
+ * @param {Function} on - Cypress event registration function.
9
+ * @param {object} config - Cypress config object.
10
+ * @returns {void}
11
+ */
12
+ export function collectFailingTests(
13
+ on: (event: string, callback: Function) => void,
14
+ config: { env: { TEST_RESULTS_PATH?: string }; configFile: string }
15
+ ): void;
16
+
17
+ /**
18
+ * Toggles the display of failed tests in the Cypress UI.
19
+ *
20
+ * @returns {void}
21
+ */
22
+ export function failedTestToggle(): void;
23
+ }
package/src/index.js CHANGED
@@ -1,60 +1,68 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const failedTestToggle = require('./toggle');
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const failedTestToggle = require("./toggle");
4
+
4
5
  /**
5
6
  * Collects failed tests from the most recent Cypress test run
6
7
  *
7
8
  * After each run, a file will store failed test titles within a test-results directory
8
9
  *
9
- * The test-results directory will be stored in cypress.config directory
10
+ * The test-results directory will be stored in the cypress.config directory
10
11
  *
11
12
  * Subsequent test runs containing failed tests will overwrite this file
12
13
  * @param {*} on
13
14
  * @param {*} config
14
15
  * @returns
15
16
  */
16
-
17
17
  const collectFailingTests = (on, config) => {
18
- on('after:run', async (results) => {
18
+ on("after:run", async (results) => {
19
19
  let failedTests = [];
20
- // Grab every failed test's title
21
- for (i in results.runs) {
22
- const tests = results.runs[i].tests
23
- .filter((test) => test.state === 'failed')
24
- .map((test) => test.title);
25
-
26
- const spec = results.runs[i].spec.relative;
27
-
28
- for (i in tests) {
29
- let report = {
30
- spec: spec,
31
- parent: [...tests[i].slice(0, -1)],
32
- test: tests[i].pop(),
33
- };
34
- // Only store non empty test titles
35
- if (tests != '') {
36
- failedTests.push(report);
37
- }
20
+
21
+ for (const run of results.runs) {
22
+ if (run.tests && run.spec) {
23
+ const tests = run.tests
24
+ .filter((test) => test.state === "failed")
25
+ .map((test) => test.title);
26
+
27
+ const spec = run.spec.relative;
28
+
29
+ failedTests = failedTests.concat(generateReports(tests, spec));
38
30
  }
39
31
  }
40
32
 
41
- // Use the cypress.config directory for path for storing test-results
42
- const failedTestFileDirectory = `${path.dirname(
43
- config.configFile
44
- )}/test-results/`;
33
+ // Use process.env.LAST_FAILED_RESULTS_PATH or fallback to the default path
34
+ const failedTestFilePath = process.env.LAST_FAILED_RESULTS_PATH
35
+ ? path.join(
36
+ process.env.INIT_CWD,
37
+ process.env.LAST_FAILED_RESULTS_PATH,
38
+ "test-results"
39
+ )
40
+ : `${path.dirname(config.configFile)}/test-results/`;
45
41
 
46
42
  // Create the directory and last-run file where failed tests will be written to
47
- await fs.promises.mkdir(`${failedTestFileDirectory}`, {
48
- recursive: true,
49
- });
50
- const lastRunReportFile = path.join(
51
- `${failedTestFileDirectory}`,
52
- 'last-run.json'
53
- );
43
+ await fs.promises.mkdir(failedTestFilePath, { recursive: true });
44
+ const lastRunReportFile = path.join(failedTestFilePath, "last-run.json");
54
45
  await fs.promises.writeFile(lastRunReportFile, JSON.stringify(failedTests));
55
46
  });
56
47
 
57
48
  return collectFailingTests;
58
49
  };
59
50
 
51
+ const generateReports = (tests, spec) => {
52
+ const reports = [];
53
+ for (const test of tests) {
54
+ const testCopy = [...test]; // Create a copy of the array to avoid mutation
55
+ const testTitle = testCopy.pop(); // Extract the test title
56
+ if (testTitle) {
57
+ const report = {
58
+ spec: spec,
59
+ parent: testCopy, // Parent titles
60
+ test: testTitle, // The actual test title
61
+ };
62
+ reports.push(report);
63
+ }
64
+ }
65
+ return reports;
66
+ };
67
+
60
68
  module.exports = { collectFailingTests, failedTestToggle };