Imagine you have a lot of Cypress spec files, and you want to refactor some code. You probably want to run the changed specs first to get faster CI feedback, right? Let's say you have a situation like I have in the repo bahmutov/test-todomvc-using-app-actions. You have 10+ spec files, and a few utilities shared across specs.
If you open a pull request with edited spec files, you can find which specs have changed and should run first using Git commands. I have even implemented code in find-cypress-specs utility to make finding changed specs simple in my projects. For example, if we modify the spec file cypress/e2e/fixture-spec.ts
:
1 | import type { Todo } from './model' |
We can run the following command to find this spec
1 | branch: f1 |
What happens when we modify the model.d.ts
file imported by cypress/e2e/fixture-spec.ts
? Several specs import this .d.ts
file, so we should re-run those specs for a pull request that modifies model.d.ts
file.
My goal today is to find these specs affected by the change to the source file model.d.ts
they all import. If I can find them, my pull request workflow can run the affected specs only, or as the first step when verifying the tests are still working correctly.
Trace the changed specs
In the latest find-cypress-specs release 1.21.0 there is a new feature that uses spec-change to trace all import
and require
statements in the source files to build a graph of file dependencies (excluding 3rd party code from node_modules
). From the graph, it can find the spec files affected by the model.d.ts
file change, and you can set the GitHub Actions workflow to run them. Here is a command to trace the source file dependencies in the cypress
folder.
1 | branch: f1 |
The tool find four specs affected by the change. If we are using GitHub Actions, we can test the affected spec files. Here is the pull request workflow we can use.
1 | name: pr changed tests |
The option --set-gha-outputs
uses GitHub Actions SDK library to save the found spec files and their number as the step's outputs, allowing us to decide if we need to run the end-to-end tests steps next.
1 | - name: Run changed Cypress specs 🏎 |
Tracing the changed specs in action
I have opened the pull request #334 and the PR workflow has finished successfully.
Let's drill into the workflow to see what specs it traced and executed. Remember: we only modified 3 different files, without touching any of the Cypress specs.
Yet, the trace detected that the changes do affect 4 spec files.
The Cypress GitHub action then executes only those affected four specs.
Finding the affected spec files and running them first on pull request lets you dramatically cut down on the time it takes to verify changes to the testing code, especially if you run run A LOT of Cypress tests.
Update 1: limit the added spec files
If you change a common utils.js
file that many specs import, you might end up with a giant pull request with hundreds of specs. You can prevent it by using the CLI parameter --max-added-traced-specs <N>
, for example to add at most 10 extra specs by tracing:
1 | npx find-cypress-specs --branch main --parent --trace-imports cypress --max-added-traced-specs 10 |
Update 2: speed up tracing using cached dependencies file
If you have a lot of source files, finding dependencies takes time. You can see how long it takes by adding --time-trace
flag.
1 | $ npx find-cypress-specs --branch main --parent --trace-imports cypress --time-trace |
You can save the found dependencies into a JSON file deps.json
and then reuse it
1 | # get the number of affected specs |
Since the E2E test specs don't change too often, you can compute the dependencies once a day and commit to you source repository. Here is an example GitHub Actions workflow from bahmutov/test-todomvc-using-app-actions.
1 | # this workflow computes the dependencies between Cypress spec |
The commands to recompute the traced dependencies and use them are in package.json
file
1 | { |
Make sure to commit the deps.json
file into the repo, and the above workflow will keep it updated every night. Even more realistic is to use your personal GitHub token to be able to force push to the protected main
branch and to trigger the workflow on schedule AND manually. The above workflow would look like this:
1 | name: spec dependencies |