feat(macos): add native menu bar app with notch overlay
Introduces a SwiftUI companion app for the Fail Well PWA: menu bar status item, popover with task list / record / history views, command palette, and a Dynamic Island-style notch overlay that shows the active step and timer beside the hardware notch and expands on hover to reveal pause/next controls.
This commit is contained in:
64
macos/Tests/FailWellTests/BreakCalculatorTests.swift
Normal file
64
macos/Tests/FailWellTests/BreakCalculatorTests.swift
Normal file
@@ -0,0 +1,64 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
@testable import FailWell
|
||||
|
||||
@Suite("BreakCalculator")
|
||||
struct BreakCalculatorTests {
|
||||
@Test func passesThroughWhenNoBreakTime() {
|
||||
let record = TaskRecord(
|
||||
taskId: "t1",
|
||||
stepRecords: ["step-id-1": TimeRange(start: date("2023-04-17T18:00:00Z"))],
|
||||
breakTime: nil
|
||||
)
|
||||
#expect(BreakCalculator.applyingBreakTime(to: record) == record)
|
||||
}
|
||||
|
||||
@Test func passesThroughWhenBreakNotEnded() {
|
||||
let record = TaskRecord(
|
||||
taskId: "t1",
|
||||
stepRecords: ["step-id-1": TimeRange(start: date("2023-04-17T18:00:00Z"))],
|
||||
breakTime: TimeRange(start: date("2023-04-17T19:00:00Z"))
|
||||
)
|
||||
#expect(BreakCalculator.applyingBreakTime(to: record) == record)
|
||||
}
|
||||
|
||||
@Test func shiftsUnfinishedStepStartsByBreakDuration() {
|
||||
let record = TaskRecord(
|
||||
taskId: "t1",
|
||||
stepRecords: ["step-id-1": TimeRange(start: date("2023-04-17T18:00:00Z"))],
|
||||
breakTime: TimeRange(
|
||||
start: date("2023-04-17T19:00:00Z"),
|
||||
end: date("2023-04-17T20:00:00Z")
|
||||
)
|
||||
)
|
||||
let result = BreakCalculator.applyingBreakTime(to: record)
|
||||
#expect(result.stepRecords["step-id-1"]?.start == date("2023-04-17T19:00:00Z"))
|
||||
}
|
||||
|
||||
@Test func doesNotShiftFinishedStepRecords() {
|
||||
let record = TaskRecord(
|
||||
taskId: "t1",
|
||||
stepRecords: [
|
||||
"step-id-1": TimeRange(
|
||||
start: date("2023-04-17T17:00:00Z"),
|
||||
end: date("2023-04-17T18:00:00Z")
|
||||
),
|
||||
"step-id-2": TimeRange(start: date("2023-04-17T18:00:00Z"))
|
||||
],
|
||||
breakTime: TimeRange(
|
||||
start: date("2023-04-17T19:00:00Z"),
|
||||
end: date("2023-04-17T20:00:00Z")
|
||||
)
|
||||
)
|
||||
let result = BreakCalculator.applyingBreakTime(to: record)
|
||||
#expect(result.stepRecords["step-id-1"]?.start == date("2023-04-17T17:00:00Z"))
|
||||
#expect(result.stepRecords["step-id-1"]?.end == date("2023-04-17T18:00:00Z"))
|
||||
#expect(result.stepRecords["step-id-2"]?.start == date("2023-04-17T19:00:00Z"))
|
||||
}
|
||||
|
||||
private func date(_ iso: String) -> Date {
|
||||
let f = ISO8601DateFormatter()
|
||||
f.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
||||
return f.date(from: iso) ?? ISO8601DateFormatter().date(from: iso)!
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user