UNPKG

creevey

Version:

Cross-browser screenshot testing tool for Storybook with fancy UI Runner

222 lines (160 loc) 8.76 kB
# GitLab JUnit Attachments Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Preserve Creevey's existing JUnit attachment properties while adding a GitLab-compatible screenshot attachment tag in testcase `system-out` output. **Architecture:** Keep the change confined to the JUnit reporter and its focused test file. The reporter will continue writing XML-relative `property name="attachment"` entries, and also emit a single GitLab `[[ATTACHMENT|...]]` marker using a project-root-relative path so GitLab can resolve files from uploaded `report/` artifacts. When multiple screenshots exist, the GitLab marker should prefer the diff image because GitLab only links the first parsed attachment. **Tech Stack:** TypeScript, Vitest, Node.js `path` utilities, GitLab JUnit attachment format --- ## File Structure - Modify: `src/server/reporters/junit.ts` - Add small path-formatting helpers. - Emit generic JUnit attachment properties and a single GitLab testcase `system-out` attachment line that prefers the diff image. - Modify: `tests/reporters/junit.test.ts` - Preserve existing attachment-property assertions. - Add GitLab `system-out` assertions. - Add a repo-local report-path helper for `report/...` path verification. - Modify: `memories/workflow.md` - Record that GitLab JUnit screenshot support relies on testcase `system-out` attachment tags in addition to the generic properties block. - Modify: `memories/memory.md` - Record the dual-format JUnit attachment behavior for future CI/reporting work. ### Task 1: Expand Reporter Tests For Dual-Format Attachments **Files:** - Modify: `tests/reporters/junit.test.ts` - Test: `tests/reporters/junit.test.ts` - [ ] **Step 1: Add repo-local report output helpers for GitLab path assertions** Update the test helpers so attachment tests can create a `junit.xml` file under the repo `report/` directory instead of only under `tmpdir()`. Extend cleanup to remove generated directories recursively. ```ts import { readFileSync, unlinkSync, existsSync, rmSync } from 'fs'; const createdFiles: string[] = []; const createdDirectories: string[] = []; function tempRepoReportXmlPath(): string { const outputDir = join(process.cwd(), 'report', `junit-test-${randomUUID()}`); const outputFile = join(outputDir, 'junit.xml'); createdDirectories.push(outputDir); createdFiles.push(outputFile); return outputFile; } afterEach(() => { for (const p of createdFiles.splice(0)) { if (existsSync(p)) unlinkSync(p); } for (const dir of createdDirectories.splice(0)) { if (existsSync(dir)) rmSync(dir, { recursive: true, force: true }); } }); ``` - [ ] **Step 2: Write failing assertions for GitLab testcase attachments** Add assertions to the existing screenshot-attachments section so one test verifies both formats are present, another verifies multi-attachment cases still emit only one GitLab marker that prefers the diff image, and another verifies no testcase `system-out` attachment block is written when attachments are absent. ```ts test('writes GitLab attachment markers in testcase system-out', () => { const out = tempRepoReportXmlPath(); const reportDir = dirname(out); const absPath = join(reportDir, 'Button', 'primary', 'chrome', 'button-actual-1.png'); const t = makeFakeTest({ state: 'failed', images: { header: {} }, attachments: [absPath], }); const xml = runReporter([t], out); expect(xml).toContain('<system-out>'); expect(xml).toContain('[[ATTACHMENT|report/'); expect(xml).toContain('[[ATTACHMENT|report/junit-test-'); expect(xml).toContain('Button/primary/chrome/button-actual-1.png]]'); expect(xml).toContain('</system-out>'); }); test('does not write testcase attachment system-out when attachments are undefined', () => { const out = tempXmlPath(); const xml = runReporter([makeFakeTest({ state: 'passed' })], out); expect(xml).not.toContain('[[ATTACHMENT|'); }); ``` - [ ] **Step 3: Run the focused reporter tests to verify the new assertions fail** Run: `yarn test tests/reporters/junit.test.ts` Expected: FAIL with missing `<system-out>` or missing `[[ATTACHMENT|report/...]]` assertions, while existing attachment-property assertions still pass. ### Task 2: Implement Dual-Format Attachment Output In The Reporter **Files:** - Modify: `src/server/reporters/junit.ts` - Test: `tests/reporters/junit.test.ts` - [ ] **Step 1: Add small helpers for stable attachment path formatting** Add helpers to keep the main testcase writer small and ensure GitLab paths use forward slashes. ```ts import { dirname, relative, resolve, sep } from 'path'; private normalizePath(filePath: string): string { return filePath.split(sep).join('/'); } private relativeAttachmentPath(absPath: string): string { return relative(dirname(this.reportFile), absPath); } private gitlabAttachmentPath(absPath: string): string { return this.normalizePath(relative(process.env.CI_PROJECT_DIR ?? process.cwd(), absPath)); } ``` - [ ] **Step 2: Add a dedicated writer for testcase attachments** Extract the attachment rendering so both formats are emitted from one place while GitLab receives only one preferred marker. ```ts private preferredGitlabAttachment(attachments: readonly string[]): string | undefined { return attachments.find((absPath) => /-diff(?:-\d+)?\.png$/i.test(this.normalizePath(absPath))) ?? attachments[0]; } private writeAttachments(attachments: string[]): void { if (attachments.length === 0) return; this.writeElement('properties', {}, () => { for (const absPath of attachments) { this.writeElement('property', { name: 'attachment', value: this.relativeAttachmentPath(absPath), }); } }); const gitlabAttachment = this.preferredGitlabAttachment(attachments); if (gitlabAttachment) { this.writeElement('system-out', {}, undefined, `[[ATTACHMENT|${this.gitlabAttachmentPath(gitlabAttachment)}]]`); } } ``` - [ ] **Step 3: Replace the inline attachment block in `writeTasks` with the new helper** Keep failure and error rendering unchanged and call the new writer only when attachments are present. ```ts private writeTasks(tests: Map<string, FakeTest>): void { for (const [, test] of tests) { const classname = test.parent.title; const attachments = test.attachments ?? []; this.writeElement( 'testcase', { classname, name: test.title, time: getDuration(test), }, () => { if (test.state === 'failed') { this.writeFailureOrError(test); } this.writeAttachments(attachments); }, ); } } ``` - [ ] **Step 4: Run the focused reporter tests to verify the implementation passes** Run: `yarn test tests/reporters/junit.test.ts` Expected: PASS with both the legacy property assertions and the new GitLab `system-out` attachment assertions. ### Task 3: Update Project Memory For GitLab JUnit Attachments **Files:** - Modify: `memories/workflow.md` - Modify: `memories/memory.md` - [ ] **Step 1: Update workflow memory with the GitLab attachment format detail** Add a concise note near the existing GitLab CI/JUnit section. ```md - The GitLab screenshot job relies on testcase-level `<system-out>` `[[ATTACHMENT|report/...]]` markers for screenshot links in GitLab's Tests UI, while Creevey also keeps generic JUnit attachment properties for other consumers. ``` - [ ] **Step 2: Update primary memory with the dual-format reporter behavior** Add a concise CI integration note. ```md 8. Creevey's JUnit reporter keeps XML-relative `property name="attachment"` entries and also emits GitLab-compatible testcase `system-out` attachment markers using `report/...` paths so screenshot artifacts are visible in GitLab test details. ``` - [ ] **Step 3: Run the focused reporter tests again after memory updates** Run: `yarn test tests/reporters/junit.test.ts` Expected: PASS unchanged, confirming the documentation-only edits did not affect behavior. ## Self-Review - Spec coverage: the plan preserves the existing properties block, adds testcase `system-out` attachment markers, uses `CI_PROJECT_DIR`-relative GitLab paths when available and otherwise falls back to project-root-relative paths, keeps failure/error output unchanged, and adds focused tests for presence and absence cases. - Placeholder scan: no TODO/TBD markers or vague testing instructions remain. - Type consistency: helper names, path rules, and assertion strings match the approved design and the current `JUnitReporter` structure.