From 83058883705fb7d99125fb65fd161102ad05f9db Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 12 Nov 2026 08:29:09 +0000 Subject: [PATCH 1/2] test: calendar timezone hint regressions after #5232 and #5235 Lock Mac hint re-anchor and hidden-primary selection, web queryFn hint sync, and empty-cache today behavior. Co-authored-by: Tyler Dane --- .../CompassData/NativeCalendarRootModel.swift | 12 +++ ...lendarRootModelCalendarHintSyncTests.swift | 90 +++++++++++++++++++ .../src/calendars/calendar.query.test.ts | 56 ++++++++++++ apps/calendar-web/src/routers/loaders.test.ts | 8 ++ 4 files changed, 166 insertions(+) create mode 100644 apps/calendar-macos/CompassData/Tests/CompassDataTests/NativeCalendarRootModelCalendarHintSyncTests.swift create mode 100644 apps/calendar-web/src/calendars/calendar.query.test.ts diff --git a/apps/calendar-macos/CompassData/Sources/CompassData/NativeCalendarRootModel.swift b/apps/calendar-macos/CompassData/Sources/CompassData/NativeCalendarRootModel.swift index 57fdb7d3e..4d2bbf688 100644 --- a/apps/calendar-macos/CompassData/Sources/CompassData/NativeCalendarRootModel.swift +++ b/apps/calendar-macos/CompassData/Sources/CompassData/NativeCalendarRootModel.swift @@ -1190,3 +1190,15 @@ public final class NativeCalendarRootModel { } } } + +extension NativeCalendarRootModel { + /// Tests drive calendar-hint sync without mocking the remote list API. + internal func applyCalendarsForPresentationTesting(_ updated: [CompassCalendar]) { + calendars = updated + rebuildPresentation() + } + + internal var testingVisibleRangeRefreshIsScheduled: Bool { + refreshTask != nil + } +} diff --git a/apps/calendar-macos/CompassData/Tests/CompassDataTests/NativeCalendarRootModelCalendarHintSyncTests.swift b/apps/calendar-macos/CompassData/Tests/CompassDataTests/NativeCalendarRootModelCalendarHintSyncTests.swift new file mode 100644 index 000000000..6cd043110 --- /dev/null +++ b/apps/calendar-macos/CompassData/Tests/CompassDataTests/NativeCalendarRootModelCalendarHintSyncTests.swift @@ -0,0 +1,90 @@ +import CompassData +import CompassKit +import XCTest + +@MainActor +final class NativeCalendarRootModelCalendarHintSyncTests: XCTestCase { + func testCalendarHintChangeReanchorsWeekAndSchedulesRangeRefresh() async throws { + let priorDefault = NSTimeZone.default + defer { NSTimeZone.default = priorDefault } + NSTimeZone.default = try XCTUnwrap(TimeZone(identifier: "UTC")) + + let referenceNow = try XCTUnwrap( + CompassDateParsing.parseGridDateTime( + "2026-11-12T00:23:00.000", + timeZoneIdentifier: "UTC" + ) + ) + let environment = try NativeCalendarEnvironment(inMemoryDatabase: true) + let model = NativeCalendarRootModel(environment: environment, now: { referenceNow }) + model.viewStore.setCalendarTimeZoneHint("America/New_York") + + let nycOnly = [ + compassCalendar(id: "nyc", name: "NYC", timeZone: "America/New_York", isPrimary: true, isVisible: true), + ] + model.applyCalendarsForPresentationTesting(nycOnly) + let anchorInNewYork = model.viewStore.anchorDate + XCTAssertEqual(EffectiveTimeZone.identifier, "America/New_York") + + let hiddenDenverPrimary = [ + compassCalendar( + id: "denver-primary", name: "Primary", timeZone: "America/Denver", isPrimary: true, isVisible: false + ), + compassCalendar(id: "nyc", name: "NYC", timeZone: "America/New_York", isPrimary: false, isVisible: true), + ] + model.applyCalendarsForPresentationTesting(hiddenDenverPrimary) + + XCTAssertEqual(EffectiveTimeZone.identifier, "America/Denver") + XCTAssertNotEqual(model.viewStore.anchorDate, anchorInNewYork) + let expected = CalendarWindowMath.sundayWeekViewStart(containing: referenceNow) + XCTAssertEqual(model.viewStore.anchorDate, expected) + XCTAssertTrue(model.testingVisibleRangeRefreshIsScheduled) + } + + func testHiddenPrimaryDenverWinsOverVisibleNewYorkOnFullList() async throws { + let environment = try NativeCalendarEnvironment(inMemoryDatabase: true) + let model = NativeCalendarRootModel(environment: environment) + model.applyCalendarsForPresentationTesting([ + compassCalendar( + id: "denver-primary", name: "Primary", timeZone: "America/Denver", isPrimary: true, isVisible: false + ), + compassCalendar(id: "nyc", name: "NYC", timeZone: "America/New_York", isPrimary: false, isVisible: true), + ]) + XCTAssertEqual(EffectiveTimeZone.identifier, "America/Denver") + } + + private func compassCalendar( + id: String, + name: String, + timeZone: String, + isPrimary: Bool, + isVisible: Bool + ) -> CompassCalendar { + let capabilities = CompassCalendarCapabilities( + canInviteAttendees: false, + canManage: false, + canReadAvailability: true, + canReadDetails: true, + canWatchEvents: false, + canWrite: true, + conferenceKinds: [] + ) + return CompassCalendar( + access: .owner, + accountEmail: "a@example.com", + backgroundColor: "#ffffff", + capabilities: capabilities, + conference: nil, + createsGoogleMeet: false, + description: "", + foregroundColor: "#000000", + id: id, + isActive: true, + isPrimary: isPrimary, + isVisible: isVisible, + name: name, + provider: "google", + timeZone: IANATimeZone(rawValue: timeZone) + ) + } +} diff --git a/apps/calendar-web/src/calendars/calendar.query.test.ts b/apps/calendar-web/src/calendars/calendar.query.test.ts new file mode 100644 index 000000000..59d5cec15 --- /dev/null +++ b/apps/calendar-web/src/calendars/calendar.query.test.ts @@ -0,0 +1,56 @@ +import { TimeZoneSchema } from "@core/types/domain-primitives"; +import { createMockCalendar } from "@web/__tests__/utils/factories/calendar.factory"; +import { CalendarApi } from "@web/api/calendar.api"; +import { calendarsQueryOptions } from "@web/calendars/calendar.query"; +import * as calendarTimeZoneHint from "@web/timezone/calendar-timezone-hint"; +import { + resetEffectiveTimeZoneStoreForTests, + setBrowserTimeZoneForTests, +} from "@web/timezone/effective-timezone.store"; +import { afterEach, describe, expect, it, spyOn } from "bun:test"; + +describe("calendarsQueryOptions", () => { + afterEach(() => { + resetEffectiveTimeZoneStoreForTests(); + }); + + it("syncs the calendar timezone hint when the remote list resolves", async () => { + const calendars = [ + createMockCalendar({ + name: "Work", + timeZone: TimeZoneSchema.parse("America/Denver"), + isPrimary: true, + }), + ]; + const list = spyOn(CalendarApi, "list").mockResolvedValue(calendars); + const sync = spyOn( + calendarTimeZoneHint, + "syncCalendarTimeZoneHintFromCalendars", + ); + + await calendarsQueryOptions(true).queryFn!(); + + expect(list).toHaveBeenCalledTimes(1); + expect(sync).toHaveBeenCalledTimes(1); + expect(sync).toHaveBeenCalledWith(calendars); + + list.mockRestore(); + sync.mockRestore(); + }); + + it("syncs the calendar timezone hint for the anonymous sentinel path", async () => { + setBrowserTimeZoneForTests("UTC"); + const sync = spyOn( + calendarTimeZoneHint, + "syncCalendarTimeZoneHintFromCalendars", + ); + + await calendarsQueryOptions(false).queryFn!(); + + expect(sync).toHaveBeenCalledTimes(1); + expect(sync.mock.calls[0]?.[0]).toHaveLength(1); + expect(sync.mock.calls[0]?.[0][0]?.isVisible).toBe(true); + + sync.mockRestore(); + }); +}); diff --git a/apps/calendar-web/src/routers/loaders.test.ts b/apps/calendar-web/src/routers/loaders.test.ts index a35988d77..b0fadc492 100644 --- a/apps/calendar-web/src/routers/loaders.test.ts +++ b/apps/calendar-web/src/routers/loaders.test.ts @@ -161,6 +161,14 @@ describe("loadTodayData", () => { expect(loadTodayData().dateString).toBe("2026-11-11"); }); + + it("uses the browser timezone for today when the calendars cache is empty", () => { + setBrowserTimeZoneForTests("America/Chicago"); + setSystemTime(new Date("2026-06-01T18:00:00.000Z")); + queryClient.removeQueries({ queryKey: calendarQueryKeys.all }); + + expect(loadTodayData().dateString).toBe("2026-06-01"); + }); }); describe("loadDateParam", () => { From 4c29be1670755b1d53c06faab685d0dee6f04b15 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 12 Nov 2026 09:19:08 +0000 Subject: [PATCH 2/2] test: fix hint regression test imports and queryFn typing Co-authored-by: Tyler Dane --- ...lendarRootModelCalendarHintSyncTests.swift | 1 + .../src/calendars/calendar.query.test.ts | 28 +++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/apps/calendar-macos/CompassData/Tests/CompassDataTests/NativeCalendarRootModelCalendarHintSyncTests.swift b/apps/calendar-macos/CompassData/Tests/CompassDataTests/NativeCalendarRootModelCalendarHintSyncTests.swift index 6cd043110..615e2c4a1 100644 --- a/apps/calendar-macos/CompassData/Tests/CompassDataTests/NativeCalendarRootModelCalendarHintSyncTests.swift +++ b/apps/calendar-macos/CompassData/Tests/CompassDataTests/NativeCalendarRootModelCalendarHintSyncTests.swift @@ -1,6 +1,7 @@ import CompassData import CompassKit import XCTest +@testable import CompassData @MainActor final class NativeCalendarRootModelCalendarHintSyncTests: XCTestCase { diff --git a/apps/calendar-web/src/calendars/calendar.query.test.ts b/apps/calendar-web/src/calendars/calendar.query.test.ts index 59d5cec15..dce0d87ad 100644 --- a/apps/calendar-web/src/calendars/calendar.query.test.ts +++ b/apps/calendar-web/src/calendars/calendar.query.test.ts @@ -1,7 +1,12 @@ +import { type QueryFunctionContext } from "@tanstack/react-query"; import { TimeZoneSchema } from "@core/types/domain-primitives"; import { createMockCalendar } from "@web/__tests__/utils/factories/calendar.factory"; import { CalendarApi } from "@web/api/calendar.api"; -import { calendarsQueryOptions } from "@web/calendars/calendar.query"; +import { queryClient } from "@web/api/query-client"; +import { + calendarQueryKeys, + calendarsQueryOptions, +} from "@web/calendars/calendar.query"; import * as calendarTimeZoneHint from "@web/timezone/calendar-timezone-hint"; import { resetEffectiveTimeZoneStoreForTests, @@ -9,6 +14,17 @@ import { } from "@web/timezone/effective-timezone.store"; import { afterEach, describe, expect, it, spyOn } from "bun:test"; +function calendarsQueryContext(): QueryFunctionContext< + typeof calendarQueryKeys.all +> { + return { + client: queryClient, + queryKey: calendarQueryKeys.all, + signal: new AbortController().signal, + meta: undefined, + }; +} + describe("calendarsQueryOptions", () => { afterEach(() => { resetEffectiveTimeZoneStoreForTests(); @@ -28,7 +44,7 @@ describe("calendarsQueryOptions", () => { "syncCalendarTimeZoneHintFromCalendars", ); - await calendarsQueryOptions(true).queryFn!(); + await calendarsQueryOptions(true).queryFn!(calendarsQueryContext()); expect(list).toHaveBeenCalledTimes(1); expect(sync).toHaveBeenCalledTimes(1); @@ -45,11 +61,13 @@ describe("calendarsQueryOptions", () => { "syncCalendarTimeZoneHintFromCalendars", ); - await calendarsQueryOptions(false).queryFn!(); + await calendarsQueryOptions(false).queryFn!(calendarsQueryContext()); expect(sync).toHaveBeenCalledTimes(1); - expect(sync.mock.calls[0]?.[0]).toHaveLength(1); - expect(sync.mock.calls[0]?.[0][0]?.isVisible).toBe(true); + const synced = sync.mock.calls[0]?.[0]; + expect(synced).toBeDefined(); + expect(synced).toHaveLength(1); + expect(synced![0]!.isVisible).toBe(true); sync.mockRestore(); });