Files
OpenFrontIO/tests/client/JoinLobbyModal.test.ts
VariableVince 9f8a2d2d84 Fix "you didn't enter the lobby in time" when device clock isn't synced (#3451)
## Description:

If the time on the local device differs from the server time, users may
see the message “You did not join the lobby on time.”

Resolve this by accounting for the time difference, reusing the logic in
`JoinLobbyModal` that was previously in `GameModeSelector`, and
centralizing it into `ServerTime.ts`.

Bug reports:
https://github.com/openfrontio/OpenFrontIO/issues/3428

https://discord.com/channels/1284581928254701718/1482511096597315815

https://discord.com/channels/1284581928254701718/1482382264011591781

Resolves #3428 

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

## Please put your Discord username so you can be contacted if a bug or
regression is found:

tryout33
2026-03-17 15:21:38 -07:00

75 lines
2.2 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { JoinLobbyModal } from "../../src/client/JoinLobbyModal";
describe("JoinLobbyModal server time offset", () => {
let nowMs = 0;
beforeEach(() => {
vi.spyOn(Date, "now").mockImplementation(() => nowMs);
});
afterEach(() => {
vi.restoreAllMocks();
});
it("updates serverTimeOffset from lobby serverTime", () => {
const modal = new JoinLobbyModal();
(modal as any).syncCountdownTimer = vi.fn();
nowMs = 220_000;
(modal as any).updateFromLobby({
gameID: "g1",
serverTime: 200_000,
startsAt: 230_000,
clients: [],
});
expect((modal as any).serverTimeOffset).toBe(-20_000);
expect((modal as any).lobbyStartAt).toBe(230_000);
});
it("does not trigger join timeout early when local clock is ahead", () => {
const modal = new JoinLobbyModal();
const closeSpy = vi
.spyOn(modal, "closeAndLeave")
.mockImplementation(() => undefined);
const dispatchSpy = vi.spyOn(window, "dispatchEvent");
(modal as any).isModalOpen = true;
(modal as any).isConnecting = true;
(modal as any).handledJoinTimeout = false;
// Local clock is +60s ahead of server clock.
nowMs = 160_000;
(modal as any).lobbyStartAt = 105_000;
(modal as any).serverTimeOffset = -60_000;
(modal as any).checkForJoinTimeout();
expect(closeSpy).not.toHaveBeenCalled();
expect(dispatchSpy).not.toHaveBeenCalled();
expect((modal as any).handledJoinTimeout).toBe(false);
});
it("triggers join timeout once adjusted server time reaches lobbyStartAt", () => {
const modal = new JoinLobbyModal();
const closeSpy = vi
.spyOn(modal, "closeAndLeave")
.mockImplementation(() => undefined);
const dispatchSpy = vi.spyOn(window, "dispatchEvent");
(modal as any).isModalOpen = true;
(modal as any).isConnecting = true;
(modal as any).handledJoinTimeout = false;
(modal as any).lobbyStartAt = 105_000;
(modal as any).serverTimeOffset = -60_000;
nowMs = 165_000;
(modal as any).checkForJoinTimeout();
expect(closeSpy).toHaveBeenCalledTimes(1);
expect(dispatchSpy).toHaveBeenCalledTimes(1);
expect((modal as any).handledJoinTimeout).toBe(true);
});
});