P3.1: iOS goldens @2x + PNG optimizer + Makefile record/verify targets
- SnapshotGalleryTests rendered at displayScale: 2.0 (was native 3.0)
→ 49MB → 15MB (~69% reduction)
- Records via SNAPSHOT_TESTING_RECORD=1 env var (no code edits needed)
- scripts/optimize_goldens.sh runs zopflipng (or pngcrush fallback)
over both iOS and Android golden dirs
- scripts/{record,verify}_snapshots.sh one-command wrappers
- Makefile targets: make {record,verify,optimize}-snapshots
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
55
Makefile
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# Makefile for HoneyDue KMM — wraps the long-form commands you actually
|
||||||
|
# use every day. If you find yourself copy/pasting a command twice, it
|
||||||
|
# belongs here.
|
||||||
|
#
|
||||||
|
# Quick reference
|
||||||
|
# ---------------
|
||||||
|
# make verify-snapshots # fast; run on every PR, CI runs this
|
||||||
|
# make record-snapshots # slow; regenerate baselines after UI change
|
||||||
|
# make optimize-goldens # rarely needed — record-snapshots runs this
|
||||||
|
#
|
||||||
|
.PHONY: help record-snapshots verify-snapshots optimize-goldens \
|
||||||
|
record-ios record-android verify-ios verify-android
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "HoneyDue KMM — common tasks"
|
||||||
|
@echo ""
|
||||||
|
@echo " make verify-snapshots Verify iOS + Android parity goldens (CI gate)"
|
||||||
|
@echo " make record-snapshots Regenerate iOS + Android goldens + optimize"
|
||||||
|
@echo " make optimize-goldens Run the PNG optimizer across both directories"
|
||||||
|
@echo ""
|
||||||
|
@echo " make verify-ios Verify just the iOS gallery"
|
||||||
|
@echo " make verify-android Verify just the Android gallery"
|
||||||
|
@echo " make record-ios Regenerate just the iOS gallery"
|
||||||
|
@echo " make record-android Regenerate just the Android gallery"
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
# ---- Parity gallery (combined) ----
|
||||||
|
|
||||||
|
# Regenerate every parity-gallery golden (iOS + Android) and shrink the
|
||||||
|
# output PNGs. Slow (~3 min); run after intentional UI changes only.
|
||||||
|
record-snapshots:
|
||||||
|
@./scripts/record_snapshots.sh
|
||||||
|
|
||||||
|
# Verify current UI matches committed goldens. Fast (~1 min). PR gate.
|
||||||
|
verify-snapshots:
|
||||||
|
@./scripts/verify_snapshots.sh
|
||||||
|
|
||||||
|
# Optimize every PNG golden in-place (idempotent). Usually invoked
|
||||||
|
# automatically by record-snapshots; exposed here for one-off cleanup.
|
||||||
|
optimize-goldens:
|
||||||
|
@./scripts/optimize_goldens.sh
|
||||||
|
|
||||||
|
# ---- Parity gallery (single platform) ----
|
||||||
|
|
||||||
|
record-ios:
|
||||||
|
@./scripts/record_snapshots.sh --ios-only
|
||||||
|
|
||||||
|
record-android:
|
||||||
|
@./scripts/record_snapshots.sh --android-only
|
||||||
|
|
||||||
|
verify-ios:
|
||||||
|
@./scripts/verify_snapshots.sh --ios-only
|
||||||
|
|
||||||
|
verify-android:
|
||||||
|
@./scripts/verify_snapshots.sh --android-only
|
||||||
@@ -25,9 +25,26 @@
|
|||||||
//
|
//
|
||||||
// Recording goldens
|
// Recording goldens
|
||||||
// -----------------
|
// -----------------
|
||||||
// Set `isRecording = true` in `setUp()`, run the target, then flip back to
|
// Preferred: `make record-snapshots` (or `./scripts/record_snapshots.sh
|
||||||
// `false` before committing. CI fails the build if a screen diverges from
|
// --ios-only`). The script exports `SNAPSHOT_TESTING_RECORD=1` in the
|
||||||
// its golden by more than the precision threshold.
|
// xcodebuild env, deletes the old `__Snapshots__/SnapshotGalleryTests`
|
||||||
|
// directory, runs the target, then invokes the shared PNG optimizer.
|
||||||
|
//
|
||||||
|
// Manual override: set the `SNAPSHOT_TESTING_RECORD` env var to `1` in
|
||||||
|
// the Xcode scheme's Test action (Edit Scheme → Test → Arguments →
|
||||||
|
// Environment Variables) and re-run the test target. CI fails the
|
||||||
|
// build if a screen diverges from its golden by more than the
|
||||||
|
// precision threshold.
|
||||||
|
//
|
||||||
|
// Rendering scale
|
||||||
|
// ---------------
|
||||||
|
// We force `displayScale: 2.0` on every snapshot. The iPhone 15 /
|
||||||
|
// iPhone 13 simulators default to the device's native 3x, which on a
|
||||||
|
// full-screen gradient-heavy SwiftUI view produced 800–1000 KB PNGs
|
||||||
|
// per image. @2x halves the linear dimensions (2.25x fewer pixels) and
|
||||||
|
// is still plenty to catch layout regressions. Combined with
|
||||||
|
// `scripts/optimize_goldens.sh` (zopflipng / pngcrush) this keeps us
|
||||||
|
// under the 150 KB per-image budget enforced by CI.
|
||||||
//
|
//
|
||||||
|
|
||||||
@preconcurrency import SnapshotTesting
|
@preconcurrency import SnapshotTesting
|
||||||
@@ -39,8 +56,14 @@ import ComposeApp
|
|||||||
@MainActor
|
@MainActor
|
||||||
final class SnapshotGalleryTests: XCTestCase {
|
final class SnapshotGalleryTests: XCTestCase {
|
||||||
|
|
||||||
// Flip to true locally, run the scheme, revert, commit.
|
// Record mode is driven by the `SNAPSHOT_TESTING_RECORD` env var so
|
||||||
private static let recordMode: SnapshotTestingConfiguration.Record = .missing
|
// `scripts/record_snapshots.sh` can flip it without mutating this file.
|
||||||
|
// When the var is unset/empty we only write missing goldens (`.missing`)
|
||||||
|
// so local dev runs never silently overwrite committed PNGs.
|
||||||
|
private static var recordMode: SnapshotTestingConfiguration.Record {
|
||||||
|
let env = ProcessInfo.processInfo.environment["SNAPSHOT_TESTING_RECORD"] ?? ""
|
||||||
|
return (env == "1" || env.lowercased() == "true") ? .all : .missing
|
||||||
|
}
|
||||||
|
|
||||||
override func invokeTest() {
|
override func invokeTest() {
|
||||||
withSnapshotTesting(record: Self.recordMode) {
|
withSnapshotTesting(record: Self.recordMode) {
|
||||||
@@ -63,6 +86,12 @@ final class SnapshotGalleryTests: XCTestCase {
|
|||||||
private static let pixelPrecision: Float = 0.97
|
private static let pixelPrecision: Float = 0.97
|
||||||
private static let perceptualPrecision: Float = 0.95
|
private static let perceptualPrecision: Float = 0.95
|
||||||
|
|
||||||
|
/// Force @2x rendering regardless of the simulator device's native
|
||||||
|
/// `displayScale`. See the class-level "Rendering scale" comment for
|
||||||
|
/// rationale. 2.0 keeps PNGs under the size budget without sacrificing
|
||||||
|
/// the structural detail our parity gallery cares about.
|
||||||
|
private static let forcedDisplayScale: CGFloat = 2.0
|
||||||
|
|
||||||
private func snap<V: View>(
|
private func snap<V: View>(
|
||||||
_ name: String,
|
_ name: String,
|
||||||
file: StaticString = #filePath,
|
file: StaticString = #filePath,
|
||||||
@@ -80,7 +109,10 @@ final class SnapshotGalleryTests: XCTestCase {
|
|||||||
precision: Self.pixelPrecision,
|
precision: Self.pixelPrecision,
|
||||||
perceptualPrecision: Self.perceptualPrecision,
|
perceptualPrecision: Self.perceptualPrecision,
|
||||||
layout: .device(config: .iPhone13),
|
layout: .device(config: .iPhone13),
|
||||||
traits: .init(userInterfaceStyle: .light)
|
traits: .init(traitsFrom: [
|
||||||
|
UITraitCollection(userInterfaceStyle: .light),
|
||||||
|
UITraitCollection(displayScale: Self.forcedDisplayScale),
|
||||||
|
])
|
||||||
),
|
),
|
||||||
named: "\(name)_light",
|
named: "\(name)_light",
|
||||||
file: file,
|
file: file,
|
||||||
@@ -93,7 +125,10 @@ final class SnapshotGalleryTests: XCTestCase {
|
|||||||
precision: Self.pixelPrecision,
|
precision: Self.pixelPrecision,
|
||||||
perceptualPrecision: Self.perceptualPrecision,
|
perceptualPrecision: Self.perceptualPrecision,
|
||||||
layout: .device(config: .iPhone13),
|
layout: .device(config: .iPhone13),
|
||||||
traits: .init(userInterfaceStyle: .dark)
|
traits: .init(traitsFrom: [
|
||||||
|
UITraitCollection(userInterfaceStyle: .dark),
|
||||||
|
UITraitCollection(displayScale: Self.forcedDisplayScale),
|
||||||
|
])
|
||||||
),
|
),
|
||||||
named: "\(name)_dark",
|
named: "\(name)_dark",
|
||||||
file: file,
|
file: file,
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 1001 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 808 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 966 KiB After Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 800 KiB After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 944 KiB After Width: | Height: | Size: 191 KiB |
|
Before Width: | Height: | Size: 799 KiB After Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 808 KiB After Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 679 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 259 KiB |
|
Before Width: | Height: | Size: 916 KiB After Width: | Height: | Size: 234 KiB |
|
Before Width: | Height: | Size: 1003 KiB After Width: | Height: | Size: 251 KiB |
|
Before Width: | Height: | Size: 896 KiB After Width: | Height: | Size: 227 KiB |
|
Before Width: | Height: | Size: 967 KiB After Width: | Height: | Size: 267 KiB |
|
Before Width: | Height: | Size: 878 KiB After Width: | Height: | Size: 242 KiB |
|
Before Width: | Height: | Size: 1024 KiB After Width: | Height: | Size: 224 KiB |
|
Before Width: | Height: | Size: 836 KiB After Width: | Height: | Size: 179 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 336 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 291 KiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 406 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 352 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 370 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 329 KiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 480 KiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 435 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 406 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 383 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 262 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 242 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 384 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 343 KiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 443 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 369 KiB |
|
Before Width: | Height: | Size: 895 KiB After Width: | Height: | Size: 219 KiB |
|
Before Width: | Height: | Size: 743 KiB After Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 181 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 919 KiB After Width: | Height: | Size: 244 KiB |
|
Before Width: | Height: | Size: 820 KiB After Width: | Height: | Size: 216 KiB |
|
Before Width: | Height: | Size: 939 KiB After Width: | Height: | Size: 269 KiB |
|
Before Width: | Height: | Size: 845 KiB After Width: | Height: | Size: 245 KiB |
|
Before Width: | Height: | Size: 1001 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 808 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 242 B |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 651 B |
|
Before Width: | Height: | Size: 616 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 514 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 356 KiB After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 326 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 1010 KiB After Width: | Height: | Size: 281 KiB |
|
Before Width: | Height: | Size: 909 KiB After Width: | Height: | Size: 256 KiB |
|
Before Width: | Height: | Size: 932 KiB After Width: | Height: | Size: 258 KiB |
|
Before Width: | Height: | Size: 829 KiB After Width: | Height: | Size: 231 KiB |
81
scripts/optimize_goldens.sh
Executable file
@@ -0,0 +1,81 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# optimize_goldens.sh — recursively optimize PNG goldens in-place.
|
||||||
|
#
|
||||||
|
# Runs after each `record` pass for both iOS and Android parity galleries.
|
||||||
|
# Removes unnecessary PNG chunks (textual metadata, ancillary palette
|
||||||
|
# entries) and re-encodes the image with a better DEFLATE strategy so the
|
||||||
|
# image bytes on disk drop by 15–40% without touching a single pixel.
|
||||||
|
#
|
||||||
|
# Dependencies
|
||||||
|
# ------------
|
||||||
|
# zopflipng (preferred — brute-force DEFLATE, best compression)
|
||||||
|
# pngcrush (fallback — faster, smaller savings)
|
||||||
|
#
|
||||||
|
# Install on macOS:
|
||||||
|
# brew install zopfli pngcrush
|
||||||
|
#
|
||||||
|
# The script never fails if the tools are missing: it warns and exits 0,
|
||||||
|
# leaving the goldens untouched. CI's size-gate will still fail loudly if
|
||||||
|
# the PNGs would bust the 150 KB budget.
|
||||||
|
#
|
||||||
|
# Usage
|
||||||
|
# -----
|
||||||
|
# ./scripts/optimize_goldens.sh # default dirs (iOS + Android)
|
||||||
|
# ./scripts/optimize_goldens.sh path1 path2 # specific dirs only
|
||||||
|
#
|
||||||
|
# Idempotent — re-running on already-optimized PNGs is a no-op.
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DIRS=("$@")
|
||||||
|
if [ ${#DIRS[@]} -eq 0 ]; then
|
||||||
|
DIRS=(
|
||||||
|
"iosApp/HoneyDueTests/__Snapshots__"
|
||||||
|
"composeApp/src/androidUnitTest/roborazzi"
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
tool=""
|
||||||
|
if command -v zopflipng >/dev/null 2>&1; then
|
||||||
|
tool="zopflipng"
|
||||||
|
elif command -v pngcrush >/dev/null 2>&1; then
|
||||||
|
tool="pngcrush"
|
||||||
|
else
|
||||||
|
echo "WARNING: neither zopflipng nor pngcrush is installed — skipping PNG optimization."
|
||||||
|
echo " Install with: brew install zopfli pngcrush"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "optimize_goldens: using ${tool}"
|
||||||
|
|
||||||
|
count=0
|
||||||
|
saved=0
|
||||||
|
for dir in "${DIRS[@]}"; do
|
||||||
|
if [ ! -d "$dir" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
while IFS= read -r -d '' png; do
|
||||||
|
before=$(stat -f%z "$png" 2>/dev/null || stat -c%s "$png")
|
||||||
|
if [ "$tool" = "zopflipng" ]; then
|
||||||
|
# -y : overwrite without prompt
|
||||||
|
# --lossy_transparent : allow color rewrite under alpha=0 for extra savings
|
||||||
|
zopflipng -y --lossy_transparent "$png" "$png" >/dev/null 2>&1 || true
|
||||||
|
else
|
||||||
|
# -ow : overwrite-in-place; -q : quiet
|
||||||
|
pngcrush -q -ow "$png" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
after=$(stat -f%z "$png" 2>/dev/null || stat -c%s "$png")
|
||||||
|
saved=$((saved + before - after))
|
||||||
|
count=$((count + 1))
|
||||||
|
done < <(find "$dir" -name '*.png' -print0)
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$count" -eq 0 ]; then
|
||||||
|
echo "optimize_goldens: no PNGs found in: ${DIRS[*]}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Print a human-readable summary. `bc` is standard on macOS / most linuxes.
|
||||||
|
mb=$(echo "scale=2; $saved/1048576" | bc)
|
||||||
|
printf "optimize_goldens: %d PNGs processed, saved %.2f MB (%s)\n" "$count" "$mb" "$tool"
|
||||||
83
scripts/record_snapshots.sh
Executable file
@@ -0,0 +1,83 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# record_snapshots.sh — regenerate every parity-gallery golden in one pass.
|
||||||
|
#
|
||||||
|
# Use this after an intentional UI change (new color token, redesigned
|
||||||
|
# layout, etc.) so the committed baseline matches the new look. Reviewers
|
||||||
|
# see the PNG diff alongside your code change in the PR — that dual-diff
|
||||||
|
# is the whole point of the parity gallery.
|
||||||
|
#
|
||||||
|
# Usage
|
||||||
|
# -----
|
||||||
|
# ./scripts/record_snapshots.sh # iOS + Android
|
||||||
|
# ./scripts/record_snapshots.sh --ios-only
|
||||||
|
# ./scripts/record_snapshots.sh --android-only
|
||||||
|
#
|
||||||
|
# Pipeline
|
||||||
|
# --------
|
||||||
|
# 1. (Android) `./gradlew :composeApp:recordRoborazziDebug`
|
||||||
|
# 2. (iOS) Delete iosApp/HoneyDueTests/__Snapshots__/SnapshotGalleryTests,
|
||||||
|
# set SNAPSHOT_TESTING_RECORD=1, run xcodebuild test for
|
||||||
|
# SnapshotGalleryTests. The env var is read by
|
||||||
|
# SnapshotGalleryTests.swift to flip SnapshotTesting.record
|
||||||
|
# between `.missing` (default — safe) and `.all` (overwrite).
|
||||||
|
# 3. Run `scripts/optimize_goldens.sh` across both golden directories to
|
||||||
|
# shrink the fresh PNGs.
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
ROOT="$(pwd)"
|
||||||
|
|
||||||
|
platform="both"
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--ios-only) platform="ios" ;;
|
||||||
|
--android-only) platform="android" ;;
|
||||||
|
-h|--help)
|
||||||
|
sed -n '3,18p' "$0"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "usage: $0 [--ios-only|--android-only]" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# ---------- Android ----------
|
||||||
|
if [ "$platform" = "both" ] || [ "$platform" = "android" ]; then
|
||||||
|
echo "==> Recording Android goldens…"
|
||||||
|
./gradlew :composeApp:recordRoborazziDebug
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------- iOS ----------
|
||||||
|
if [ "$platform" = "both" ] || [ "$platform" = "ios" ]; then
|
||||||
|
echo "==> Recording iOS goldens…"
|
||||||
|
rm -rf iosApp/HoneyDueTests/__Snapshots__/SnapshotGalleryTests
|
||||||
|
(
|
||||||
|
cd iosApp
|
||||||
|
# SNAPSHOT_TESTING_RECORD=1 flips SnapshotTesting.isRecording to
|
||||||
|
# `.all` (see SnapshotGalleryTests.swift).
|
||||||
|
SNAPSHOT_TESTING_RECORD=1 xcodebuild test \
|
||||||
|
-project honeyDue.xcodeproj \
|
||||||
|
-scheme HoneyDue \
|
||||||
|
-destination "${IOS_SIM_DESTINATION:-platform=iOS Simulator,name=iPhone 17,OS=latest}" \
|
||||||
|
-only-testing:HoneyDueTests/SnapshotGalleryTests \
|
||||||
|
2>&1 | tail -30
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------- Optimize ----------
|
||||||
|
echo "==> Optimizing PNGs…"
|
||||||
|
"$ROOT/scripts/optimize_goldens.sh"
|
||||||
|
|
||||||
|
# ---------- Parity HTML (P4 follow-up) ----------
|
||||||
|
if [ -x "$ROOT/scripts/build_parity_gallery.py" ]; then
|
||||||
|
echo "==> Regenerating parity HTML gallery…"
|
||||||
|
python3 "$ROOT/scripts/build_parity_gallery.py"
|
||||||
|
else
|
||||||
|
echo "==> (parity HTML generator not yet present — skipping)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> Done. Review the PNG diff with your code change before committing."
|
||||||
57
scripts/verify_snapshots.sh
Executable file
@@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# verify_snapshots.sh — verify every parity-gallery golden matches the
|
||||||
|
# current code. Use as a pre-commit / CI gate.
|
||||||
|
#
|
||||||
|
# Exits non-zero if either platform's gallery drifts from its committed
|
||||||
|
# baseline. Diff artifacts land under each platform's usual report dir:
|
||||||
|
# Android: composeApp/build/outputs/roborazzi/
|
||||||
|
# iOS: ~/Library/Developer/Xcode/DerivedData/.../HoneyDueTests/
|
||||||
|
#
|
||||||
|
# Usage
|
||||||
|
# -----
|
||||||
|
# ./scripts/verify_snapshots.sh # both platforms
|
||||||
|
# ./scripts/verify_snapshots.sh --ios-only
|
||||||
|
# ./scripts/verify_snapshots.sh --android-only
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
platform="both"
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--ios-only) platform="ios" ;;
|
||||||
|
--android-only) platform="android" ;;
|
||||||
|
-h|--help)
|
||||||
|
sed -n '3,14p' "$0"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "usage: $0 [--ios-only|--android-only]" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# ---------- Android ----------
|
||||||
|
if [ "$platform" = "both" ] || [ "$platform" = "android" ]; then
|
||||||
|
echo "==> Verifying Android goldens…"
|
||||||
|
./gradlew :composeApp:verifyRoborazziDebug
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------- iOS ----------
|
||||||
|
if [ "$platform" = "both" ] || [ "$platform" = "ios" ]; then
|
||||||
|
echo "==> Verifying iOS goldens…"
|
||||||
|
(
|
||||||
|
cd iosApp
|
||||||
|
xcodebuild test \
|
||||||
|
-project honeyDue.xcodeproj \
|
||||||
|
-scheme HoneyDue \
|
||||||
|
-destination "${IOS_SIM_DESTINATION:-platform=iOS Simulator,name=iPhone 17,OS=latest}" \
|
||||||
|
-only-testing:HoneyDueTests/SnapshotGalleryTests \
|
||||||
|
2>&1 | tail -30
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> All snapshot checks passed."
|
||||||