├── .github └── workflows │ ├── ci.yml │ ├── parallel.yml │ ├── split.yml │ └── standard.yml ├── .gitignore ├── README.md ├── docs └── examples.md ├── images ├── ci1.png ├── combine.png ├── d1.png ├── d2.png └── flow.png ├── package-lock.json └── package.json /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: main 2 | on: [push] 3 | 4 | jobs: 5 | # TODO: add some test jobs 6 | 7 | release: 8 | runs-on: ubuntu-22.04 9 | name: release 10 | # only release from the master branch 11 | if: github.ref == 'refs/heads/main' 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | 16 | - uses: bahmutov/npm-install@v1 17 | 18 | # https://github.com/cycjimmy/semantic-release-action 19 | - name: Semantic Release 20 | uses: cycjimmy/semantic-release-action@v3 21 | id: semantic 22 | with: 23 | branch: main 24 | dry_run: false 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 28 | 29 | - name: Push updates to branch for major version 30 | # if there is a new version published, let's say v1.2.3 31 | # then this step will update branch "v1" to this commit 32 | # https://github.com/cypress-io/github-action/branches 33 | if: steps.semantic.outputs.new_release_published == 'true' 34 | run: 'git push https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git HEAD:refs/heads/v${{steps.semantic.outputs.new_release_major_version}}' 35 | env: 36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | -------------------------------------------------------------------------------- /.github/workflows/parallel.yml: -------------------------------------------------------------------------------- 1 | # reusable workflow to install NPM dependencies 2 | # and run Cypress tests across N machines in parallel 3 | # https://docs.github.com/en/actions/learn-github-actions/reusing-workflows 4 | name: parallel 5 | on: 6 | workflow_call: 7 | inputs: 8 | n: 9 | description: 'Number of parallel containers' 10 | type: number 11 | required: true 12 | default: 1 13 | # standard parameters 14 | config: 15 | description: 'Set configuration values. Separate multiple values with a comma. The values set here override any values set in your configuration file.' 16 | type: string 17 | required: false 18 | config-file: 19 | description: 'Path to a JSON file where configuration values are set.' 20 | type: string 21 | required: false 22 | env: 23 | description: 'Sets Cypress environment variables' 24 | type: string 25 | required: false 26 | browser: 27 | description: 'Name of the browser to use' 28 | type: string 29 | required: false 30 | command: 31 | description: 'Command that overrides cypress run' 32 | type: string 33 | required: false 34 | start: 35 | description: 'Command for starting local server in the background' 36 | type: string 37 | required: false 38 | start-windows: 39 | description: 'A different start command on Windows' 40 | type: string 41 | required: false 42 | build: 43 | description: 'Command to run in build step before starting tests' 44 | type: string 45 | required: false 46 | install: 47 | description: 'Whether or not to run install' 48 | type: boolean 49 | required: false 50 | default: true 51 | install-command: 52 | description: 'Custom install command to use' 53 | type: string 54 | required: false 55 | runTests: 56 | description: 'Whether or not to run tests' 57 | type: boolean 58 | required: false 59 | default: true 60 | wait-on: 61 | description: 'Local server URL to wait for' 62 | type: string 63 | required: false 64 | wait-on-timeout: 65 | description: 'Amount of time to wait for wait-on url to be available' 66 | type: number 67 | required: false 68 | # default is 60 seconds 69 | default: 60 70 | parallel: 71 | description: 'Whether or not to load balance tests using multiple containers' 72 | type: boolean 73 | required: false 74 | group: 75 | description: 'Group setting for tests' 76 | type: string 77 | required: false 78 | tag: 79 | description: 'Tag setting for tests' 80 | type: string 81 | required: false 82 | working-directory: 83 | description: 'Working directory containing Cypress folder' 84 | type: string 85 | required: false 86 | headed: 87 | description: 'Whether or not to use headed mode' 88 | type: boolean 89 | required: false 90 | spec: 91 | description: 'Provide a specific specs to run' 92 | type: string 93 | required: false 94 | project: 95 | description: 'Path of project to run' 96 | type: string 97 | required: false 98 | command-prefix: 99 | description: 'You can prefix the default test command using the command-prefix option.' 100 | type: string 101 | required: false 102 | ci-build-id: 103 | description: 'ID associates multiple CI machines to one test run' 104 | type: string 105 | required: false 106 | cache-key: 107 | description: 'Custom cache key' 108 | type: string 109 | required: false 110 | quiet: 111 | description: 'Whether or not to silence any Cypress specific output from stdout' 112 | type: boolean 113 | required: false 114 | default: false 115 | debug-inputs: 116 | description: 'Print the workflow inputs' 117 | type: boolean 118 | required: false 119 | default: false 120 | debug: 121 | description: 'Set the environment variable DEBUG' 122 | type: string 123 | required: false 124 | default: '' 125 | component: 126 | description: 'Run the component tests, skipping end-to-end tests' 127 | type: boolean 128 | required: false 129 | default: false 130 | publish-summary: 131 | description: 'Whether or not to publish job summary' 132 | type: boolean 133 | required: false 134 | default: true 135 | 136 | # to tie all tests into a single recorded run, need the Dashboard key 137 | secrets: 138 | recordKey: 139 | description: 'Cypress Dashboard Record Key' 140 | required: true 141 | 142 | jobs: 143 | prepare: 144 | runs-on: ubuntu-22.04 145 | # explicitly set the output of this job 146 | # so that other jobs can use it 147 | outputs: 148 | matrix: ${{ steps.prepare.outputs.matrix }} 149 | steps: 150 | # generate the list using a bash script 151 | - name: Create matrix ⊹ 152 | id: prepare 153 | # for reusable workflow, must use the full action reference 154 | uses: bahmutov/gh-build-matrix@main 155 | with: 156 | n: ${{ inputs.n }} # number of containers to output 157 | 158 | - name: Print result 🖨 159 | run: echo '${{ steps.prepare.outputs.matrix }}' 160 | 161 | # the N parallel testing jobs we create 162 | # https://github.com/cypress-io/github-action#parallel 163 | test: 164 | needs: prepare 165 | runs-on: ubuntu-22.04 166 | strategy: 167 | fail-fast: false 168 | matrix: ${{fromJSON(needs.prepare.outputs.matrix)}} 169 | steps: 170 | - name: Debug inputs 🐞 171 | if: ${{ inputs.debug-inputs }} 172 | env: 173 | WORKFLOW_INPUTS: ${{ toJson(inputs) }} 174 | run: echo "$WORKFLOW_INPUTS" 175 | 176 | - name: Checkout 🛎 177 | uses: actions/checkout@v4 178 | 179 | # because of "record" and "parallel" parameters 180 | # these containers will load balance all found tests among themselves 181 | - name: Cypress tests 🧪 182 | uses: cypress-io/github-action@v6 183 | with: 184 | record: true 185 | parallel: true 186 | # pass the rest of workflow parameters 187 | config: ${{ inputs.config }} 188 | config-file: ${{ inputs.config-file }} 189 | env: '${{ inputs.env }}' 190 | browser: ${{ inputs.browser }} 191 | command: ${{ inputs.command }} 192 | start: ${{ inputs.start }} 193 | start-windows: ${{ inputs.start-windows }} 194 | build: ${{ inputs.build }} 195 | install: ${{ inputs.install }} 196 | install-command: ${{ inputs.install-command }} 197 | runTests: ${{ inputs.runTests }} 198 | wait-on: ${{ inputs.wait-on }} 199 | wait-on-timeout: ${{ inputs.wait-on-timeout }} 200 | group: ${{ inputs.group }} 201 | tag: ${{ inputs.tag }} 202 | working-directory: ${{ inputs.working-directory }} 203 | headed: ${{ inputs.headed }} 204 | spec: ${{ inputs.spec }} 205 | project: ${{ inputs.project }} 206 | command-prefix: ${{ inputs.command-prefix }} 207 | ci-build-id: ${{ inputs.ci-build-id }} 208 | cache-key: ${{ inputs.cache-key }} 209 | quiet: ${{ inputs.quiet }} 210 | component: ${{ inputs.component }} 211 | publish-summary: ${{ inputs.publish-summary }} 212 | env: 213 | # pass the Dashboard record key as an environment variable 214 | CYPRESS_RECORD_KEY: ${{ secrets.recordKey }} 215 | # pass the DEBUG environment variable 216 | DEBUG: ${{ inputs.debug }} 217 | -------------------------------------------------------------------------------- /.github/workflows/split.yml: -------------------------------------------------------------------------------- 1 | # reusable workflow to install NPM dependencies 2 | # and run Cypress tests across N machines in using cypress-split 3 | # https://github.com/bahmutov/cypress-split 4 | name: split 5 | on: 6 | workflow_call: 7 | inputs: 8 | nE2E: 9 | description: 'Number of parallel containers for running E2E specs' 10 | type: number 11 | required: false 12 | default: 0 13 | nComponent: 14 | description: 'Number of parallel containers for running Component specs' 15 | type: number 16 | required: false 17 | default: 0 18 | # a common command to run before parallel jobs 19 | before-run: 20 | description: 'A command to run once before all parallel jobs' 21 | type: string 22 | required: false 23 | # standard parameters 24 | config: 25 | description: 'Set configuration values. Separate multiple values with a comma. The values set here override any values set in your configuration file.' 26 | type: string 27 | required: false 28 | config-file: 29 | description: 'Path to a JSON file where configuration values are set.' 30 | type: string 31 | required: false 32 | env: 33 | description: 'Sets Cypress environment variables' 34 | type: string 35 | required: false 36 | browser: 37 | description: 'Name of the browser to use' 38 | type: string 39 | required: false 40 | command: 41 | description: 'Command that overrides cypress run' 42 | type: string 43 | required: false 44 | start: 45 | description: 'Command for starting local server in the background' 46 | type: string 47 | required: false 48 | start-windows: 49 | description: 'A different start command on Windows' 50 | type: string 51 | required: false 52 | build: 53 | description: 'Command to run in build step before starting tests' 54 | type: string 55 | required: false 56 | install: 57 | description: 'Whether or not to run install' 58 | type: boolean 59 | required: false 60 | default: true 61 | install-command: 62 | description: 'Custom install command to use' 63 | type: string 64 | required: false 65 | runTests: 66 | description: 'Whether or not to run tests' 67 | type: boolean 68 | required: false 69 | default: true 70 | wait-on: 71 | description: 'Local server URL to wait for' 72 | type: string 73 | required: false 74 | wait-on-timeout: 75 | description: 'Amount of time to wait for wait-on url to be available' 76 | type: number 77 | required: false 78 | # default is 60 seconds 79 | default: 60 80 | working-directory: 81 | description: 'Working directory containing Cypress folder' 82 | type: string 83 | required: false 84 | headed: 85 | description: 'Whether or not to use headed mode' 86 | type: boolean 87 | required: false 88 | spec: 89 | description: 'Provide a specific specs to run' 90 | type: string 91 | required: false 92 | skip-spec: 93 | description: 'Provide a list of specs to NOT run' 94 | type: string 95 | required: false 96 | project: 97 | description: 'Path of project to run' 98 | type: string 99 | required: false 100 | command-prefix: 101 | description: 'You can prefix the default test command using the command-prefix option.' 102 | type: string 103 | required: false 104 | cache-key: 105 | description: 'Custom cache key' 106 | type: string 107 | required: false 108 | quiet: 109 | description: 'Whether or not to silence any Cypress specific output from stdout' 110 | type: boolean 111 | required: false 112 | default: false 113 | # custom input parameters 114 | debug-inputs: 115 | description: 'Print the workflow inputs' 116 | type: boolean 117 | required: false 118 | default: false 119 | debug: 120 | description: 'Set the environment variable DEBUG' 121 | type: string 122 | required: false 123 | default: '' 124 | store-artifacts: 125 | description: 'Store screenshots and videos from the cypress folder' 126 | type: boolean 127 | required: false 128 | default: true 129 | marge: 130 | description: | 131 | Download the Mochawesome results from all test jobs 132 | and merge into a single report 133 | type: boolean 134 | required: false 135 | default: false 136 | coverage: 137 | description: | 138 | Download all coverage results from all test jobs 139 | and merge into a single coverage report 140 | type: boolean 141 | required: false 142 | default: false 143 | # we are setting it true by default for the split workflow 144 | # since this workflow has its own GHA summary via cypress-split plugin 145 | publish-summary: 146 | description: 'Whether or not to publish job summary' 147 | type: boolean 148 | required: false 149 | default: false 150 | # use the following split configuration/timings file 151 | split-file: 152 | description: 'E2E split timings file to use and merge back' 153 | type: string 154 | required: false 155 | outputs: 156 | merged-timings: 157 | description: "Combined timings JSON from split files" 158 | value: ${{ jobs.merge-split-timings.outputs.timings }} 159 | jobs: 160 | prepare: 161 | runs-on: ubuntu-22.04 162 | # explicitly set the output of this job 163 | # so that other jobs can use it 164 | outputs: 165 | matrixE2E: ${{ steps.prepareE2E.outputs.matrix }} 166 | matrixComponent: ${{ steps.prepareComponent.outputs.matrix }} 167 | steps: 168 | # generate the list using a bash script 169 | - name: Create E2E container matrix ⊹ 170 | id: prepareE2E 171 | # for reusable workflow, must use the full action reference 172 | uses: bahmutov/gh-build-matrix@main 173 | with: 174 | # number of containers to use for running E2E tests 175 | n: ${{ inputs.nE2E }} 176 | 177 | - name: Create component container matrix ⊹ 178 | id: prepareComponent 179 | # for reusable workflow, must use the full action reference 180 | uses: bahmutov/gh-build-matrix@main 181 | with: 182 | # number of containers to use for running component tests 183 | n: ${{ inputs.nComponent }} 184 | 185 | - name: Print result 🖨 186 | run: echo '${{ steps.prepareE2E.outputs.matrix }}' 187 | 188 | # by installing dependencies once 189 | # we cache them 190 | - name: Checkout 🛎 191 | uses: actions/checkout@v4 192 | - name: Install deps 📦 193 | uses: cypress-io/github-action@v6 194 | with: 195 | runTests: false 196 | - name: Before run 🧺 197 | if: ${{ inputs.before-run }} 198 | run: ${{ inputs.before-run }} 199 | 200 | # the N parallel E2E testing jobs we create 201 | e2eTests: 202 | if: ${{ inputs.nE2E > 0 }} 203 | needs: prepare 204 | runs-on: ubuntu-22.04 205 | strategy: 206 | fail-fast: false 207 | matrix: ${{ fromJSON(needs.prepare.outputs.matrixE2E) }} 208 | steps: 209 | - name: Debug inputs 🐞 210 | if: ${{ inputs.debug-inputs }} 211 | env: 212 | WORKFLOW_INPUTS: ${{ toJson(inputs) }} 213 | run: echo "$WORKFLOW_INPUTS" 214 | 215 | - name: Checkout 🛎 216 | uses: actions/checkout@v4 217 | 218 | # these containers will load balance all found tests among themselves 219 | - name: Cypress tests 🧪 220 | uses: cypress-io/github-action@v6 221 | # pass the machine index and the total number 222 | # https://github.com/bahmutov/cypress-split 223 | env: 224 | SPLIT: ${{ strategy.job-total }} 225 | SPLIT_INDEX: ${{ strategy.job-index }} 226 | SPLIT_FILE: '${{ inputs.split-file }}' 227 | # pass the custom list of specs if needed 228 | SPEC: '${{ inputs.spec }}' 229 | SKIP_SPEC: '${{ inputs.skip-spec }}' 230 | # pass the DEBUG environment variable 231 | DEBUG: ${{ inputs.debug }} 232 | # pass the rest of the commands via Cypress GH Action 233 | with: 234 | config: ${{ inputs.config }} 235 | config-file: ${{ inputs.config-file }} 236 | # Cypress.env values 237 | env: '${{ inputs.env }}' 238 | browser: ${{ inputs.browser }} 239 | build: ${{ inputs.build }} 240 | command: ${{ inputs.command }} 241 | start: ${{ inputs.start }} 242 | start-windows: ${{ inputs.start-windows }} 243 | install: ${{ inputs.install }} 244 | install-command: ${{ inputs.install-command }} 245 | runTests: ${{ inputs.runTests }} 246 | wait-on: ${{ inputs.wait-on }} 247 | wait-on-timeout: ${{ inputs.wait-on-timeout }} 248 | working-directory: ${{ inputs.working-directory }} 249 | headed: ${{ inputs.headed }} 250 | spec: ${{ inputs.spec }} 251 | project: ${{ inputs.project }} 252 | command-prefix: ${{ inputs.command-prefix }} 253 | cache-key: ${{ inputs.cache-key }} 254 | quiet: ${{ inputs.quiet }} 255 | publish-summary: ${{ inputs.publish-summary }} 256 | 257 | # capture screenshots, videos, Mochawesome reports 258 | # in a single test artifact so that relative paths work 259 | # capture screenshots, videos, Mochawesome reports, coverage folder 260 | # https://github.com/actions/upload-artifact 261 | - uses: actions/upload-artifact@v4 262 | if: ${{ inputs.store-artifacts && always() }} 263 | with: 264 | name: cypress-split-results-e2e-${{ strategy.job-index }} 265 | path: | 266 | cypress/screenshots 267 | cypress/videos 268 | cypress/results 269 | coverage 270 | ${{ inputs.split-file }} 271 | if-no-files-found: ignore 272 | 273 | # the N parallel Component testing jobs we create 274 | componentTests: 275 | if: ${{ inputs.nComponent > 0 }} 276 | needs: prepare 277 | runs-on: ubuntu-22.04 278 | strategy: 279 | fail-fast: false 280 | matrix: ${{ fromJSON(needs.prepare.outputs.matrixComponent) }} 281 | steps: 282 | - name: Debug inputs 🐞 283 | if: ${{ inputs.debug-inputs }} 284 | env: 285 | WORKFLOW_INPUTS: ${{ toJson(inputs) }} 286 | run: echo "$WORKFLOW_INPUTS" 287 | 288 | - name: Checkout 🛎 289 | uses: actions/checkout@v4 290 | 291 | # these containers will load balance all found tests among themselves 292 | - name: Cypress tests 🧪 293 | uses: cypress-io/github-action@v6 294 | # pass the machine index and the total number 295 | # https://github.com/bahmutov/cypress-split 296 | env: 297 | SPLIT: ${{ strategy.job-total }} 298 | SPLIT_INDEX: ${{ strategy.job-index }} 299 | # pass the custom list of specs if needed 300 | SPEC: '${{ inputs.spec }}' 301 | SKIP_SPEC: '${{ inputs.skip-spec }}' 302 | # pass the DEBUG environment variable 303 | DEBUG: ${{ inputs.debug }} 304 | # pass the rest of the commands via Cypress GH Action 305 | with: 306 | config: ${{ inputs.config }} 307 | config-file: ${{ inputs.config-file }} 308 | # Cypress.env values 309 | env: '${{ inputs.env }}' 310 | browser: ${{ inputs.browser }} 311 | build: ${{ inputs.build }} 312 | command: ${{ inputs.command }} 313 | install: ${{ inputs.install }} 314 | install-command: ${{ inputs.install-command }} 315 | runTests: ${{ inputs.runTests }} 316 | working-directory: ${{ inputs.working-directory }} 317 | headed: ${{ inputs.headed }} 318 | spec: ${{ inputs.spec }} 319 | project: ${{ inputs.project }} 320 | command-prefix: ${{ inputs.command-prefix }} 321 | cache-key: ${{ inputs.cache-key }} 322 | quiet: ${{ inputs.quiet }} 323 | component: true 324 | 325 | # capture screenshots, videos, Mochawesome reports 326 | # in a single test artifact so that relative paths work 327 | # capture screenshots, videos, Mochawesome reports, coverage folder 328 | # https://github.com/actions/upload-artifact 329 | - uses: actions/upload-artifact@v4 330 | if: ${{ inputs.store-artifacts && always() }} 331 | with: 332 | name: cypress-split-results-component-${{ strategy.job-index }} 333 | path: | 334 | cypress/screenshots 335 | cypress/videos 336 | cypress/results 337 | coverage 338 | if-no-files-found: ignore 339 | 340 | merge-reports: 341 | if: ${{ inputs.store-artifacts && inputs.marge && always() }} 342 | needs: [e2eTests, componentTests] 343 | runs-on: ubuntu-22.04 344 | steps: 345 | - name: Checkout 🛎 346 | uses: actions/checkout@v4 347 | - name: Install dependencies 🧪 348 | uses: cypress-io/github-action@v6 349 | with: 350 | runTests: false 351 | # https://github.com/actions/download-artifact 352 | - uses: actions/download-artifact@v4 353 | # download all test results artifacts from the previous jobs 354 | # it would be nice to download only the split jobs test artifacts 355 | # but cannot specify the pattern of the test artifacts yet 356 | # https://github.com/actions/download-artifact/issues/103 357 | with: 358 | path: split-results 359 | - name: Display structure of downloaded files 360 | run: ls -R split-results 361 | 362 | # copy all reports and videos and screenshots into a single place 363 | # mochawesome/ 364 | # screenshots/ 365 | # videos/ 366 | # results/ 367 | # all individual JSON reports 368 | - name: Prepare folder 369 | run: | 370 | mkdir mochawesome 371 | mkdir -p mochawesome/screenshots 372 | mkdir -p mochawesome/videos 373 | mkdir -p mochawesome/results 374 | - name: Copy all assets and JSON reports 375 | run: | 376 | cp -r split-results/cypress-split-results-*/cypress/screenshots/* mochawesome/screenshots || true 377 | cp -r split-results/cypress-split-results-*/cypress/videos/* mochawesome/videos || true 378 | cp -r split-results/cypress-split-results-*/cypress/results/* mochawesome/results || true 379 | - name: Show copied files 380 | run: ls -lR mochawesome 381 | 382 | - name: Merge Mochawesome JSON reports 383 | # assuming the merge tool is installed 384 | run: npx mochawesome-merge mochawesome/results/*.json -o mochawesome/results/merged.json 385 | - name: Generate Mochawesome HTML report 386 | # assuming the merge tool is installed 387 | run: | 388 | npx marge mochawesome/results/merged.json \ 389 | --charts true --showHooks always \ 390 | --reportDir mochawesome/results \ 391 | --reportFilename index.html 392 | 393 | - uses: actions/upload-artifact@v4 394 | with: 395 | name: merged-mochawesome-report 396 | path: mochawesome 397 | 398 | merge-coverage: 399 | if: ${{ inputs.store-artifacts && inputs.coverage && always() }} 400 | needs: [e2eTests, componentTests] 401 | runs-on: ubuntu-22.04 402 | steps: 403 | - name: Checkout 🛎 404 | uses: actions/checkout@v4 405 | - name: Install dependencies 🧪 406 | uses: cypress-io/github-action@v6 407 | with: 408 | runTests: false 409 | # https://github.com/actions/download-artifact 410 | - uses: actions/download-artifact@v4 411 | # download all test results artifacts from the previous jobs 412 | # it would be nice to download only the split jobs test artifacts 413 | # but cannot specify the pattern of the test artifacts yet 414 | # https://github.com/actions/download-artifact/issues/103 415 | with: 416 | path: split-results 417 | - name: Display structure of downloaded files 418 | run: ls -R split-results 419 | 420 | - name: Merge coverage reports 421 | # assuming the merge tool is installed 422 | # https://github.com/bahmutov/cypress-code-coverage 423 | run: npx cc-merge split-results 424 | 425 | - uses: actions/upload-artifact@v4 426 | with: 427 | name: merged-coverage 428 | path: coverage 429 | 430 | merge-split-timings: 431 | # only merge timings if E2E specs were successful 432 | if: ${{ inputs.split-file }} 433 | needs: [e2eTests] 434 | outputs: 435 | timings: ${{ steps.merge.outputs.merged-timings }} 436 | runs-on: ubuntu-22.04 437 | steps: 438 | - name: Checkout 🛎 439 | uses: actions/checkout@v4 440 | - name: Install dependencies 🧪 441 | uses: cypress-io/github-action@v6 442 | with: 443 | runTests: false 444 | # https://github.com/actions/download-artifact 445 | - uses: actions/download-artifact@v4 446 | # download all test results artifacts from the previous jobs 447 | # it would be nice to download only the split jobs test artifacts 448 | # but cannot specify the pattern of the test artifacts yet 449 | # https://github.com/actions/download-artifact/issues/103 450 | with: 451 | path: split-results 452 | - name: Display structure of downloaded files 453 | run: ls -R split-results 454 | 455 | # to merge timings you need v1.13+ of cypress-split 456 | - name: Merge split timings 457 | id: merge 458 | run: | 459 | npx cypress-split-merge \ 460 | --parent-folder split-results \ 461 | --split-file ${{ inputs.split-file }} \ 462 | --output ${{ inputs.split-file }} \ 463 | --set-gha-output merged-timings 464 | env: 465 | # pass the DEBUG environment variable 466 | DEBUG: ${{ inputs.debug }} 467 | 468 | - name: Print timings 🖨️ 469 | run: | 470 | echo off 471 | echo Merged timings 472 | echo '${{ steps.merge.outputs.merged-timings }}' 473 | 474 | - uses: actions/upload-artifact@v4 475 | with: 476 | name: merged-split-file 477 | path: '${{ inputs.split-file }}' 478 | -------------------------------------------------------------------------------- /.github/workflows/standard.yml: -------------------------------------------------------------------------------- 1 | # This is a reusable workflow that checks out the source code, 2 | # installs dependencies and runs Cypress tests on a single machine 3 | # https://docs.github.com/en/actions/learn-github-actions/reusing-workflows 4 | name: standard 5 | on: 6 | workflow_call: 7 | inputs: 8 | # most parameters are just copied from the Cypress GH Action 9 | # https://github.com/cypress-io/github-action/blob/master/action.yml 10 | record: 11 | description: 'Sends test results to Cypress Dashboard' 12 | type: boolean 13 | required: false 14 | default: false 15 | config: 16 | description: 'Set configuration values. Separate multiple values with a comma. The values set here override any values set in your configuration file.' 17 | type: string 18 | required: false 19 | config-file: 20 | description: 'Path to a JSON file where configuration values are set.' 21 | type: string 22 | required: false 23 | env: 24 | description: 'Sets Cypress environment variables' 25 | type: string 26 | required: false 27 | browser: 28 | description: 'Name of the browser to use' 29 | type: string 30 | required: false 31 | command: 32 | description: 'Command that overrides cypress run' 33 | type: string 34 | required: false 35 | start: 36 | description: 'Command for starting local server in the background' 37 | type: string 38 | required: false 39 | start-windows: 40 | description: 'A different start command on Windows' 41 | type: string 42 | required: false 43 | build: 44 | description: 'Command to run in build step before starting tests' 45 | type: string 46 | required: false 47 | install: 48 | description: 'Whether or not to run install' 49 | type: boolean 50 | required: false 51 | default: true 52 | install-command: 53 | description: 'Custom install command to use' 54 | type: string 55 | required: false 56 | runTests: 57 | description: 'Whether or not to run tests' 58 | type: boolean 59 | required: false 60 | default: true 61 | wait-on: 62 | description: 'Local server URL to wait for' 63 | type: string 64 | required: false 65 | wait-on-timeout: 66 | description: 'Amount of time to wait for wait-on url to be available' 67 | type: number 68 | required: false 69 | # default is 60 seconds 70 | default: 60 71 | parallel: 72 | description: 'Whether or not to load balance tests using multiple containers' 73 | type: boolean 74 | required: false 75 | group: 76 | description: 'Group setting for tests' 77 | type: string 78 | required: false 79 | tag: 80 | description: 'Tag setting for tests' 81 | type: string 82 | required: false 83 | working-directory: 84 | description: 'Working directory containing Cypress folder' 85 | type: string 86 | required: false 87 | headed: 88 | description: 'Whether or not to use the headed mode' 89 | type: boolean 90 | required: false 91 | spec: 92 | description: 'Provide a specific specs to run' 93 | type: string 94 | required: false 95 | project: 96 | description: 'Path of project to run' 97 | type: string 98 | required: false 99 | command-prefix: 100 | description: 'You can prefix the default test command using the command-prefix option.' 101 | type: string 102 | required: false 103 | ci-build-id: 104 | description: 'ID associates multiple CI machines to one test run' 105 | type: string 106 | required: false 107 | cache-key: 108 | description: 'Custom cache key' 109 | type: string 110 | required: false 111 | quiet: 112 | description: 'Whether or not to silence any Cypress specific output from stdout' 113 | type: boolean 114 | required: false 115 | default: false 116 | debug-inputs: 117 | description: 'Print the workflow inputs' 118 | type: boolean 119 | required: false 120 | default: false 121 | debug: 122 | description: 'Set the environment variable DEBUG' 123 | type: string 124 | required: false 125 | default: '' 126 | store-artifacts: 127 | description: 'Store screenshots and videos from the cypress folder' 128 | type: boolean 129 | required: false 130 | default: true 131 | component: 132 | description: 'Run the component tests, skipping end-to-end tests' 133 | type: boolean 134 | required: false 135 | default: false 136 | publish-summary: 137 | description: 'Whether or not to publish job summary' 138 | type: boolean 139 | required: false 140 | default: true 141 | 142 | secrets: 143 | recordKey: 144 | description: 'Cypress Dashboard Record Key' 145 | required: false 146 | 147 | jobs: 148 | standard: 149 | runs-on: ubuntu-22.04 150 | steps: 151 | - name: Debug inputs 🐞 152 | if: ${{ inputs.debug-inputs }} 153 | env: 154 | WORKFLOW_INPUTS: ${{ toJson(inputs) }} 155 | run: echo "$WORKFLOW_INPUTS" 156 | 157 | - name: Checkout 🛎 158 | uses: actions/checkout@v4 159 | 160 | - name: Cypress tests 🧪 161 | # https://github.com/cypress-io/github-action 162 | uses: cypress-io/github-action@v6 163 | with: 164 | record: ${{ inputs.record }} 165 | config: ${{ inputs.config }} 166 | config-file: ${{ inputs.config-file }} 167 | env: '${{ inputs.env }}' 168 | browser: ${{ inputs.browser }} 169 | command: ${{ inputs.command }} 170 | start: ${{ inputs.start }} 171 | start-windows: ${{ inputs.start-windows }} 172 | build: ${{ inputs.build }} 173 | install: ${{ inputs.install }} 174 | install-command: ${{ inputs.install-command }} 175 | runTests: ${{ inputs.runTests }} 176 | wait-on: ${{ inputs.wait-on }} 177 | wait-on-timeout: ${{ inputs.wait-on-timeout }} 178 | parallel: ${{ inputs.parallel }} 179 | group: ${{ inputs.group }} 180 | tag: ${{ inputs.tag }} 181 | working-directory: ${{ inputs.working-directory }} 182 | headed: ${{ inputs.headed }} 183 | spec: ${{ inputs.spec }} 184 | project: ${{ inputs.project }} 185 | command-prefix: ${{ inputs.command-prefix }} 186 | ci-build-id: ${{ inputs.ci-build-id }} 187 | cache-key: ${{ inputs.cache-key }} 188 | quiet: ${{ inputs.quiet }} 189 | component: ${{ inputs.component }} 190 | publish-summary: ${{ inputs.publish-summary }} 191 | env: 192 | # pass the Dashboard record key as an environment variable 193 | CYPRESS_RECORD_KEY: ${{ secrets.recordKey }} 194 | # pass the DEBUG environment variable 195 | DEBUG: ${{ inputs.debug }} 196 | 197 | # capture screenshots, videos, Mochawesome reports 198 | # in a single test artifact so that relative paths work 199 | # capture screenshots, videos, Mochawesome reports 200 | # https://github.com/actions/upload-artifact 201 | - uses: actions/upload-artifact@v4 202 | if: ${{ inputs.store-artifacts && always() }} 203 | with: 204 | name: cypress-results 205 | path: | 206 | cypress/screenshots 207 | cypress/videos 208 | cypress/results 209 | if-no-files-found: ignore 210 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cypress-workflows 2 | > Reusable Cypress workflows for GitHub Actions 3 | 4 | Call these workflows from your GitHub Action workflows, a single line (with parameters) let's you run N parallel test jobs without any configuration. 5 | 6 | 🎓 Covered in my course [Testing The Swag Store](https://cypress.tips/courses/swag-store) and [Cypress-split plugin](https://cypress.tips/courses/cypress-split) 7 | 8 | ## Example 9 | 10 | Check out the source code, install and cache dependencies, and run all Cypress specs using the following workflow, store the `cypress/screenshots`, `cypress/videos`, and `cypress/results` artifacts. 11 | 12 | In your `.github/workflows/ci.yml` use the following: 13 | 14 | ```yml 15 | name: ci 16 | on: [push] 17 | jobs: 18 | test: 19 | # use the reusable workflow to check out the code, install dependencies 20 | # and run the Cypress tests 21 | # https://github.com/bahmutov/cypress-workflows 22 | uses: bahmutov/cypress-workflows/.github/workflows/standard.yml@v1 23 | ``` 24 | 25 | ### store-artifacts 26 | 27 | default `true`. Stores the test run artifacts. 28 | 29 | ### start 30 | 31 | A common scenario is to start the application and wait for it to respond. You can pass parameters to the workflow with the start command and the address to wait for. 32 | 33 | ```yml 34 | name: ci 35 | on: [push] 36 | jobs: 37 | test: 38 | # use the reusable workflow to check out the code, install dependencies 39 | # and run the Cypress tests 40 | # https://github.com/bahmutov/cypress-workflows 41 | uses: bahmutov/cypress-workflows/.github/workflows/standard.yml@v1 42 | with: 43 | start: npm start 44 | wait-on: 'http://127.0.0.1:3000' 45 | ``` 46 | 47 | ## Record the run 48 | 49 | To record the test run on the Cypress Dashboard, pass the options to the workflow and the record key as a secret 50 | 51 | ```yml 52 | name: ci 53 | on: [push] 54 | jobs: 55 | test: 56 | # use the reusable workflow to check out the code, install dependencies 57 | # and run the Cypress tests 58 | # https://github.com/bahmutov/cypress-workflows 59 | uses: bahmutov/cypress-workflows/.github/workflows/standard.yml@v1 60 | with: 61 | record: true 62 | secrets: 63 | recordKey: ${{ secrets.CYPRESS_RECORD_KEY }} 64 | ``` 65 | 66 | ## Split example 67 | 68 | Let's split all specs across 3 machines using [cypress-split](https://github.com/bahmutov/cypress-split) plugin. Configure the plugin using its documentation, then use this workflow: 69 | 70 | ```yml 71 | name: ci 72 | on: [push] 73 | jobs: 74 | test: 75 | # https://github.com/bahmutov/cypress-workflows 76 | uses: bahmutov/cypress-workflows/.github/workflows/split.yml@v2 77 | with: 78 | nE2E: 3 79 | ``` 80 | 81 | Sometimes you might want to run a single command before all split jobs start. You can use `before-run` parameter. See [rn-examples](https://github.com/bahmutov/rn-examples) 82 | 83 | ```yml 84 | name: ci 85 | on: push 86 | jobs: 87 | component-tests: 88 | # https://github.com/bahmutov/cypress-workflows 89 | uses: bahmutov/cypress-workflows/.github/workflows/split.yml@v2 90 | with: 91 | # print the test names 92 | before-run: 'npm run test-names --silent' 93 | nComponent: 2 94 | ``` 95 | 96 | ![Workflow diagram](./images/d1.png) 97 | 98 | If you click on the "prepare" job, the `before-run` step prints the test names 99 | 100 | ![The before-run step](./images/d2.png) 101 | 102 | ### marge 103 | 104 | Combines all separate Mochawesome JSON reports into a single HTML report including screenshots and videos. 105 | 106 | ```yml 107 | # https://github.com/bahmutov/cypress-workflows 108 | uses: bahmutov/cypress-workflows/.github/workflows/split.yml@v2 109 | with: 110 | nE2E: 3 111 | marge: true 112 | ``` 113 | 114 | Assumes the project has installed `mochawesome`, `mochawesome-merge`, and `mochawesome-report-generator` dependencies. 115 | 116 | ```js cypress.config.js 117 | import { defineConfig } from 'cypress' 118 | export default defineConfig({ 119 | // https://github.com/adamgruber/mochawesome 120 | reporter: 'mochawesome', 121 | reporterOptions: { 122 | useInlineDiffs: true, 123 | embeddedScreenshots: true, 124 | reportDir: 'cypress/results', 125 | reportFilename: '[name].html', 126 | overwrite: true, 127 | html: true, 128 | json: true, 129 | } 130 | }) 131 | ``` 132 | 133 | See the example in [bahmutov/cy-report-example](https://github.com/bahmutov/cy-report-example) and read the blog post [The Battle of Cypress Mochawesome Reporters](https://glebbahmutov.com/blog/the-awesome-battle/). 134 | 135 | ![Merge reports job](./images/ci1.png) 136 | 137 | **Warning:** make sure the reporter options are shown as above, including `json: true`. For example, you might accidentally overwrite the reporter in the `e2e` block: 138 | 139 | ```js 140 | export default defineConfig({ 141 | // https://github.com/adamgruber/mochawesome 142 | reporter: 'mochawesome', 143 | // all good 144 | reporterOptions: { 145 | useInlineDiffs: true, 146 | embeddedScreenshots: true, 147 | reportDir: 'cypress/results', 148 | reportFilename: '[name].html', 149 | overwrite: true, 150 | html: true, 151 | json: true, 152 | }, 153 | e2e: { 154 | baseUrl: 'http://localhost:3000', 155 | // 🚨 Oops, wrong reporter for merging 156 | reporter: 'cypress-junit-reporter', 157 | } 158 | }) 159 | ``` 160 | 161 | ### Combine code coverage 162 | 163 | If you use [@bahmutov/cypress-code-coverage](https://github.com/bahmutov/cypress-code-coverage) you can combine the coverage from each split run into a single report 164 | 165 | ```yml 166 | # https://github.com/bahmutov/cypress-workflows 167 | uses: bahmutov/cypress-workflows/.github/workflows/split.yml@v2 168 | with: 169 | nE2E: 3 170 | coverage: true 171 | ``` 172 | 173 | ![Combine code coverage workflow](./images/combine.png) 174 | 175 | ## Parallel example 176 | 177 | Let's split all tests across 3 machines using [Cypress Parallelization](https://on.cypress.io/parallelization) paid feature. 178 | 179 | ```yml 180 | name: ci 181 | on: [push] 182 | jobs: 183 | test: 184 | # https://github.com/bahmutov/cypress-workflows 185 | uses: bahmutov/cypress-workflows/.github/workflows/parallel.yml@v1 186 | with: 187 | n: 3 188 | secrets: 189 | recordKey: ${{ secrets.CYPRESS_RECORD_KEY }} 190 | ``` 191 | 192 | Result: 193 | 194 | ![Workflow](./images/flow.png) 195 | 196 | ## Workflows 197 | 198 | - [standard.yml](./.github/workflows/standard.yml) checks out code, installs dependencies, and runs tests on a single machine 199 | - [split.yml](./.github/workflows/split.yml) checks out the coe, installs dependencies, splits specs per machine using [cypress-split](https://github.com/bahmutov/cypress-split) plugin. 200 | - [parallel.yml](./.github/workflows/parallel.yml) lets you specify the number if test machines to use. Splits specs using Cypress Dashboard 201 | 202 | The workflows allow passing pretty much all [Cypress GH Action](https://github.com/cypress-io/github-action) parameters, see the individual workflow YML file. 203 | 204 | ## Coming split spec durations 205 | 206 | If you are using `cypress-split` and the split workflow, you can generate timings for each spec and combine them into a single file. That file can then be used to split the specs more efficiently than alphabetically. You can even commit the file back to the repo using the same GitHub Token. See the example in [bahmutov/cypress-split-timings-example](bahmutov/cypress-split-timings-example). 207 | 208 | ```yml 209 | name: split 210 | on: 211 | # launch this workflow from GitHub Actions page 212 | workflow_dispatch: 213 | jobs: 214 | split: 215 | # https://github.com/bahmutov/cypress-workflows 216 | uses: bahmutov/cypress-workflows/.github/workflows/split.yml@v2 217 | with: 218 | nE2E: 2 219 | # use timings to split E2E specs across 2 machines efficiently 220 | split-file: 'timings.json' 221 | 222 | # this job grab the output for the `split` workflow 223 | # and writes it into a JSON file "timings.json" 224 | # and then commits the updated file to the repository 225 | commit-updated-timings: 226 | if: github.ref == 'refs/heads/main' 227 | runs-on: ubuntu-22.04 228 | needs: split 229 | steps: 230 | - name: Checkout 🛎 231 | uses: actions/checkout@v4 232 | 233 | - name: Show merged timings 🖨️ 234 | run: | 235 | echo off 236 | echo '${{ needs.split.outputs.merged-timings }}' 237 | 238 | - name: Write updated timings 💾 239 | # pretty-print json string into a file 240 | run: echo '${{ toJson(fromJson(needs.split.outputs.merged-timings)) }}' > timings.json 241 | 242 | - name: Commit changed spec timings ⏱️ 243 | # https://github.com/stefanzweifel/git-auto-commit-action 244 | uses: stefanzweifel/git-auto-commit-action@v4 245 | with: 246 | commit_message: Updated spec timings 247 | branch: main 248 | file_pattern: timings.json 249 | ``` 250 | 251 | 252 | ## Versions 253 | 254 | Advice: use an explicit [release tag](https://github.com/bahmutov/cypress-workflows/releases) when using a workflow like `parallel.yml@v1.0.1`. You might also use the latest release from the major branch `v1` like `parallel.yml@v1`. Not recommended: using the latest commit on the branch `parallel.yml@main` or using a specific commit `parallel.yml@2a9d460`. 255 | 256 | ### Migrations 257 | 258 | #### split v1 to v2 259 | 260 | Instead of separate E2E and component test jobs, use a single job and split it as you would like 261 | 262 | ```yml 263 | # v1 264 | jobs: 265 | e2e: 266 | uses: bahmutov/cypress-workflows/.github/workflows/split.yml@v1 267 | with: 268 | n: 3 269 | component: 270 | uses: bahmutov/cypress-workflows/.github/workflows/split.yml@v1 271 | with: 272 | component: true 273 | n: 2 274 | # v2 275 | jobs: 276 | tests: 277 | uses: bahmutov/cypress-workflows/.github/workflows/split.yml@v2 278 | with: 279 | nE2E: 3 # run 3 parallel jobs for E2E tests 280 | nComponent: 2 # run 2 parallel jobs for component tests 281 | ``` 282 | 283 | ## Examples 284 | 285 | - see [docs/examples.md](./docs/examples.md) 286 | - [bahmutov/cypress-workflows-example](https://github.com/bahmutov/cypress-workflows-example) shows how to use the standard and the parallel workflows 287 | - [cypress-3rd-party-script-example](https://github.com/bahmutov/cypress-3rd-party-script-example) shows how to run the end-to-end tests using the standard workflow before deploying the site 288 | - [cypress-wordle](https://github.com/bahmutov/cypress-wordle) 289 | - [react-app-actions](https://github.com/bahmutov/react-app-actions) shows how to run the tests using a workflow and then release a new version using semantic release step 290 | - [bahmutov/cypress-recurse](https://github.com/bahmutov/cypress-recurse) 291 | - [bahmutov/my-svelte-app](https://github.com/bahmutov/my-svelte-app) 292 | - [bahmutov/sudoku-testing-v10](https://github.com/bahmutov/sudoku-testing-v10) runs E2E and component tests using Cypress v10 293 | 294 | ## Blog posts 295 | 296 | - [Run Cypress Specs In Parallel For Free](https://glebbahmutov.com/blog/cypress-parallel-free/) 297 | - [Trigger Selected Cypress Specs Using GitHub Actions](https://glebbahmutov.com/blog/trigger-cypress-specs/) 298 | - [Run Cypress Specs In Parallel For Free Using Spec Timings](https://glebbahmutov.com/blog/cypress-parallel-free-based-on-timings/) 299 | - [Quickly Run The Changed Specs on GitHub Actions](https://glebbahmutov.com/blog/quick-changed-specs/) 300 | 301 | ## Videos 302 | 303 | - [Simplify Cypress Parallel Workflow With A Reusable cypress-split Workflow](https://youtu.be/HWePftvroI4) 304 | 305 | ## Debugging 306 | 307 | ### debug-inputs 308 | 309 | You can print the workflow inputs using the parameter `debug-inputs`, for example 310 | 311 | ```yml 312 | uses: bahmutov/cypress-workflows/.github/workflows/standard.yml@v1 313 | with: 314 | debug-inputs: true 315 | ``` 316 | 317 | Should print something like 318 | 319 | ``` 320 | Debug inputs 🐞 321 | { 322 | "record": false, 323 | "config": false, 324 | "config-file": "", 325 | "envs": "grepTags=@nightly", 326 | ... 327 | "quiet": false, 328 | "debug-inputs": true 329 | } 330 | ``` 331 | 332 | ### debug variable 333 | 334 | You can set the environment variable `DEBUG` to enable verbose output from the [debug](https://github.com/debug-js/debug#readme) module, often used by Cypress and its plugins. 335 | 336 | ```yml 337 | uses: bahmutov/cypress-workflows/.github/workflows/standard.yml@v1 338 | with: 339 | debug: cy-grep 340 | ``` 341 | 342 | Should print something like: 343 | 344 | ``` 345 | cy-grep cy-grep plugin version 1.3.1 +0ms 346 | cy-grep Cypress config env object: { grepFilterSpecs: true, grepOmitFiltered: true, CACHE_FOLDER: '/home/runner/.cache/Cypress' } +1ms 347 | cy-grep specPattern cypress/e2e/**/*.cy.{js,jsx,ts,tsx} +2ms 348 | cy-grep excludeSpecPattern *.hot-update.js +0ms 349 | ``` 350 | 351 | ## Small print 352 | 353 | Author: Gleb Bahmutov <gleb.bahmutov@gmail.com> © 2021 354 | 355 | - [@bahmutov](https://twitter.com/bahmutov) 356 | - [glebbahmutov.com](https://glebbahmutov.com) 357 | - [blog](https://glebbahmutov.com/blog) 358 | - [videos](https://www.youtube.com/glebbahmutov) 359 | - [presentations](https://slides.com/bahmutov) 360 | - [cypress.tips](https://cypress.tips) 361 | - 🎓 [My Cypress courses](https://cypress.tips/courses) 362 | 363 | License: MIT - do anything with the code, but don't blame me if it does not work. 364 | 365 | Support: if you find any problems with this module, email / tweet / 366 | [open issue](https://github.com/bahmutov/cypress-workflows/issues) on Github 367 | 368 | ## MIT License 369 | 370 | Copyright (c) 2021 Gleb Bahmutov <gleb.bahmutov@gmail.com> 371 | 372 | Permission is hereby granted, free of charge, to any person 373 | obtaining a copy of this software and associated documentation 374 | files (the "Software"), to deal in the Software without 375 | restriction, including without limitation the rights to use, 376 | copy, modify, merge, publish, distribute, sublicense, and/or sell 377 | copies of the Software, and to permit persons to whom the 378 | Software is furnished to do so, subject to the following 379 | conditions: 380 | 381 | The above copyright notice and this permission notice shall be 382 | included in all copies or substantial portions of the Software. 383 | 384 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 385 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 386 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 387 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 388 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 389 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 390 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 391 | OTHER DEALINGS IN THE SOFTWARE. 392 | -------------------------------------------------------------------------------- /docs/examples.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | ## Standard workflow 4 | 5 | ### End-to-end tests with an app start 6 | 7 | ```yml 8 | name: ci 9 | on: [push] 10 | jobs: 11 | e2e: 12 | # https://github.com/bahmutov/cypress-workflows 13 | uses: bahmutov/cypress-workflows/.github/workflows/standard.yml@v1 14 | with: 15 | start: npm start 16 | wait-on: 'http://127.0.0.1:3000' 17 | ``` 18 | 19 | ### End-to-end tests with a build and start steps 20 | 21 | ```yml 22 | name: ci 23 | on: [push] 24 | jobs: 25 | e2e: 26 | # https://github.com/bahmutov/cypress-workflows 27 | uses: bahmutov/cypress-workflows/.github/workflows/standard.yml@v1 28 | with: 29 | build: npm run build 30 | start: npm start 31 | wait-on: 'http://127.0.0.1:3000' 32 | ``` 33 | 34 | ### Component tests 35 | 36 | ```yml 37 | name: ci 38 | on: [push] 39 | jobs: 40 | components: 41 | # https://github.com/bahmutov/cypress-workflows 42 | uses: bahmutov/cypress-workflows/.github/workflows/standard.yml@v1 43 | with: 44 | component: true 45 | ``` 46 | -------------------------------------------------------------------------------- /images/ci1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bahmutov/cypress-workflows/f9150b11428a442e102ed7b0ca8e7bc229fa15e4/images/ci1.png -------------------------------------------------------------------------------- /images/combine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bahmutov/cypress-workflows/f9150b11428a442e102ed7b0ca8e7bc229fa15e4/images/combine.png -------------------------------------------------------------------------------- /images/d1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bahmutov/cypress-workflows/f9150b11428a442e102ed7b0ca8e7bc229fa15e4/images/d1.png -------------------------------------------------------------------------------- /images/d2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bahmutov/cypress-workflows/f9150b11428a442e102ed7b0ca8e7bc229fa15e4/images/d2.png -------------------------------------------------------------------------------- /images/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bahmutov/cypress-workflows/f9150b11428a442e102ed7b0ca8e7bc229fa15e4/images/flow.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cypress-workflows", 3 | "version": "0.0.0-development", 4 | "description": "Reusable Cypress workflows for GitHub Actions", 5 | "main": "index.js", 6 | "private": true, 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "semantic-release": "semantic-release" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/bahmutov/cypress-workflows.git" 14 | }, 15 | "keywords": [ 16 | "github-actions", 17 | "cypress", 18 | "workflows" 19 | ], 20 | "author": "Gleb Bahmutov ", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/bahmutov/cypress-workflows/issues" 24 | }, 25 | "homepage": "https://github.com/bahmutov/cypress-workflows#readme", 26 | "release": { 27 | "npmPublish": false, 28 | "branches": [ 29 | "main" 30 | ] 31 | }, 32 | "devDependencies": { 33 | "semantic-release": "^18.0.1" 34 | } 35 | } 36 | --------------------------------------------------------------------------------