Skip to content

Add macOS snapshot test support - #168

Merged
BarredEwe merged 13 commits into
mainfrom
codex/macos-snapshot-tests
Sep 1, 2026
Merged

Add macOS snapshot test support#168
BarredEwe merged 13 commits into
mainfrom
codex/macos-snapshot-tests

Conversation

@BarredEwe

@BarredEwe BarredEwe commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Summary

Adds macOS 13+ snapshot-test support for SwiftUI, NSView, and NSViewController, without changing iOS behaviour.

Problem

Package.swift already declared macOS support, but the library imported UIKit unconditionally. This prevented the package from compiling on macOS, and generated snapshot tests contained UIKit, simulator, and iOS-device assumptions.

Changes

macOS support

  • AppKit wrappers and PreviewModel / PrefireSnapshot overloads for NSView and NSViewController.
  • SwiftUI is hosted in an off-screen NSWindow; SnapshotTesting's NSView.image strategy captures the image.
  • .fixedLayout(width:height:) and .previewLayout(.fixed(width:height:)) pin the macOS canvas. Applied on macOS only — honouring them on iOS would resize every already recorded reference image.
  • The backing scale factor is pinned (2 by default, configurable via DeviceConfig(size:scale:)) instead of being inherited from the current display, so a snapshot recorded on a Retina Mac matches one verified on CI.
  • An oversized preview is clamped to the 4096 pt canvas rather than collapsing to a 1x1 image that would silently pass forever.
  • The host window is shared, so a suite does not leak one NSWindow plus a full SwiftUI tree per snapshot.

Cleanups

  • PrefireNativeView / PrefireNativeViewController collapse the duplicated per-platform initialisers in PrefireSnapshot and PreviewModel; the iOS-only traits: variants remain as overloads, so existing call sites keep compiling.
  • The template emits a single verifySnapshot call — the platform only selects the Snapshotting strategy.
  • traitArguments(from:) is shared by trait and fixed-layout parsing; the three fixed-layout stencil keys collapse into one fixedLayoutSize.
  • DeviceConfig moves out of #if canImport(XCTest): PreviewModel stores one unconditionally, so the gate made the library fail to compile wherever XCTest is not importable.

Build

  • No new package dependencies. swift-snapshot-testing is not required by this package.
  • make build no longer pins OS=latest, which resolves against the selected Xcode's SDK version and fails when that exact version is not installed. The destination is generic and overridable via DESTINATION=.

Notes

NSViewRepresentable and NSViewControllerRepresentable are SwiftUI protocol names, so the public wrappers reuse the iOS names ViewRepresentable and ViewControllerRepresentable.

macOS supports snapshot tests only — PlaybookView remains iOS-only, and the README says so.

Validation

  • swift test (root package) — 17 tests
  • cd PrefireExecutable && swift test — 56 tests
  • End-to-end: tests generated by the bundled binary compile, record, and re-verify in a macOS SwiftPM package
  • swiftc -typecheck of Sources/Prefire for iOS, tvOS and macOS — clean
  • Bundled CLI binary rebuilt with make binary

@BarredEwe BarredEwe changed the title [codex] Add macOS snapshot test support Add macOS snapshot test support Jul 24, 2026

@BarredEwe BarredEwe left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Earlier review items are addressed in later commits on this branch. See the resolved threads for the template, wrapper names, window host, PreviewProvider layout, docs, and image assertions.

@BarredEwe
BarredEwe force-pushed the codex/macos-snapshot-tests branch 3 times, most recently from 75fb3f0 to 18fb4e2 Compare August 31, 2026 09:15
Restore a compiling PrefireProvider template, reuse ViewRepresentable
names on AppKit, map PreviewLayout.fixed into DeviceConfig, and host
SwiftUI in an NSWindow so SnapshotTesting's NSView.image path can run.
orderFrontRegardless was flashing a window for every snapshot. The
hosting NSWindow stays attached for layout, but is never ordered front.
@BarredEwe
BarredEwe force-pushed the codex/macos-snapshot-tests branch from 18fb4e2 to 1e1c8ab Compare August 31, 2026 18:34
@BarredEwe
BarredEwe marked this pull request as ready for review August 31, 2026 18:35
Three issues in the macOS hosting path:

- Content larger than the 4096 pt canvas failed the size check and fell
  back to 1x1, producing an image that records and re-verifies while
  showing nothing. Clamp it instead.
- The container stored its NSWindow while the window retains its content
  view, so each snapshot kept a window and its SwiftUI tree alive. Host in
  a shared off-screen window, keyed by scale.
- backingScaleFactor came from the current display, so the same test
  recorded @2x on a Retina Mac and @1x elsewhere. Fix it at 2 by default,
  configurable through DeviceConfig(size:scale:).

.fixedLayout and .previewLayout(.fixed) now apply on macOS only; applying
them on iOS and tvOS changes the size of existing reference images.

DeviceConfig moves out of #if canImport(XCTest). PreviewModel stores one
unconditionally, so the gate broke compilation wherever XCTest cannot be
imported.

PrefireNativeView and PrefireNativeViewController replace the duplicated
per-platform initialisers. The iOS-only traits: variants remain as
overloads, so existing call sites still compile.
- Extract traitArguments(from:), shared by trait and fixed-layout parsing.
- Collapse fixedLayoutWidth, fixedLayoutHeight and fixedLayoutSize into a
  single stencil key; only fixedLayoutSize was used by the template.
- Emit one verifySnapshot call, with the platform selecting only the
  Snapshotting strategy. Drops the snapshotLayout helper and restores the
  original iOS layout expression.
- Remove test_defaultTemplateContainsMacOSSnapshotBranch: containment
  assertions over the template text, including negative ones for helpers
  that do not exist. PrefireGeneratorTests already cover the rendered
  output.

Bundled binary rebuilt with make binary.
…ation

The dependency backed a single test that built a Snapshotting value and
discarded it. Generated code is not compiled by CI on any platform, so that
test did not cover the macOS template branch either. Removed together with
Package.resolved.

Documentation:
- macOS supports snapshot tests only; PlaybookView is iOS-only.
- Every macOS preview renders at its fitting size, not just components.
- --disable-sandbox is needed for recording, because SwiftPM's sandbox
  denies writes into the package directory. Verifying works with a plain
  swift test, which CI now uses.
- Requirements listed Swift 5.6 and Xcode 14 while Package.swift declares
  swift-tools-version 6.0.

Makefile: 'generic/platform=iOS Simulator,OS=latest' resolves OS against the
selected Xcode's SDK version and fails when that version is not installed.
Drop the pin and the redundant -sdk, make the destination overridable, and
report the missing-platform case on failure.
@BarredEwe
BarredEwe force-pushed the codex/macos-snapshot-tests branch from 1e1c8ab to 437a2af Compare August 31, 2026 19:36
@BarredEwe
BarredEwe merged commit a895478 into main Sep 1, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant