Take Control of Your Cypress E2E Execution Order (and Keep Your CI Clean) using bash
As an SDET, we often rely on the standard npx cypress run command. It works great for running everything at once, but in the real world, things are rarely that simple.
Sometimes you need to run tests in a specific sequence. Other times, you need to temporarily exclude a test file because of a known bug in the application (not the test code itself), and you don't want to modify the source code with .skip().
Trying to handle this directly in your CI/CD pipeline (like GitHub Actions or GitLab CI) usually results in a messy config.yaml file cluttered with long --spec arguments.
Today, I want to share a simple solution I implemented using a shell script (run_e2e_tests.sh) to manage test execution order and exclusions cleanly.
The Problem: The "Messy" YAML
If you want to run specific files in a specific order using just the CLI, your CI configuration often ends up looking like this:
YAML
# .gitlab-ci.yml or similar
script:
- npx cypress run --spec "cypress/e2e/login.cy.js,cypress/e2e/checkout.cy.js,cypress/e2e/admin.cy.js"
This is hard to read. Worse, if checkout.cy.js starts failing due to an application bug, you have to edit this massive string in your pipeline config to remove it.
The Solution: A Shell Script Wrapper
Instead of putting logic in the pipeline config, I created a shell script called run_e2e_tests.sh. This script acts as the "Source of Truth" for which tests run and in what order.
1. Create the Script
Create a file named run_e2e_tests.sh in your root directory:
Bash
#!/bin/bash
# Define the list of specs in the desired execution order
SPECS=(
"cypress/e2e/auth/login.cy.ts"
"cypress/e2e/dashboard/user_settings.cy.ts"
# Known Issue: JIRA-123 - Checkout button is broken
# "cypress/e2e/checkout/payment.cy.ts"
"cypress/e2e/admin/reports.cy.ts"
)
# Join the array with commas
IFS=,
SPEC_LIST="${SPECS[*]}"
# Run Cypress with the constructed list
echo "Running specs: $SPEC_LIST"
npx cypress run --spec "$SPEC_LIST"
2. Make it Executable
Don't forget to give the script permission to run:
Bash
chmod +x run_e2e_tests.sh
3. Update Your Pipeline
Now, your CI configuration file becomes incredibly simple:
YAML
script:
- ./run_e2e_tests.sh
Why This Approach is Better
1. Deterministic Execution Order
By default, Cypress might run specs alphabetically or effectively random depending on parallelization settings. With this array, you explicitly define that login.cy.ts must happen before user_settings.cy.ts. This gives you full control over the sequence.
2. Easy "Quarantining" of Broken Features
This is the biggest benefit. If the application code has a bug (e.g., the checkout button is broken) and your test is failing, you don't need to change the test code or commit a it.skip which you might forget later.
You simply comment out the line in the shell script:
Bash
# "cypress/e2e/checkout/payment.cy.ts"
The pipeline goes green, and you leave a comment explaining why it is disabled (e.g., linking to a Jira ticket).
3. Separation of Concerns
Your CI/CD YAML file should handle the environment (Docker images, caching, stages), not the specific logic of which tests to run. Moving this logic to a shell script keeps your pipeline config clean and maintainable.
Conclusion
Sometimes the best tools are the simplest ones. By wrapping your Cypress execution in a shell script, you gain flexibility and clarity without adding complexity to your codebase.
Happy testing!

