// Renders screens with demo data to PNGs in watch/screenshots. Skipped unless WC_SCREENSHOT_DIR is // set, which xcodebuild passes through from TEST_RUNNER_WC_SCREENSHOT_DIR: // // TEST_RUNNER_WC_SCREENSHOT_DIR=$PWD/screenshots xcodebuild ... test -only-testing:WristCodeTests/SnapshotTests import ImageIO import SwiftUI import Testing import UniformTypeIdentifiers import WidgetKit @testable import WristCode private let outputDirectory = ProcessInfo.processInfo.environment["WC_SCREENSHOT_DIR"] /// The Series 12 (46mm) screen in points. private let screen = CGSize(width: 208, height: 248) @MainActor @Suite(.enabled(if: outputDirectory != nil)) struct SnapshotTests { @Test func complication() async throws { let gateway = DemoGateway() let waiting = WaitingEntry(date: .now, content: .counts(try await gateway.summary(), stale: false)) let feed = try await gateway.feed() for item in feed.needsYou { try await gateway.respond( hostId: item.hostId, threadId: item.threadId, request: .decision(requestId: item.request!.id, decision: .decline)) } let idle = WaitingEntry(date: .now, content: .counts(try await gateway.summary(), stale: false)) try write(ComplicationGallery(waiting: waiting, idle: idle), "complication.png") } @Test func multiSelectQuestion() async throws { let detail = try await DemoGateway().thread(hostId: "mac-mini", threadId: "t-release") let view = ThreadView( hostId: "mac-mini", threadId: "t-release", detail: detail, selections: ["checks": ["unit", "lint"]], scrolls: false) .environment(AppModel.preview()) .fontDesign(.rounded) .tint(Theme.work) try write(view, "thread-multiselect.png") } /// Renders at the screen's width and as tall as the content, as if scrolled through. private func write(_ view: some View, _ name: String) throws { let renderer = ImageRenderer(content: view .frame(width: screen.width) .fixedSize(horizontal: false, vertical: true) .padding(.bottom, 8) .background(.black) .environment(\.colorScheme, .dark)) renderer.scale = 2 let image = try #require(renderer.cgImage) let url = URL(fileURLWithPath: try #require(outputDirectory)).appending(path: name) let destination = try #require(CGImageDestinationCreateWithURL(url as CFURL, UTType.png.identifier as CFString, 1, nil)) CGImageDestinationAddImage(destination, image, nil) #expect(CGImageDestinationFinalize(destination)) } } /// Every family at its 46mm size, once with threads waiting and once with nothing waiting. private struct ComplicationGallery: View { var waiting: WaitingEntry var idle: WaitingEntry var body: some View { VStack(alignment: .leading, spacing: 10) { section("3 waiting", waiting) section("Nothing waiting", idle) } .padding(.horizontal, 10) .padding(.top, 8) .foregroundStyle(.white) } private func section(_ title: String, _ entry: WaitingEntry) -> some View { VStack(alignment: .leading, spacing: 6) { Text(title.uppercased()) .font(.system(size: 10, weight: .semibold, design: .rounded)) .foregroundStyle(Theme.done) WaitingView(entry: entry, familyOverride: .accessoryInline) .font(.system(size: 13, weight: .semibold, design: .rounded)) .frame(height: 18) WaitingView(entry: entry, familyOverride: .accessoryRectangular) .frame(width: 184, height: 72) HStack(spacing: 16) { // Outside a face AccessoryWidgetBackground fills a square; a face clips it round. WaitingView(entry: entry, familyOverride: .accessoryCircular) .frame(width: 50, height: 50) .clipShape(Circle()) WaitingView(entry: entry, familyOverride: .accessoryCorner) .frame(width: 50, height: 50) } } } }