Bootssymbol hinzugefügt
This commit is contained in:
@@ -9,6 +9,8 @@ vi.mock("../src/api", () => apiMocks);
|
||||
import { useAnchorWatch } from "../src/hooks/useAnchorWatch";
|
||||
|
||||
const originalVibrate = Object.getOwnPropertyDescriptor(navigator, "vibrate");
|
||||
const originalAudioContext = Object.getOwnPropertyDescriptor(window, "AudioContext");
|
||||
const originalWebkitAudioContext = Object.getOwnPropertyDescriptor(window, "webkitAudioContext");
|
||||
|
||||
beforeEach(() => {
|
||||
apiMocks.getNearestTide.mockReset();
|
||||
@@ -20,9 +22,25 @@ afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
if (originalVibrate) Object.defineProperty(navigator, "vibrate", originalVibrate);
|
||||
else Reflect.deleteProperty(navigator, "vibrate");
|
||||
restoreProperty(window, "AudioContext", originalAudioContext);
|
||||
restoreProperty(window, "webkitAudioContext", originalWebkitAudioContext);
|
||||
});
|
||||
|
||||
describe("useAnchorWatch", () => {
|
||||
it("uses the current boat's bow roller height instead of a separate default", () => {
|
||||
const gps = gpsFix({ lat: 53.2, lon: 7.1 });
|
||||
const { result, rerender } = renderHook(
|
||||
({ bowRollerHeightM }) =>
|
||||
useAnchorWatch(gps, { bowRollerHeightM }),
|
||||
{ initialProps: { bowRollerHeightM: 1.4 } }
|
||||
);
|
||||
|
||||
expect(result.current.settings.bowRollerHeightM).toBe(1.4);
|
||||
|
||||
rerender({ bowRollerHeightM: 1.8 });
|
||||
expect(result.current.settings.bowRollerHeightM).toBe(1.8);
|
||||
});
|
||||
|
||||
it("captures an immutable anchor point only after an explicit action", async () => {
|
||||
const gps = gpsFix({ lat: 53.2, lon: 7.1 });
|
||||
const { result, rerender } = renderHook(
|
||||
@@ -102,6 +120,61 @@ describe("useAnchorWatch", () => {
|
||||
expect(vibrate).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("repeats the audible drift alarm until it is explicitly acknowledged", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-07-26T02:00:00.000Z"));
|
||||
const vibrate = vi.fn();
|
||||
Object.defineProperty(navigator, "vibrate", { configurable: true, value: vibrate });
|
||||
const audio = installAudioContextMock();
|
||||
const initial = gpsFix({ lat: 0, lon: 0 });
|
||||
const { result, rerender } = renderHook(
|
||||
({ value }) => useAnchorWatch(value),
|
||||
{ initialProps: { value: initial } }
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.captureAnchor();
|
||||
result.current.updateSettings({ alarmRadiusM: 100, deployedRodeLengthM: 100 });
|
||||
});
|
||||
await act(async () => {
|
||||
expect(await result.current.arm()).toBe(true);
|
||||
});
|
||||
|
||||
rerender({
|
||||
value: gpsFix(
|
||||
{ lat: 150 / 111_195.08, lon: 0 },
|
||||
{ accuracyM: 10, timestampMs: Date.now() }
|
||||
)
|
||||
});
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(result.current.driftAlarm).toBe(true);
|
||||
expect(result.current.audibleDriftAlarmActive).toBe(true);
|
||||
expect(audio.start).toHaveBeenCalledTimes(3);
|
||||
expect(vibrate).toHaveBeenCalledTimes(1);
|
||||
|
||||
await act(async () => {
|
||||
vi.advanceTimersByTime(1_300);
|
||||
await Promise.resolve();
|
||||
});
|
||||
expect(audio.start).toHaveBeenCalledTimes(6);
|
||||
expect(vibrate).toHaveBeenCalledTimes(2);
|
||||
|
||||
act(() => result.current.acknowledgeAlarm());
|
||||
expect(result.current.alarmAcknowledged).toBe(true);
|
||||
expect(result.current.audibleDriftAlarmActive).toBe(false);
|
||||
const startsAfterAcknowledgement = audio.start.mock.calls.length;
|
||||
|
||||
await act(async () => {
|
||||
vi.advanceTimersByTime(5_000);
|
||||
await Promise.resolve();
|
||||
});
|
||||
expect(audio.start).toHaveBeenCalledTimes(startsAfterAcknowledgement);
|
||||
expect(vibrate).toHaveBeenLastCalledWith(0);
|
||||
});
|
||||
|
||||
it("keeps the positional watch usable but never confirms rode sufficiency from partial tide data", async () => {
|
||||
apiMocks.getNearestTide.mockResolvedValue(tideFixture(6));
|
||||
const { result } = renderHook(() => useAnchorWatch(gpsFix({ lat: 53.2, lon: 7.1 })));
|
||||
@@ -146,3 +219,52 @@ function tideFixture(hours = 30): TideSummary {
|
||||
updatedAt: new Date(now).toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
function installAudioContextMock() {
|
||||
const start = vi.fn();
|
||||
const stop = vi.fn();
|
||||
const context = {
|
||||
state: "running",
|
||||
currentTime: 0,
|
||||
destination: {},
|
||||
resume: vi.fn().mockResolvedValue(undefined),
|
||||
close: vi.fn().mockResolvedValue(undefined),
|
||||
createOscillator: vi.fn(() => ({
|
||||
type: "sine",
|
||||
frequency: { value: 0 },
|
||||
connect: vi.fn(),
|
||||
disconnect: vi.fn(),
|
||||
start,
|
||||
stop,
|
||||
onended: null
|
||||
})),
|
||||
createGain: vi.fn(() => ({
|
||||
gain: {
|
||||
setValueAtTime: vi.fn(),
|
||||
exponentialRampToValueAtTime: vi.fn()
|
||||
},
|
||||
connect: vi.fn(),
|
||||
disconnect: vi.fn()
|
||||
}))
|
||||
};
|
||||
function AudioContextMock() {
|
||||
return context;
|
||||
}
|
||||
Object.defineProperty(window, "AudioContext", {
|
||||
configurable: true,
|
||||
value: AudioContextMock
|
||||
});
|
||||
return { context, start, stop };
|
||||
}
|
||||
|
||||
function restoreProperty(
|
||||
target: object,
|
||||
property: PropertyKey,
|
||||
descriptor: PropertyDescriptor | undefined
|
||||
) {
|
||||
if (descriptor) {
|
||||
Object.defineProperty(target, property, descriptor);
|
||||
} else {
|
||||
Reflect.deleteProperty(target, property);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user