统一推荐页各玩法正式 runtime 的游客鉴权透传。 收口推荐页首页展示队列和嵌入运行态切换队列。 补齐未登录读档、签名资产和个人数据读取的游客态处理。 新增运行态 HUD 小尺寸 logo 资源并更新拼图与抓鹅展示。 补充推荐切换、runtime guest 启动和客户端请求回归测试。 更新玩法链路、后端契约和团队记忆文档。
650 lines
18 KiB
TypeScript
650 lines
18 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const apiClientMocks = vi.hoisted(() => ({
|
|
requestJson: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('./apiClient', async () => {
|
|
const actual =
|
|
await vi.importActual<typeof import('./apiClient')>('./apiClient');
|
|
return {
|
|
...actual,
|
|
requestJson: apiClientMocks.requestJson,
|
|
};
|
|
});
|
|
|
|
import {
|
|
finishBarkBattleRun,
|
|
getBarkBattleRuntimeConfig,
|
|
startBarkBattleRun,
|
|
} from './bark-battle-runtime/barkBattleRuntimeClient';
|
|
import {
|
|
getBigFishRun,
|
|
recordBigFishPlay,
|
|
startBigFishRun,
|
|
submitBigFishInput,
|
|
} from './big-fish-runtime/bigFishRuntimeClient';
|
|
import { startJumpHopRuntimeRun } from './jump-hop/jumpHopClient';
|
|
import {
|
|
clickMatch3DItem,
|
|
finishMatch3DTimeUp,
|
|
getMatch3DRun,
|
|
restartMatch3DRun,
|
|
startMatch3DRun,
|
|
stopMatch3DRun,
|
|
} from './match3d-runtime/match3dRuntimeClient';
|
|
import {
|
|
advancePuzzleNextLevel,
|
|
dragPuzzlePieceOrGroup,
|
|
getPuzzleRun,
|
|
startPuzzleRun,
|
|
submitPuzzleLeaderboard,
|
|
swapPuzzlePieces,
|
|
updatePuzzleRunPause,
|
|
usePuzzleRuntimeProp,
|
|
} from './puzzle-runtime/puzzleRuntimeClient';
|
|
import { puzzleClearClient } from './puzzle-clear/puzzleClearClient';
|
|
import {
|
|
dropSquareHoleShape,
|
|
finishSquareHoleTimeUp,
|
|
getSquareHoleRun,
|
|
restartSquareHoleRun,
|
|
startSquareHoleRun,
|
|
stopSquareHoleRun,
|
|
} from './square-hole-runtime/squareHoleRuntimeClient';
|
|
import {
|
|
checkpointWoodenFishRun,
|
|
finishWoodenFishRun,
|
|
startWoodenFishRuntimeRun,
|
|
} from './wooden-fish/woodenFishClient';
|
|
import {
|
|
getVisualNovelHistory,
|
|
getVisualNovelRun,
|
|
regenerateVisualNovelRun,
|
|
startVisualNovelRun,
|
|
} from './visual-novel-runtime/visualNovelRuntimeClient';
|
|
|
|
describe('recommended runtime guest launch clients', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
apiClientMocks.requestJson.mockResolvedValue({ run: {} });
|
|
});
|
|
|
|
function expectRuntimeGuestRequest(expectedUrl: string, expectedMethod: string) {
|
|
const [url, init, , options] = apiClientMocks.requestJson.mock.calls[0];
|
|
expect(url).toBe(expectedUrl);
|
|
expect(init).toEqual(
|
|
expect.objectContaining({
|
|
method: expectedMethod,
|
|
headers: expect.objectContaining({
|
|
Authorization: 'Bearer runtime-guest-token',
|
|
}),
|
|
}),
|
|
);
|
|
expect(options).toEqual(
|
|
expect.objectContaining({
|
|
skipAuth: true,
|
|
skipRefresh: true,
|
|
}),
|
|
);
|
|
}
|
|
|
|
it.each([
|
|
{
|
|
name: 'jump-hop',
|
|
start: () =>
|
|
startJumpHopRuntimeRun('jump-hop-profile-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/jump-hop/runs',
|
|
},
|
|
{
|
|
name: 'visual-novel',
|
|
start: () =>
|
|
startVisualNovelRun(
|
|
'visual-novel-profile-1',
|
|
{ profileId: 'visual-novel-profile-1', mode: 'play' },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/visual-novel/works/visual-novel-profile-1/runs',
|
|
},
|
|
{
|
|
name: 'match3d',
|
|
start: () =>
|
|
startMatch3DRun('match3d-profile-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/match3d/works/match3d-profile-1/runs',
|
|
},
|
|
{
|
|
name: 'square-hole',
|
|
start: () =>
|
|
startSquareHoleRun('square-hole-profile-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/square-hole/works/square-hole-profile-1/runs',
|
|
},
|
|
{
|
|
name: 'big-fish',
|
|
start: () =>
|
|
startBigFishRun('big-fish-session-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/big-fish/sessions/big-fish-session-1/runs',
|
|
},
|
|
{
|
|
name: 'bark-battle',
|
|
start: () =>
|
|
startBarkBattleRun('bark-battle-work-1', {}, {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/bark-battle/works/bark-battle-work-1/runs',
|
|
},
|
|
{
|
|
name: 'wooden-fish',
|
|
start: () =>
|
|
startWoodenFishRuntimeRun('wooden-fish-profile-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/wooden-fish/runs',
|
|
},
|
|
{
|
|
name: 'puzzle',
|
|
start: () =>
|
|
startPuzzleRun(
|
|
{ profileId: 'puzzle-profile-1', levelId: 'level-1' },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/puzzle/runs',
|
|
},
|
|
{
|
|
name: 'puzzle leaderboard',
|
|
start: () =>
|
|
submitPuzzleLeaderboard(
|
|
'run-puzzle-1',
|
|
{
|
|
profileId: 'puzzle-profile-1',
|
|
gridSize: 3,
|
|
elapsedMs: 18_000,
|
|
nickname: '玩家',
|
|
},
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/puzzle/runs/run-puzzle-1/leaderboard',
|
|
},
|
|
])(
|
|
'$name start request uses the runtime guest bearer token without touching login auth',
|
|
async ({ start, expectedUrl }) => {
|
|
await start();
|
|
|
|
const [url, init, , options] = apiClientMocks.requestJson.mock.calls[0];
|
|
expect(url).toBe(expectedUrl);
|
|
expect(init).toEqual(
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: expect.objectContaining({
|
|
Authorization: 'Bearer runtime-guest-token',
|
|
}),
|
|
}),
|
|
);
|
|
expect(options).toEqual(
|
|
expect.objectContaining({
|
|
skipAuth: true,
|
|
skipRefresh: true,
|
|
}),
|
|
);
|
|
},
|
|
);
|
|
|
|
it('puzzle next level keeps the default current-run handoff without a request body', async () => {
|
|
await advancePuzzleNextLevel(
|
|
'run-puzzle-1',
|
|
{},
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
);
|
|
|
|
const [url, init, , options] = apiClientMocks.requestJson.mock.calls[0];
|
|
expect(url).toBe('/api/runtime/puzzle/runs/run-puzzle-1/next-level');
|
|
expect(init).toEqual(
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: expect.objectContaining({
|
|
Authorization: 'Bearer runtime-guest-token',
|
|
}),
|
|
}),
|
|
);
|
|
expect(init.body).toBeUndefined();
|
|
expect(options).toEqual(
|
|
expect.objectContaining({
|
|
skipAuth: true,
|
|
skipRefresh: true,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('puzzle leaderboard submission does not retry unsafe writes', async () => {
|
|
await submitPuzzleLeaderboard(
|
|
'run-puzzle-1',
|
|
{
|
|
profileId: 'puzzle-profile-1',
|
|
gridSize: 3,
|
|
elapsedMs: 18_000,
|
|
nickname: '玩家',
|
|
},
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
);
|
|
|
|
const [url, init, , options] = apiClientMocks.requestJson.mock.calls[0];
|
|
expect(url).toBe('/api/runtime/puzzle/runs/run-puzzle-1/leaderboard');
|
|
expect(init).toEqual(
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: expect.objectContaining({
|
|
Authorization: 'Bearer runtime-guest-token',
|
|
'Content-Type': 'application/json',
|
|
}),
|
|
}),
|
|
);
|
|
expect(options).toEqual(
|
|
expect.objectContaining({
|
|
retry: expect.objectContaining({
|
|
maxRetries: 0,
|
|
}),
|
|
}),
|
|
);
|
|
});
|
|
|
|
it.each([
|
|
{
|
|
name: 'puzzle get run',
|
|
run: () =>
|
|
getPuzzleRun('run-puzzle-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/puzzle/runs/run-puzzle-1',
|
|
expectedMethod: 'GET',
|
|
},
|
|
{
|
|
name: 'puzzle swap',
|
|
run: () =>
|
|
swapPuzzlePieces(
|
|
'run-puzzle-1',
|
|
{ firstPieceId: 'piece-a', secondPieceId: 'piece-b' },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/puzzle/runs/run-puzzle-1/swap',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'puzzle drag',
|
|
run: () =>
|
|
dragPuzzlePieceOrGroup(
|
|
'run-puzzle-1',
|
|
{ pieceId: 'piece-a', targetRow: 1, targetCol: 2 },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/puzzle/runs/run-puzzle-1/drag',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'puzzle pause',
|
|
run: () =>
|
|
updatePuzzleRunPause(
|
|
'run-puzzle-1',
|
|
{ paused: true },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/puzzle/runs/run-puzzle-1/pause',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'puzzle prop',
|
|
run: () =>
|
|
usePuzzleRuntimeProp(
|
|
'run-puzzle-1',
|
|
{ propKind: 'extendTime' },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/puzzle/runs/run-puzzle-1/props',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'puzzle-clear get run',
|
|
run: () =>
|
|
puzzleClearClient.getRun('puzzle-clear-run-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/puzzle-clear/runs/puzzle-clear-run-1',
|
|
expectedMethod: 'GET',
|
|
},
|
|
{
|
|
name: 'puzzle-clear swap',
|
|
run: () =>
|
|
puzzleClearClient.swapCards(
|
|
'puzzle-clear-run-1',
|
|
{ fromRow: 0, fromCol: 0, toRow: 0, toCol: 1 },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/puzzle-clear/runs/puzzle-clear-run-1/swap',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'puzzle-clear retry',
|
|
run: () =>
|
|
puzzleClearClient.retryLevel('puzzle-clear-run-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl:
|
|
'/api/runtime/puzzle-clear/runs/puzzle-clear-run-1/retry-level',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'puzzle-clear next',
|
|
run: () =>
|
|
puzzleClearClient.advanceNextLevel('puzzle-clear-run-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl:
|
|
'/api/runtime/puzzle-clear/runs/puzzle-clear-run-1/next-level',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'puzzle-clear time up',
|
|
run: () =>
|
|
puzzleClearClient.markTimeUp('puzzle-clear-run-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/puzzle-clear/runs/puzzle-clear-run-1/time-up',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'square-hole get run',
|
|
run: () =>
|
|
getSquareHoleRun('square-hole-run-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/square-hole/runs/square-hole-run-1',
|
|
expectedMethod: 'GET',
|
|
},
|
|
{
|
|
name: 'square-hole drop',
|
|
run: () =>
|
|
dropSquareHoleShape(
|
|
'square-hole-run-1',
|
|
{
|
|
holeId: 'hole-1',
|
|
clientSnapshotVersion: 1,
|
|
clientEventId: 'event-1',
|
|
droppedAtMs: 1_700_000_000_000,
|
|
},
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/square-hole/runs/square-hole-run-1/drop',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'square-hole stop',
|
|
run: () =>
|
|
stopSquareHoleRun(
|
|
'square-hole-run-1',
|
|
{ clientActionId: 'stop-1' },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/square-hole/runs/square-hole-run-1/stop',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'square-hole restart',
|
|
run: () =>
|
|
restartSquareHoleRun('square-hole-run-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/square-hole/runs/square-hole-run-1/restart',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'square-hole time up',
|
|
run: () =>
|
|
finishSquareHoleTimeUp('square-hole-run-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/square-hole/runs/square-hole-run-1/time-up',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'big-fish get run',
|
|
run: () =>
|
|
getBigFishRun('big-fish-run-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/big-fish/runs/big-fish-run-1',
|
|
expectedMethod: 'GET',
|
|
},
|
|
{
|
|
name: 'big-fish input',
|
|
run: () =>
|
|
submitBigFishInput(
|
|
'big-fish-run-1',
|
|
{ x: 0.25, y: 0.75 },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/big-fish/runs/big-fish-run-1/input',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'big-fish play report',
|
|
run: () =>
|
|
recordBigFishPlay(
|
|
'big-fish-session-1',
|
|
{ elapsedMs: 1_000 },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl:
|
|
'/api/runtime/big-fish/sessions/big-fish-session-1/play',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'bark-battle config',
|
|
run: () =>
|
|
getBarkBattleRuntimeConfig('bark-battle-work-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl:
|
|
'/api/runtime/bark-battle/works/bark-battle-work-1/config',
|
|
expectedMethod: 'GET',
|
|
},
|
|
{
|
|
name: 'bark-battle finish',
|
|
run: () =>
|
|
finishBarkBattleRun(
|
|
'bark-battle-run-1',
|
|
{
|
|
runId: 'bark-battle-run-1',
|
|
runToken: 'run-token',
|
|
workId: 'bark-battle-work-1',
|
|
configVersion: 1,
|
|
rulesetVersion: 'v1',
|
|
difficultyPreset: 'normal',
|
|
clientStartedAt: '2026-06-10T00:00:00Z',
|
|
clientFinishedAt: '2026-06-10T00:00:10Z',
|
|
durationMs: 10_000,
|
|
derivedMetrics: {
|
|
triggerCount: 1,
|
|
maxVolume: 0.8,
|
|
averageVolume: 0.4,
|
|
finalEnergy: 10,
|
|
comboMax: 1,
|
|
},
|
|
},
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/bark-battle/runs/bark-battle-run-1/finish',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'wooden-fish checkpoint',
|
|
run: () =>
|
|
checkpointWoodenFishRun(
|
|
'wooden-fish-run-1',
|
|
{ totalTapCount: 8, wordCounters: [] },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl:
|
|
'/api/runtime/wooden-fish/runs/wooden-fish-run-1/checkpoint',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'wooden-fish finish',
|
|
run: () =>
|
|
finishWoodenFishRun(
|
|
'wooden-fish-run-1',
|
|
{ totalTapCount: 8, wordCounters: [] },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/wooden-fish/runs/wooden-fish-run-1/finish',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'visual-novel get run',
|
|
run: () =>
|
|
getVisualNovelRun('visual-novel-run-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/visual-novel/runs/visual-novel-run-1',
|
|
expectedMethod: 'GET',
|
|
},
|
|
{
|
|
name: 'visual-novel history',
|
|
run: () =>
|
|
getVisualNovelHistory('visual-novel-run-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl:
|
|
'/api/runtime/visual-novel/runs/visual-novel-run-1/history',
|
|
expectedMethod: 'GET',
|
|
},
|
|
{
|
|
name: 'visual-novel regenerate',
|
|
run: () =>
|
|
regenerateVisualNovelRun(
|
|
'visual-novel-run-1',
|
|
{ historyEntryId: 'history-1', clientEventId: 'event-1' },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl:
|
|
'/api/runtime/visual-novel/runs/visual-novel-run-1/regenerate',
|
|
expectedMethod: 'POST',
|
|
},
|
|
])(
|
|
'$name uses the shared runtime guest bearer token without touching login auth',
|
|
async ({ run, expectedUrl, expectedMethod }) => {
|
|
await run();
|
|
|
|
expectRuntimeGuestRequest(expectedUrl, expectedMethod);
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
{
|
|
name: 'get run',
|
|
run: () =>
|
|
getMatch3DRun('match3d-run-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/match3d/runs/match3d-run-1',
|
|
expectedMethod: 'GET',
|
|
},
|
|
{
|
|
name: 'restart',
|
|
run: () =>
|
|
restartMatch3DRun('match3d-run-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/match3d/runs/match3d-run-1/restart',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'stop',
|
|
run: () =>
|
|
stopMatch3DRun(
|
|
'match3d-run-1',
|
|
{ clientActionId: 'stop-1' },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/match3d/runs/match3d-run-1/stop',
|
|
expectedMethod: 'POST',
|
|
},
|
|
{
|
|
name: 'time up',
|
|
run: () =>
|
|
finishMatch3DTimeUp('match3d-run-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/match3d/runs/match3d-run-1/time-up',
|
|
expectedMethod: 'POST',
|
|
},
|
|
])(
|
|
'match3d $name uses the runtime guest bearer token without touching login auth',
|
|
async ({ run, expectedUrl, expectedMethod }) => {
|
|
await run();
|
|
|
|
const [url, init, , options] = apiClientMocks.requestJson.mock.calls[0];
|
|
expect(url).toBe(expectedUrl);
|
|
expect(init).toEqual(
|
|
expect.objectContaining({
|
|
method: expectedMethod,
|
|
headers: expect.objectContaining({
|
|
Authorization: 'Bearer runtime-guest-token',
|
|
}),
|
|
}),
|
|
);
|
|
expect(options).toEqual(
|
|
expect.objectContaining({
|
|
skipAuth: true,
|
|
skipRefresh: true,
|
|
}),
|
|
);
|
|
},
|
|
);
|
|
|
|
it('match3d click uses the runtime guest bearer token without touching login auth', async () => {
|
|
apiClientMocks.requestJson.mockResolvedValueOnce({
|
|
confirmation: {
|
|
accepted: true,
|
|
run: {},
|
|
clearedItemInstanceIds: [],
|
|
},
|
|
});
|
|
|
|
await clickMatch3DItem(
|
|
'match3d-run-1',
|
|
{
|
|
runId: 'match3d-run-1',
|
|
itemInstanceId: 'item-1',
|
|
clientActionId: 'action-1',
|
|
clientEventId: 'event-1',
|
|
clickedAtMs: 1_700_000_000_000,
|
|
clientSnapshotVersion: 1,
|
|
},
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
);
|
|
|
|
const [url, init, , options] = apiClientMocks.requestJson.mock.calls[0];
|
|
expect(url).toBe('/api/runtime/match3d/runs/match3d-run-1/click');
|
|
expect(init).toEqual(
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: expect.objectContaining({
|
|
Authorization: 'Bearer runtime-guest-token',
|
|
}),
|
|
}),
|
|
);
|
|
expect(options).toEqual(
|
|
expect.objectContaining({
|
|
skipAuth: true,
|
|
skipRefresh: true,
|
|
}),
|
|
);
|
|
});
|
|
});
|