diff --git a/ClaudeMeter.xcodeproj/xcshareddata/xcschemes/ClaudeMeter.xcscheme b/ClaudeMeter.xcodeproj/xcshareddata/xcschemes/ClaudeMeter.xcscheme
index b90c50c..c27f3be 100644
--- a/ClaudeMeter.xcodeproj/xcshareddata/xcschemes/ClaudeMeter.xcscheme
+++ b/ClaudeMeter.xcodeproj/xcshareddata/xcschemes/ClaudeMeter.xcscheme
@@ -27,8 +27,18 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- shouldUseLaunchSchemeArgsEnv = "YES"
+ shouldUseLaunchSchemeArgsEnv = "NO"
shouldAutocreateTestPlan = "YES">
+
+
+
+
+
+
= .image(
+ precision: 0.99,
+ perceptualPrecision: 0.90
+ )
+
func test_menuBarIcon_showsBatteryStyleWhenWarning() {
let image = renderIcon(style: .battery)
- assertSnapshot(of: image, as: .image, record: isRecording)
+ assertSnapshot(of: image, as: strategy, record: isRecording)
}
func test_menuBarIcon_showsCircularStyleWhenWarning() {
let image = renderIcon(style: .circular)
- assertSnapshot(of: image, as: .image, record: isRecording)
+ assertSnapshot(of: image, as: strategy, record: isRecording)
}
func test_menuBarIcon_showsMinimalStyleWhenWarning() {
let image = renderIcon(style: .minimal)
- assertSnapshot(of: image, as: .image, record: isRecording)
+ assertSnapshot(of: image, as: strategy, record: isRecording)
}
func test_menuBarIcon_showsSegmentsStyleWhenWarning() {
let image = renderIcon(style: .segments)
- assertSnapshot(of: image, as: .image, record: isRecording)
+ assertSnapshot(of: image, as: strategy, record: isRecording)
}
func test_menuBarIcon_showsDualBarStyleWhenWarning() {
let image = renderIcon(style: .dualBar)
- assertSnapshot(of: image, as: .image, record: isRecording)
+ assertSnapshot(of: image, as: strategy, record: isRecording)
}
func test_menuBarIcon_showsGaugeStyleWhenWarning() {
let image = renderIcon(style: .gauge)
- assertSnapshot(of: image, as: .image, record: isRecording)
+ assertSnapshot(of: image, as: strategy, record: isRecording)
}
func test_menuBarIcon_showsLoadingIndicatorInBatteryStyle() {
let image = renderIcon(style: .battery, status: .safe, isLoading: true)
- assertSnapshot(of: image, as: .image, record: isRecording)
+ assertSnapshot(of: image, as: strategy, record: isRecording)
}
func test_menuBarIcon_showsStaleIndicatorInBatteryStyle() {
let image = renderIcon(style: .battery, status: .safe, isStale: true)
- assertSnapshot(of: image, as: .image, record: isRecording)
+ assertSnapshot(of: image, as: strategy, record: isRecording)
+ }
+
+ /// Keeps the tolerance above honest: it must still reject an icon whose status colour
+ /// changed, otherwise the snapshots assert nothing.
+ func test_snapshotTolerance_rejectsChangedStatusColour() {
+ let warning = renderIcon(style: .battery, status: .warning)
+ let critical = renderIcon(style: .battery, status: .critical)
+
+ XCTAssertNotNil(
+ strategy.diffing.diff(warning, critical),
+ "Tolerance is too loose: a changed status colour compares as unchanged"
+ )
}
private func renderIcon(
diff --git a/ClaudeMeterTests/TestHostLaunchTests.swift b/ClaudeMeterTests/TestHostLaunchTests.swift
new file mode 100644
index 0000000..06a5e8f
--- /dev/null
+++ b/ClaudeMeterTests/TestHostLaunchTests.swift
@@ -0,0 +1,38 @@
+//
+// TestHostLaunchTests.swift
+// ClaudeMeterTests
+//
+
+import XCTest
+@testable import ClaudeMeter
+
+/// The test bundle is hosted by the app, so running any test launches the real app.
+/// Left alone it would bootstrap the menu bar, read the session key from the Keychain
+/// (prompting on every run, since the test binary is signed differently from the
+/// installed app) and fetch live usage over the network.
+///
+/// The scheme's test action passes `--demo safeUsage`, which routes the app to
+/// `startWithoutBootstrap()`. That keeps the workaround out of the production path,
+/// at the cost of living in the scheme where it is easy to delete by accident —
+/// hence this test.
+final class TestHostLaunchTests: XCTestCase {
+ func test_testHost_launchesWithoutBootstrappingTheMenuBar() throws {
+ let arguments = ProcessInfo.processInfo.arguments
+
+ guard let flag = arguments.firstIndex(of: "--demo") else {
+ return XCTFail(
+ """
+ The test host was launched without `--demo`, so it will bootstrap the menu bar: \
+ read the Keychain and hit the network during tests. Restore the CommandLineArguments \
+ in the scheme's TestAction (ClaudeMeter.xcscheme).
+ """
+ )
+ }
+
+ let mode = arguments[arguments.index(after: flag)]
+ XCTAssertNotNil(
+ DemoMode(rawValue: mode),
+ "`--demo \(mode)` is not a DemoMode, so the app will bootstrap normally."
+ )
+ }
+}