Skip to content

Redesign the report UI #439

Description

@tylervick

Scope and relationship to #391

This is a sibling 4.0 workstream to #391, not a follow-up release. The migration design (docs/superpowers/specs/2026-08-10-xcresulttool-legacy-migration-design.md) already concedes the point: the report's visual contract breaks in 4.0 either way once activityType loses its data source — the five activity-type states stop being renderable no matter what the templates look like. Breaking the visual contract once, in the same major release, is cheaper than breaking it twice.

Sequencing (strict)

  • Templates stay frozen until Migrate off xcresulttool --legacy before Apple removes it #391's legacy/modern differential passes. The differential attributes every rendered diff to the reader swap; if reader and markup change together it can no longer attribute anything. No template PR merges before that gate.
  • Design exploration is not blocked. Palette, type, layout, dark-mode strategy can all be worked out concurrently on the current markup — only the merge is serialized behind the differential.
  • HTMLTemplates.swift claims to be generated, but the generator was deleted in #295 #434 must be resolved before any markup is written. Sources/XCTestHTMLReportCore/Classes/HTMLTemplates.swift is the real, hand-maintained source despite its stale "autogenerated — DO NOT EDIT" header; Sources/XCTestHTMLReportCore/HTML/*.html is a dead copy excluded from the build and 28 diff hunks behind. Anyone who starts the redesign from HTML/ is editing markup that does not ship. Whichever of HTMLTemplates.swift claims to be generated, but the generator was deleted in #295 #434's options is chosen, it has to land first so the redesign has exactly one source of truth (and one the linters actually see).

The structural problem

The stylesheet is 585 lines with zero custom properties, zero @media queries, no dark mode, no grid; all 16 colours are hardcoded hex literals (plus bare white/red), the font stack pins "SF Pro Display" with no system-ui and no generic fallback, and sizing is 99 fixed-px values with a single em in the whole sheet. There is no token layer, so every visual refresh is a scattered find-and-replace across literals — which is why there has been none since 2020.

A 2026-08-12 visual audit (rendered from the three fixture bundles next to Xcode 26's report on the same TestResults.xcresult) confirmed all of the above empirically — with the OS in dark mode, the report renders identical light — and catalogued the user-facing consequences:

  • No run summary of any kind: no counts visualization, no failure digest, no run date/duration/OS headline. The overview is just the tree. Xcode leads with a donut, per-device bars, a failure digest with messages, and build/runtime warnings.
  • A fixed three-pane layout spends ~200px on a device sidebar (even for single-device runs) and ~28% of the window on a right pane that almost always says "No Selected Attachment".
  • No <meta viewport> and fixed-px panes make small screens unusable: at 375px the sidebar eats over half the viewport and test rows clip mid-word.
  • Mixed-outcome (1 failed, 1 succeeded) and unknown-state tests render a blank status cell — only pass/fail/skip have icons.
  • Raw internals leak into the UI (kXCTAttachmentScreenRecording as an attachment label); the selected video keeps playing in the right pane after switching to the Logs tab.
  • The default linking-mode output silently breaks if moved anywhere: markup references TestResults.xcresult/<file>, so a report rendered with -o <elsewhere> has every attachment 404 with no warning — and the export mutates the source bundle. Inline mode (-i) is the true single-file artifact, at 8.6MB for the 54-second sample run.
  • Still worth protecting: the single-artifact shareability (our reason to exist), whole-run-in-one-tree visibility (Xcode hides passed tests behind filter dropdowns), compact retry/iteration rendering, JUnit export, instant client-side filters.

New data the redesign can act on once #391 lands

  • expectedFailure is now a named status in the ParsedResult model, though the renderer still flattens it — the redesign can surface it.
  • Swift Testing parameterized-test arguments exist in the port (ParsedTestCase.arguments, empty on legacy); the current templates have no slot for them.
  • Per-activity durations and the five activityType states are gone — activities carry start ordering and isFailure only. Xcode's own report shows elapsed offsets rather than per-activity durations, so this is a design opportunity, not just a loss.

Non-goal

This issue deliberately does not propose a visual direction (palette, type, layout, dark-mode approach). That exploration is unstarted on purpose and is the maintainer's call.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions