Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions src/hooks/useSimulationMode.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ jest.mock('~/store/atoms/navigation', () => ({
autoModeEnabledAtom: { toString: () => 'autoModeEnabledAtom' },
}));

jest.mock('~/store/atoms/speech', () => ({
__esModule: true,
default: { toString: () => 'speechState' },
resetFirstSpeechAtom: { toString: () => 'resetFirstSpeechAtom' },
}));

jest.mock('~/store', () => ({
store: {
get: jest.fn(() => null),
Expand Down Expand Up @@ -654,8 +660,12 @@ describe('useSimulationMode', () => {
.spyOn(trainSpeedModule, 'generateTrainSpeedProfile')
.mockReturnValue([2000]);

(store.get as jest.Mock).mockReturnValue(
mockLocationObject(35.691, 139.777)
// resetFirstSpeechAtom は非ゼロの数値、それ以外(locationAtom)は位置オブジェクトを返す。
// 非ゼロ(3)にすることで「現在値 + 1」を読んでいることを検証できる(固定値1だと通ってしまう)。
(store.get as jest.Mock).mockImplementation((atom) =>
atom?.toString?.() === 'resetFirstSpeechAtom'
? 3
: mockLocationObject(35.691, 139.777)
);

renderHook(() => useSimulationMode(), {
Expand All @@ -669,6 +679,12 @@ describe('useSimulationMode', () => {
(call) => call[0]?.toString?.() === 'selectedDirectionAtom'
);
expect(directionSetCalls).toHaveLength(0);
// 折り返す前は初回放送の再発火も起きない
expect(
(store.set as jest.Mock).mock.calls.filter(
(call) => call[0]?.toString?.() === 'resetFirstSpeechAtom'
)
).toHaveLength(0);

// さらに進めて待機時間を超過させると方面(selectedDirection/selectedBound)が逆転する
jest.advanceTimersByTime(40000);
Expand All @@ -685,6 +701,14 @@ describe('useSimulationMode', () => {
(call) => call[0]?.toString?.() === 'selectedBoundAtom'
);
expect(boundSetCalls.length).toBeGreaterThanOrEqual(1);

// 折り返し時に初回放送(firstSpeech)が再発火する(resetFirstSpeechをインクリメント)。
// 現在値3 + 1 = 4 が設定され、二重発火せず1回だけ呼ばれることを検証する。
const resetFirstSpeechCalls = (store.set as jest.Mock).mock.calls.filter(
(call) => call[0]?.toString?.() === 'resetFirstSpeechAtom'
);
expect(resetFirstSpeechCalls).toHaveLength(1);
expect(resetFirstSpeechCalls[0][1]).toBe(4);
});

it('ループ線では終点でも方面を逆転せず先頭に戻って周回を続ける', () => {
Expand Down
5 changes: 5 additions & 0 deletions src/hooks/useSimulationMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { GET_TRAIN_ROUTE } from '~/lib/graphql/queries';
import { store } from '~/store';
import { locationAtom } from '~/store/atoms/location';
import { autoModeEnabledAtom } from '~/store/atoms/navigation';
import { resetFirstSpeechAtom } from '~/store/atoms/speech';
import { generateTrainSpeedProfile } from '~/utils/trainSpeed';
import {
selectedBoundAtom,
Expand Down Expand Up @@ -520,6 +521,10 @@ export const useSimulationMode = (): void => {
// 折り返し後の行き先(selectedBound)は現在の始発駅にあたる先頭要素。
store.set(selectedBoundAtom, maybeRevsersedStations[0] ?? null);
store.set(selectedDirectionAtom, reversedDirection);
// 折り返し後は新しい行き先として初回放送(この電車は〜行きです)を再発火させる。
// resetFirstSpeechAtom を進めると useTTS 側で firstSpeech が true に戻り、
// 行き先変更 + 発車後(arrived=false)に初回TTSが改めて再生される。
store.set(resetFirstSpeechAtom, store.get(resetFirstSpeechAtom) + 1);
return;
}

Expand Down
Loading