Artifacts are files a pipeline produces and deliberately persists after a job finishes — test reports, screenshots, videos, logs, coverage data, traces, or build outputs — so they can be downloaded and inspected, or passed to a later job.
Since CI failures can't be reproduced by re-running interactively, artifacts are how you debug them. On a failing E2E run you want the screenshot at failure, the Playwright trace, the console/server logs and the HTML report — captured automatically and uploaded so any reviewer can open them. The crucial detail is conditional upload: you must upload artifacts even when (especially when) the tests fail, using if: always() or if: !cancelled(), because the default is to skip subsequent steps after a failure. Artifacts also transfer outputs between jobs (build → deploy). Manage retention deliberately: artifacts consume storage, so set sensible retention-days and avoid uploading gigabytes of video on every green run — capture heavy artifacts only on failure or first retry.
An E2E test fails only on CI. Because the job uploads the Playwright HTML report and trace as an artifact with if:!cancelled(), the engineer downloads it, opens the trace, and sees the exact DOM and network state at failure — diagnosing in minutes without ever reproducing the CI machine.
- name: Run E2E
run: npx playwright test
- name: Upload report + traces (even on failure)
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: |
playwright-report/
test-results/
retention-days: 7
# In playwright.config: capture heavy artifacts only when needed
# use: { trace: 'on-first-retry', screenshot: 'only-on-failure', video: 'retain-on-failure' }Scenario: Your pipeline uploads screenshots and traces, but the artifact step is configured with the default behavior. Whenever tests actually FAIL, the artifacts are missing — the only runs with artifacts are the ones that passed and didn't need them. What's wrong?