Help Center/Automation & CI/CD/Quality gates & traceability

Attach Screenshots, Logs, and Traces to Automated Test Results

Attach screenshots, logs, videos, traces, and other evidence to automated TestCollab executions by using JUnit attachment markers with tc report.

On this page

A failed automated result is more useful when the screenshot, browser trace, or log is attached to the same TestCollab execution. Starting with @testcollab/cli 1.17.0, tc report can read attachment markers from JUnit XML, upload the referenced files, and link them to the matching test case execution.

This feature applies to JUnit XML only. Mochawesome JSON results can be uploaded, but attachment markers in Mochawesome are not processed. If the XML structure is unfamiliar, see JUnit XML Format Explained.

Attachments fit inside the standard createTestPlan, test runner, and report path. They do not require createBuild or gate.

Use one attachment marker per line

Put each file path on its own line inside the test case's <system-out> element:

<testcase
  classname="Authentication"
  name="[TC-42] customer can sign in"
>
  <failure message="Expected dashboard URL" />
  <system-out><![CDATA[
[[ATTACHMENT|test-results/login/test-failed-1.png]]
[[ATTACHMENT|test-results/login/browser.log]]
[[ATTACHMENT|test-results/login/trace.zip]]
  ]]></system-out>
</testcase>

The marker is case-insensitive, but it must occupy the complete line. Leading and trailing whitespace are allowed. A log sentence that only mentions [[ATTACHMENT|file]] in the middle of a line is ignored.

Some runners add an optional JSON part:

[[ATTACHMENT|test-results/login/browser.log|{"name":"browser console"}]]

The CLI accepts this form but ignores the JSON metadata. The uploaded file uses its filename.

Playwright can emit the markers automatically

Playwright's JUnit reporter writes an attachment marker for each attachment that Playwright recorded for a test. Configure Playwright to retain the evidence and write JUnit XML to a file:

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  reporter: [['junit', { outputFile: 'test-results/results.xml' }]],
  use: {
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
    trace: 'retain-on-failure'
  }
});
npx playwright test

tc report \
  --project 123 \
  --test-plan-id 456 \
  --format junit \
  --result-file ./test-results/results.xml

Keep the JUnit file and Playwright's artifact directory on the same CI agent until tc report finishes. See Playwright Test Reports for a complete setup.

Emit markers from another test runner

For another framework, print the marker while the test runs and configure its JUnit reporter to retain captured standard output in <system-out>. For example:

# Pytest test code
def attach_to_junit(file_path):
    print(f"[[ATTACHMENT|{file_path}]]")

def test_login(page):
    # Test steps...
    attach_to_junit("test-results/login-failure.png")
// Jest test code
function attachToJunit(filePath) {
  console.log(`[[ATTACHMENT|${filePath}]]`);
}

test('[TC-42] customer can sign in', async () => {
  // Test steps...
  attachToJunit('test-results/login-failure.png');
});

Reporter behavior varies. Some JUnit reporters discard console output unless an option is enabled. Open the generated XML and verify the marker is inside the correct test case's <system-out> before relying on it in CI.

How file paths are resolved

An attachment path can be:

  • An absolute path to a file on the CI agent
  • Relative to the directory where tc report runs
  • Relative to the directory containing the file passed to --result-file

For a relative path, the CLI checks the current working directory first and the result file's directory second. If the same relative path exists in both places, the current working directory wins. Duplicate paths within one test case are attached once.

The file must still exist locally when the upload runs. Publishing or deleting the runner's artifact directory before tc report makes the marker unresolvable.

Limits and supported file types

  • Up to 10 successfully resolved files per test case
  • Up to 10 MB per file, calculated as 10 x 1024 x 1024 bytes
  • Markers are deduplicated in document order

The CLI assigns a known content type to these extensions:

Images:     .png .jpg .jpeg .gif .webp .svg
Documents:  .pdf .txt .log .csv .json .har .xml .html .htm
Archives:   .zip
Video:      .webm .mp4

An unknown extension is sent as application/octet-stream. The TestCollab server can still reject a file based on its upload policy.

What happens when an attachment fails

Result status is updated before attachments are processed. A missing, unreadable, oversized, extra, rejected, or un-linkable attachment produces a warning and is skipped. Attachment-related warnings do not change the exit status of an otherwise successful tc report command.

This does not mean every tc report error exits 0. Invalid arguments, a missing or invalid result file, authentication failures, and fatal API errors exit 1. Read the upload summary and warnings to distinguish a successful result upload with missing evidence from a failed result upload.

Where attachments appear in TestCollab

Each file is linked to the individual executed test case that contained the marker. It is not added as a test-plan-level or build-level attachment. Open the test plan run, select the executed case, and review its attachments with the result and failure details.

The test itself must map to an execution first. Use a marker such as [TC-42], confirm the case belongs to the target plan, and make sure the execution is assigned to the user who owns the API token.

Use the EU region

For an EU-hosted account, add this option to tc report:

--api-url https://api-eu.testcollab.io

Troubleshooting

The marker is ignored

Confirm you uploaded with --format junit, the marker is inside that test case's <system-out>, and nothing except whitespace shares its line. The syntax is [[ATTACHMENT|path]] with two closing brackets.

The CLI reports attachment not found

Run tc report from the same workspace as the test runner, or make the path relative to the JUnit file. Confirm an earlier CI step did not move or remove the artifact.

The file uploads but does not appear on the expected case

Verify the marker is inside the expected <testcase>, not a suite-level <system-out>. Then check the [TC-42] mapping and the token owner's assignment.

The pipeline passes after an attachment warning

This is expected. Attachment problems are nonfatal by design. If missing evidence must block your workflow, scan the CLI output for attachment warnings or add a separate file-existence check before reporting. The optional TestCollab quality gate evaluates case statuses, not whether every attachment uploaded.

Last updated 2026-08-26.