refactor redux data model

This commit is contained in:
seavor 2026-04-15 21:48:03 -05:00
parent ae1bc3da38
commit 0ff391491d
243 changed files with 5212 additions and 5963 deletions

View file

@ -27,15 +27,6 @@ vi.mock('./services/ProtobufService', () => ({
}),
}));
vi.mock('./persistence', () => ({
RoomPersistence: { clearStore: vi.fn() },
SessionPersistence: { clearStore: vi.fn(), initialized: vi.fn(), connectionAttempted: vi.fn() },
}));
vi.mock('@app/store', () => ({
GameDispatch: { clearStore: vi.fn() },
}));
vi.mock('./commands/session', () => ({
ping: vi.fn(),
}));
@ -43,19 +34,49 @@ vi.mock('./commands/session', () => ({
import { WebClient } from './WebClient';
import { WebSocketService } from './services/WebSocketService';
import { ProtobufService } from './services/ProtobufService';
import { RoomPersistence, SessionPersistence } from './persistence';
import { ping } from './commands/session';
import { App, Enriched } from '@app/types';
import { Subject } from 'rxjs';
import { Mock } from 'vitest';
import { SocketTransport } from './services/ProtobufService';
import { WebSocketServiceConfig } from './services/WebSocketService';
import type { IWebClientResponse, IWebClientRequest } from './interfaces';
function makeMockResponse(): IWebClientResponse {
return {
session: {
initialized: vi.fn(),
connectionAttempted: vi.fn(),
clearStore: vi.fn(),
updateStatus: vi.fn(),
},
room: { clearStore: vi.fn() },
game: { clearStore: vi.fn() },
admin: {},
moderator: {},
} as unknown as IWebClientResponse;
}
function makeMockRequest(): IWebClientRequest {
return {
authentication: {},
session: {},
rooms: {},
admin: {},
moderator: {},
} as unknown as IWebClientRequest;
}
describe('WebClient', () => {
let client: WebClient;
let mockResponse: IWebClientResponse;
let mockRequest: IWebClientRequest;
let messageSubject: Subject<MessageEvent>;
beforeEach(() => {
// Reset the singleton so each test starts fresh.
(WebClient as unknown as { _instance: WebClient | null })._instance = null;
(ProtobufService as Mock).mockImplementation(function ProtobufServiceImpl(options: SocketTransport) {
captured.pbOptions = options;
return {
@ -75,32 +96,55 @@ describe('WebClient', () => {
checkReadyState: vi.fn().mockReturnValue(true),
};
});
// suppress console.log from constructor in non-test-env check
vi.spyOn(console, 'log').mockImplementation(() => {});
client = new WebClient();
mockResponse = makeMockResponse();
mockRequest = makeMockRequest();
client = new WebClient(mockResponse, mockRequest);
});
afterEach(() => {
vi.restoreAllMocks();
(WebClient as unknown as { _instance: WebClient | null })._instance = null;
});
describe('constructor', () => {
it('stores the response and request on the instance', () => {
expect(client.response).toBe(mockResponse);
expect(client.request).toBe(mockRequest);
});
it('subscribes socket.message$ to protobuf.handleMessageEvent', () => {
const event = { data: new ArrayBuffer(0) } as MessageEvent;
messageSubject.next(event);
expect(client.protobuf.handleMessageEvent).toHaveBeenCalledWith(event);
});
it('calls SessionPersistence.initialized', () => {
expect(SessionPersistence.initialized).toHaveBeenCalled();
it('calls response.session.initialized', () => {
expect(mockResponse.session.initialized).toHaveBeenCalled();
});
it('sets WebClient.instance to the constructed instance', () => {
expect(WebClient.instance).toBe(client);
});
it('throws when instantiated more than once', () => {
expect(() => new WebClient(makeMockResponse(), makeMockRequest())).toThrow(/singleton/);
});
});
describe('static instance accessor', () => {
it('throws when accessed before construction', () => {
(WebClient as unknown as { _instance: WebClient | null })._instance = null;
expect(() => WebClient.instance).toThrow(/not been initialized/);
});
});
describe('connect', () => {
it('calls SessionPersistence.connectionAttempted', () => {
it('calls response.session.connectionAttempted', () => {
const opts: Enriched.WebSocketConnectOptions = { host: 'h', port: '1', reason: App.WebSocketConnectReason.LOGIN, userName: 'u' };
client.connect(opts);
expect(SessionPersistence.connectionAttempted).toHaveBeenCalled();
expect(mockResponse.session.connectionAttempted).toHaveBeenCalled();
});
it('stores options and calls socket.connect', () => {
@ -132,35 +176,38 @@ describe('WebClient', () => {
expect(client.status).toBe(App.StatusEnum.CONNECTED);
});
it('calls protobuf.resetCommands and clears stores on DISCONNECTED', () => {
it('calls protobuf.resetCommands on DISCONNECTED', () => {
client.updateStatus(App.StatusEnum.DISCONNECTED);
expect(client.protobuf.resetCommands).toHaveBeenCalled();
expect(RoomPersistence.clearStore).toHaveBeenCalled();
expect(SessionPersistence.clearStore).toHaveBeenCalled();
});
it('does not clear stores when status is not DISCONNECTED', () => {
it('does not reset protobuf when status is not DISCONNECTED', () => {
client.updateStatus(App.StatusEnum.CONNECTED);
expect(client.protobuf.resetCommands).not.toHaveBeenCalled();
expect(RoomPersistence.clearStore).not.toHaveBeenCalled();
});
});
describe('constructor closures', () => {
it('keepAliveFn calls ping with the callback', () => {
const cb = vi.fn();
captured.wsOptions.keepAliveFn(cb);
captured.wsOptions!.keepAliveFn(cb);
expect(ping).toHaveBeenCalledWith(cb);
});
it('onStatusChange routes to response.session.updateStatus and updates own status', () => {
captured.wsOptions!.onStatusChange(App.StatusEnum.CONNECTED, 'Connected');
expect(mockResponse.session.updateStatus).toHaveBeenCalledWith(App.StatusEnum.CONNECTED, 'Connected');
expect(client.status).toBe(App.StatusEnum.CONNECTED);
});
it('send closure delegates to socket.send', () => {
const data = new Uint8Array([1, 2, 3]);
captured.pbOptions.send(data);
captured.pbOptions!.send(data);
expect(client.socket.send).toHaveBeenCalledWith(data);
});
it('isOpen closure delegates to socket.checkReadyState', () => {
const result = captured.pbOptions.isOpen();
const result = captured.pbOptions!.isOpen();
expect(client.socket.checkReadyState).toHaveBeenCalledWith(WebSocket.OPEN);
expect(result).toBe(true);
});

View file

@ -3,20 +3,43 @@ import { App, Enriched } from '@app/types';
import { ProtobufService } from './services/ProtobufService';
import { WebSocketService } from './services/WebSocketService';
import { ping } from './commands/session';
import { GameDispatch } from '@app/store';
import { RoomPersistence, SessionPersistence } from './persistence';
import { IWebClientResponse, IWebClientRequest } from './interfaces';
export class WebClient {
private static _instance: WebClient | null = null;
public static get instance(): WebClient {
if (!WebClient._instance) {
throw new Error(
'WebClient has not been initialized. Instantiate it via `new WebClient(response, request)` before accessing `WebClient.instance`.'
);
}
return WebClient._instance;
}
public socket: WebSocketService;
public protobuf: ProtobufService;
public response: IWebClientResponse;
public request: IWebClientRequest;
public options: Enriched.WebSocketConnectOptions | null = null;
public status: App.StatusEnum;
constructor() {
constructor(response: IWebClientResponse, request: IWebClientRequest) {
if (WebClient._instance) {
throw new Error('WebClient is a singleton and has already been initialized.');
}
this.response = response;
this.request = request;
this.socket = new WebSocketService({
keepAliveFn: (cb) => ping(cb),
response,
onStatusChange: (status, description) => {
this.response.session.updateStatus(status, description);
this.updateStatus(status);
},
});
this.protobuf = new ProtobufService({
@ -28,7 +51,9 @@ export class WebClient {
this.protobuf.handleMessageEvent(message);
});
SessionPersistence.initialized();
WebClient._instance = this;
this.response.session.initialized();
if (import.meta.env.MODE !== 'test') {
console.log(this);
@ -36,7 +61,7 @@ export class WebClient {
}
public connect(options: Enriched.WebSocketConnectOptions) {
SessionPersistence.connectionAttempted();
this.response.session.connectionAttempted();
this.options = options;
this.socket.connect(options);
}
@ -54,17 +79,6 @@ export class WebClient {
if (status === App.StatusEnum.DISCONNECTED) {
this.protobuf.resetCommands();
this.clearStores();
}
}
private clearStores() {
GameDispatch.clearStore();
RoomPersistence.clearStore();
SessionPersistence.clearStore();
}
}
const webClient = new WebClient();
export default webClient;

View file

@ -1,16 +1,15 @@
/**
* Shared mock shape factories for session command specs.
*
* Usage inside vi.mock() factory callbacks (require is used because
* vi.mock() is hoisted above imports):
* Usage inside vi.mock() factory callbacks:
*
* vi.mock('../../WebClient', () => {
* const { makeWebClientMock } = require('../../__mocks__/sessionCommandMocks');
* return { __esModule: true, default: makeWebClientMock() };
* vi.mock('../../WebClient', async () => {
* const { makeWebClientMock } = await import('../../__mocks__/sessionCommandMocks');
* return { WebClient: { instance: makeWebClientMock() } };
* });
*/
/** Superset WebClient mock — covers all properties used across both session spec files. */
/** Superset WebClient instance mock — covers all properties used across both session spec files. */
export function makeWebClientMock() {
return {
connect: vi.fn(),
@ -21,6 +20,17 @@ export function makeWebClientMock() {
status: 0,
protobuf: {
sendSessionCommand: vi.fn(),
sendRoomCommand: vi.fn(),
sendGameCommand: vi.fn(),
sendAdminCommand: vi.fn(),
sendModeratorCommand: vi.fn(),
},
response: {
session: makeSessionPersistenceMock(),
room: { joinRoom: vi.fn() },
game: {},
admin: {},
moderator: {},
},
};
}

View file

@ -1,15 +1,13 @@
import { create } from '@bufbuild/protobuf';
import { Data } from '@app/types';
import webClient from '../../WebClient';
import { AdminPersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
export function adjustMod(userName: string, shouldBeMod?: boolean, shouldBeJudge?: boolean): void {
webClient.protobuf.sendAdminCommand(
WebClient.instance.protobuf.sendAdminCommand(
Data.Command_AdjustMod_ext,
create(Data.Command_AdjustModSchema, { userName, shouldBeMod, shouldBeJudge }),
{
onSuccess: () => {
AdminPersistence.adjustMod(userName, shouldBeMod, shouldBeJudge);
WebClient.instance.response.admin.adjustMod(userName, shouldBeMod, shouldBeJudge);
},
}
);

View file

@ -1,20 +1,21 @@
vi.mock('../../WebClient', () => ({
__esModule: true,
default: { protobuf: { sendAdminCommand: vi.fn() } },
}));
vi.mock('../../persistence', () => ({
AdminPersistence: {
adjustMod: vi.fn(),
reloadConfig: vi.fn(),
shutdownServer: vi.fn(),
updateServerMessage: vi.fn(),
WebClient: {
instance: {
protobuf: { sendAdminCommand: vi.fn() },
response: {
admin: {
adjustMod: vi.fn(),
reloadConfig: vi.fn(),
shutdownServer: vi.fn(),
updateServerMessage: vi.fn(),
},
},
},
},
}));
import { makeCallbackHelpers } from '../../__mocks__/callbackHelpers';
import webClient from '../../WebClient';
import { AdminPersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
import { adjustMod } from './adjustMod';
import { reloadConfig } from './reloadConfig';
import { shutdownServer } from './shutdownServer';
@ -23,7 +24,7 @@ import { updateServerMessage } from './updateServerMessage';
import { Mock } from 'vitest';
const { invokeOnSuccess } = makeCallbackHelpers(
webClient.protobuf.sendAdminCommand as Mock,
WebClient.instance.protobuf.sendAdminCommand as Mock,
2
);
@ -34,13 +35,13 @@ describe('adjustMod', () => {
it('calls sendAdminCommand with Command_AdjustMod', () => {
adjustMod('alice', true, false);
expect(webClient.protobuf.sendAdminCommand).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), expect.any(Object));
expect(WebClient.instance.protobuf.sendAdminCommand).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), expect.any(Object));
});
it('onSuccess calls AdminPersistence.adjustMod', () => {
it('onSuccess calls response.admin.adjustMod', () => {
adjustMod('alice', true, false);
invokeOnSuccess();
expect(AdminPersistence.adjustMod).toHaveBeenCalledWith('alice', true, false);
expect(WebClient.instance.response.admin.adjustMod).toHaveBeenCalledWith('alice', true, false);
});
});
@ -51,13 +52,13 @@ describe('reloadConfig', () => {
it('calls sendAdminCommand with Command_ReloadConfig', () => {
reloadConfig();
expect(webClient.protobuf.sendAdminCommand).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), expect.any(Object));
expect(WebClient.instance.protobuf.sendAdminCommand).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), expect.any(Object));
});
it('onSuccess calls AdminPersistence.reloadConfig', () => {
it('onSuccess calls response.admin.reloadConfig', () => {
reloadConfig();
invokeOnSuccess();
expect(AdminPersistence.reloadConfig).toHaveBeenCalled();
expect(WebClient.instance.response.admin.reloadConfig).toHaveBeenCalled();
});
});
@ -68,13 +69,13 @@ describe('shutdownServer', () => {
it('calls sendAdminCommand with Command_ShutdownServer', () => {
shutdownServer('maintenance', 10);
expect(webClient.protobuf.sendAdminCommand).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), expect.any(Object));
expect(WebClient.instance.protobuf.sendAdminCommand).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), expect.any(Object));
});
it('onSuccess calls AdminPersistence.shutdownServer', () => {
it('onSuccess calls response.admin.shutdownServer', () => {
shutdownServer('maintenance', 10);
invokeOnSuccess();
expect(AdminPersistence.shutdownServer).toHaveBeenCalled();
expect(WebClient.instance.response.admin.shutdownServer).toHaveBeenCalled();
});
});
@ -85,12 +86,12 @@ describe('updateServerMessage', () => {
it('calls sendAdminCommand with Command_UpdateServerMessage', () => {
updateServerMessage();
expect(webClient.protobuf.sendAdminCommand).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), expect.any(Object));
expect(WebClient.instance.protobuf.sendAdminCommand).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), expect.any(Object));
});
it('onSuccess calls AdminPersistence.updateServerMessage', () => {
it('onSuccess calls response.admin.updateServerMessage', () => {
updateServerMessage();
invokeOnSuccess();
expect(AdminPersistence.updateServerMessage).toHaveBeenCalled();
expect(WebClient.instance.response.admin.updateServerMessage).toHaveBeenCalled();
});
});

View file

@ -1,12 +1,10 @@
import { create } from '@bufbuild/protobuf';
import { Data } from '@app/types';
import webClient from '../../WebClient';
import { AdminPersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
export function reloadConfig(): void {
webClient.protobuf.sendAdminCommand(Data.Command_ReloadConfig_ext, create(Data.Command_ReloadConfigSchema), {
WebClient.instance.protobuf.sendAdminCommand(Data.Command_ReloadConfig_ext, create(Data.Command_ReloadConfigSchema), {
onSuccess: () => {
AdminPersistence.reloadConfig();
WebClient.instance.response.admin.reloadConfig();
},
});
}

View file

@ -1,12 +1,14 @@
import { create } from '@bufbuild/protobuf';
import { Data } from '@app/types';
import webClient from '../../WebClient';
import { AdminPersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
export function shutdownServer(reason: string, minutes: number): void {
webClient.protobuf.sendAdminCommand(Data.Command_ShutdownServer_ext, create(Data.Command_ShutdownServerSchema, { reason, minutes }), {
onSuccess: () => {
AdminPersistence.shutdownServer();
},
});
WebClient.instance.protobuf.sendAdminCommand(
Data.Command_ShutdownServer_ext,
create(Data.Command_ShutdownServerSchema, { reason, minutes }),
{
onSuccess: () => {
WebClient.instance.response.admin.shutdownServer();
},
}
);
}

View file

@ -1,12 +1,10 @@
import { create } from '@bufbuild/protobuf';
import { Data } from '@app/types';
import webClient from '../../WebClient';
import { AdminPersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
export function updateServerMessage(): void {
webClient.protobuf.sendAdminCommand(Data.Command_UpdateServerMessage_ext, create(Data.Command_UpdateServerMessageSchema), {
WebClient.instance.protobuf.sendAdminCommand(Data.Command_UpdateServerMessage_ext, create(Data.Command_UpdateServerMessageSchema), {
onSuccess: () => {
AdminPersistence.updateServerMessage();
WebClient.instance.response.admin.updateServerMessage();
},
});
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function attachCard(gameId: number, params: Data.AttachCardParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_AttachCard_ext, create(Data.Command_AttachCardSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_AttachCard_ext, create(Data.Command_AttachCardSchema, params));
}

View file

@ -1,10 +1,10 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function changeZoneProperties(gameId: number, params: Data.ChangeZonePropertiesParams): void {
webClient.protobuf.sendGameCommand(
WebClient.instance.protobuf.sendGameCommand(
gameId,
Data.Command_ChangeZoneProperties_ext,
create(Data.Command_ChangeZonePropertiesSchema, params)

View file

@ -1,7 +1,7 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function concede(gameId: number): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_Concede_ext, create(Data.Command_ConcedeSchema));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_Concede_ext, create(Data.Command_ConcedeSchema));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function createArrow(gameId: number, params: Data.CreateArrowParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_CreateArrow_ext, create(Data.Command_CreateArrowSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_CreateArrow_ext, create(Data.Command_CreateArrowSchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function createCounter(gameId: number, params: Data.CreateCounterParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_CreateCounter_ext, create(Data.Command_CreateCounterSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_CreateCounter_ext, create(Data.Command_CreateCounterSchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function createToken(gameId: number, params: Data.CreateTokenParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_CreateToken_ext, create(Data.Command_CreateTokenSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_CreateToken_ext, create(Data.Command_CreateTokenSchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function deckSelect(gameId: number, params: Data.DeckSelectParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_DeckSelect_ext, create(Data.Command_DeckSelectSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_DeckSelect_ext, create(Data.Command_DeckSelectSchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function delCounter(gameId: number, params: Data.DelCounterParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_DelCounter_ext, create(Data.Command_DelCounterSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_DelCounter_ext, create(Data.Command_DelCounterSchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function deleteArrow(gameId: number, params: Data.DeleteArrowParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_DeleteArrow_ext, create(Data.Command_DeleteArrowSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_DeleteArrow_ext, create(Data.Command_DeleteArrowSchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function drawCards(gameId: number, params: Data.DrawCardsParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_DrawCards_ext, create(Data.Command_DrawCardsSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_DrawCards_ext, create(Data.Command_DrawCardsSchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function dumpZone(gameId: number, params: Data.DumpZoneParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_DumpZone_ext, create(Data.Command_DumpZoneSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_DumpZone_ext, create(Data.Command_DumpZoneSchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function flipCard(gameId: number, params: Data.FlipCardParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_FlipCard_ext, create(Data.Command_FlipCardSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_FlipCard_ext, create(Data.Command_FlipCardSchema, params));
}

View file

@ -1,4 +1,13 @@
import webClient from '../../WebClient';
vi.mock('../../WebClient', () => ({
WebClient: {
instance: {
protobuf: { sendGameCommand: vi.fn() },
response: { game: {} },
},
},
}));
import { WebClient } from '../../WebClient';
import { create, setExtension } from '@bufbuild/protobuf';
import { Data } from '@app/types';
@ -36,132 +45,127 @@ import { undoDraw } from './undoDraw';
import { unconcede } from './unconcede';
import { judge } from './judge';
vi.mock('../../WebClient', () => ({
__esModule: true,
default: { protobuf: { sendGameCommand: vi.fn() } },
}));
const gameId = 1;
describe('Game commands — delegate to webClient.protobuf.sendGameCommand', () => {
describe('Game commands — delegate to WebClient.instance.protobuf.sendGameCommand', () => {
it('attachCard sends Command_AttachCard', () => {
attachCard(gameId, { cardId: 10, startZone: 'hand' });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_AttachCard_ext, expect.objectContaining({ cardId: 10, startZone: 'hand' })
);
});
it('changeZoneProperties sends Command_ChangeZoneProperties', () => {
changeZoneProperties(gameId, { zoneName: 'side' });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_ChangeZoneProperties_ext, expect.objectContaining({ zoneName: 'side' })
);
});
it('concede sends Command_Concede with empty object', () => {
concede(gameId);
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Data.Command_Concede_ext, expect.any(Object));
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Data.Command_Concede_ext, expect.any(Object));
});
it('createArrow sends Command_CreateArrow', () => {
createArrow(gameId, { startPlayerId: 1, startZone: 'hand' });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_CreateArrow_ext, expect.objectContaining({ startPlayerId: 1, startZone: 'hand' })
);
});
it('createCounter sends Command_CreateCounter', () => {
createCounter(gameId, { counterName: 'life' });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_CreateCounter_ext, expect.objectContaining({ counterName: 'life' })
);
});
it('createToken sends Command_CreateToken', () => {
createToken(gameId, { cardName: 'Goblin', zone: 'play' });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_CreateToken_ext, expect.objectContaining({ cardName: 'Goblin', zone: 'play' })
);
});
it('deckSelect sends Command_DeckSelect', () => {
deckSelect(gameId, { deckId: 5 });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_DeckSelect_ext, expect.objectContaining({ deckId: 5 })
);
});
it('delCounter sends Command_DelCounter', () => {
delCounter(gameId, { counterId: 3 });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_DelCounter_ext, expect.objectContaining({ counterId: 3 })
);
});
it('deleteArrow sends Command_DeleteArrow', () => {
deleteArrow(gameId, { arrowId: 2 });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_DeleteArrow_ext, expect.objectContaining({ arrowId: 2 })
);
});
it('drawCards sends Command_DrawCards', () => {
drawCards(gameId, { number: 3 });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_DrawCards_ext, expect.objectContaining({ number: 3 })
);
});
it('dumpZone sends Command_DumpZone', () => {
dumpZone(gameId, { playerId: 2, zoneName: 'library' });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_DumpZone_ext, expect.objectContaining({ playerId: 2, zoneName: 'library' })
);
});
it('flipCard sends Command_FlipCard', () => {
flipCard(gameId, { cardId: 7, faceDown: false });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_FlipCard_ext, expect.objectContaining({ cardId: 7, faceDown: false })
);
});
it('gameSay sends Command_GameSay', () => {
gameSay(gameId, { message: 'hello' });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_GameSay_ext, expect.objectContaining({ message: 'hello' })
);
});
it('incCardCounter sends Command_IncCardCounter', () => {
incCardCounter(gameId, { cardId: 5, counterId: 1 });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_IncCardCounter_ext, expect.objectContaining({ cardId: 5, counterId: 1 })
);
});
it('incCounter sends Command_IncCounter', () => {
incCounter(gameId, { counterId: 1, delta: 5 });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_IncCounter_ext, expect.objectContaining({ counterId: 1, delta: 5 })
);
});
it('kickFromGame sends Command_KickFromGame', () => {
kickFromGame(gameId, { playerId: 2 });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_KickFromGame_ext, expect.objectContaining({ playerId: 2 })
);
});
it('leaveGame sends Command_LeaveGame with empty object', () => {
leaveGame(gameId);
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Data.Command_LeaveGame_ext, expect.any(Object));
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Data.Command_LeaveGame_ext, expect.any(Object));
});
it('moveCard sends Command_MoveCard', () => {
moveCard(gameId, { startZone: 'hand', targetZone: 'graveyard' });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_MoveCard_ext,
expect.objectContaining({ startZone: 'hand', targetZone: 'graveyard' })
);
@ -169,45 +173,45 @@ describe('Game commands — delegate to webClient.protobuf.sendGameCommand', ()
it('mulligan sends Command_Mulligan', () => {
mulligan(gameId, { number: 7 });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_Mulligan_ext, expect.objectContaining({ number: 7 })
);
});
it('nextTurn sends Command_NextTurn with empty object', () => {
nextTurn(gameId);
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Data.Command_NextTurn_ext, expect.any(Object));
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Data.Command_NextTurn_ext, expect.any(Object));
});
it('readyStart sends Command_ReadyStart', () => {
readyStart(gameId, { ready: true });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_ReadyStart_ext, expect.objectContaining({ ready: true })
);
});
it('revealCards sends Command_RevealCards', () => {
revealCards(gameId, { zoneName: 'hand', cardId: [1, 2] });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_RevealCards_ext, expect.objectContaining({ zoneName: 'hand', cardId: [1, 2] })
);
});
it('reverseTurn sends Command_ReverseTurn with empty object', () => {
reverseTurn(gameId);
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Data.Command_ReverseTurn_ext, expect.any(Object));
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Data.Command_ReverseTurn_ext, expect.any(Object));
});
it('setActivePhase sends Command_SetActivePhase', () => {
setActivePhase(gameId, { phase: 2 });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_SetActivePhase_ext, expect.objectContaining({ phase: 2 })
);
});
it('setCardAttr sends Command_SetCardAttr', () => {
setCardAttr(gameId, { zone: 'play', cardId: 5, attrValue: '2' });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_SetCardAttr_ext,
expect.objectContaining({ zone: 'play', cardId: 5, attrValue: '2' })
);
@ -215,47 +219,47 @@ describe('Game commands — delegate to webClient.protobuf.sendGameCommand', ()
it('setCardCounter sends Command_SetCardCounter', () => {
setCardCounter(gameId, { cardId: 5, counterId: 1 });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_SetCardCounter_ext, expect.objectContaining({ cardId: 5, counterId: 1 })
);
});
it('setCounter sends Command_SetCounter', () => {
setCounter(gameId, { counterId: 1, value: 10 });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_SetCounter_ext, expect.objectContaining({ counterId: 1, value: 10 })
);
});
it('setSideboardLock sends Command_SetSideboardLock', () => {
setSideboardLock(gameId, { locked: true });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_SetSideboardLock_ext, expect.objectContaining({ locked: true })
);
});
it('setSideboardPlan sends Command_SetSideboardPlan', () => {
setSideboardPlan(gameId, { moveList: [] });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_SetSideboardPlan_ext, expect.objectContaining({ moveList: expect.any(Array) })
);
});
it('shuffle sends Command_Shuffle', () => {
shuffle(gameId, { zoneName: 'hand' });
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Data.Command_Shuffle_ext, expect.objectContaining({ zoneName: 'hand' })
);
});
it('undoDraw sends Command_UndoDraw with empty object', () => {
undoDraw(gameId);
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Data.Command_UndoDraw_ext, expect.any(Object));
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Data.Command_UndoDraw_ext, expect.any(Object));
});
it('unconcede sends Command_Unconcede with empty object', () => {
unconcede(gameId);
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Data.Command_Unconcede_ext, expect.any(Object));
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Data.Command_Unconcede_ext, expect.any(Object));
});
it('judge sends Command_Judge with targetId and wrapped gameCommand array', () => {
@ -263,7 +267,7 @@ describe('Game commands — delegate to webClient.protobuf.sendGameCommand', ()
const innerCmd = create(Data.GameCommandSchema);
setExtension(innerCmd, Data.Command_DrawCards_ext, create(Data.Command_DrawCardsSchema, { number: 2 }));
judge(gameId, targetId, innerCmd);
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId,
Data.Command_Judge_ext,
expect.objectContaining({ targetId: 3, gameCommand: expect.any(Array) })

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function gameSay(gameId: number, params: Data.GameSayParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_GameSay_ext, create(Data.Command_GameSaySchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_GameSay_ext, create(Data.Command_GameSaySchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function incCardCounter(gameId: number, params: Data.IncCardCounterParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_IncCardCounter_ext, create(Data.Command_IncCardCounterSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_IncCardCounter_ext, create(Data.Command_IncCardCounterSchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function incCounter(gameId: number, params: Data.IncCounterParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_IncCounter_ext, create(Data.Command_IncCounterSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_IncCounter_ext, create(Data.Command_IncCounterSchema, params));
}

View file

@ -1,9 +1,9 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function judge(gameId: number, targetId: number, innerGameCommand: Data.GameCommand): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_Judge_ext, create(Data.Command_JudgeSchema, {
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_Judge_ext, create(Data.Command_JudgeSchema, {
targetId,
gameCommand: [innerGameCommand],
}));

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function kickFromGame(gameId: number, params: Data.KickFromGameParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_KickFromGame_ext, create(Data.Command_KickFromGameSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_KickFromGame_ext, create(Data.Command_KickFromGameSchema, params));
}

View file

@ -1,7 +1,7 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function leaveGame(gameId: number): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_LeaveGame_ext, create(Data.Command_LeaveGameSchema));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_LeaveGame_ext, create(Data.Command_LeaveGameSchema));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function moveCard(gameId: number, params: Data.MoveCardParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_MoveCard_ext, create(Data.Command_MoveCardSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_MoveCard_ext, create(Data.Command_MoveCardSchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function mulligan(gameId: number, params: Data.MulliganParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_Mulligan_ext, create(Data.Command_MulliganSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_Mulligan_ext, create(Data.Command_MulliganSchema, params));
}

View file

@ -1,7 +1,7 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function nextTurn(gameId: number): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_NextTurn_ext, create(Data.Command_NextTurnSchema));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_NextTurn_ext, create(Data.Command_NextTurnSchema));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function readyStart(gameId: number, params: Data.ReadyStartParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_ReadyStart_ext, create(Data.Command_ReadyStartSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_ReadyStart_ext, create(Data.Command_ReadyStartSchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function revealCards(gameId: number, params: Data.RevealCardsParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_RevealCards_ext, create(Data.Command_RevealCardsSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_RevealCards_ext, create(Data.Command_RevealCardsSchema, params));
}

View file

@ -1,7 +1,7 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function reverseTurn(gameId: number): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_ReverseTurn_ext, create(Data.Command_ReverseTurnSchema));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_ReverseTurn_ext, create(Data.Command_ReverseTurnSchema));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function setActivePhase(gameId: number, params: Data.SetActivePhaseParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_SetActivePhase_ext, create(Data.Command_SetActivePhaseSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_SetActivePhase_ext, create(Data.Command_SetActivePhaseSchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function setCardAttr(gameId: number, params: Data.SetCardAttrParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_SetCardAttr_ext, create(Data.Command_SetCardAttrSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_SetCardAttr_ext, create(Data.Command_SetCardAttrSchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function setCardCounter(gameId: number, params: Data.SetCardCounterParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_SetCardCounter_ext, create(Data.Command_SetCardCounterSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_SetCardCounter_ext, create(Data.Command_SetCardCounterSchema, params));
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function setCounter(gameId: number, params: Data.SetCounterParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_SetCounter_ext, create(Data.Command_SetCounterSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_SetCounter_ext, create(Data.Command_SetCounterSchema, params));
}

View file

@ -1,8 +1,12 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function setSideboardLock(gameId: number, params: Data.SetSideboardLockParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_SetSideboardLock_ext, create(Data.Command_SetSideboardLockSchema, params));
WebClient.instance.protobuf.sendGameCommand(
gameId,
Data.Command_SetSideboardLock_ext,
create(Data.Command_SetSideboardLockSchema, params)
);
}

View file

@ -1,8 +1,12 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function setSideboardPlan(gameId: number, params: Data.SetSideboardPlanParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_SetSideboardPlan_ext, create(Data.Command_SetSideboardPlanSchema, params));
WebClient.instance.protobuf.sendGameCommand(
gameId,
Data.Command_SetSideboardPlan_ext,
create(Data.Command_SetSideboardPlanSchema, params)
);
}

View file

@ -1,8 +1,8 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function shuffle(gameId: number, params: Data.ShuffleParams): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_Shuffle_ext, create(Data.Command_ShuffleSchema, params));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_Shuffle_ext, create(Data.Command_ShuffleSchema, params));
}

View file

@ -1,7 +1,7 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function unconcede(gameId: number): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_Unconcede_ext, create(Data.Command_UnconcedeSchema));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_Unconcede_ext, create(Data.Command_UnconcedeSchema));
}

View file

@ -1,7 +1,7 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function undoDraw(gameId: number): void {
webClient.protobuf.sendGameCommand(gameId, Data.Command_UndoDraw_ext, create(Data.Command_UndoDrawSchema));
WebClient.instance.protobuf.sendGameCommand(gameId, Data.Command_UndoDraw_ext, create(Data.Command_UndoDrawSchema));
}

View file

@ -1,16 +1,15 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { ModeratorPersistence } from '../../persistence';
import { Data } from '@app/types';
export function banFromServer(minutes: number, userName?: string, address?: string, reason?: string,
visibleReason?: string, clientid?: string, removeMessages?: number): void {
webClient.protobuf.sendModeratorCommand(Data.Command_BanFromServer_ext, create(Data.Command_BanFromServerSchema, {
WebClient.instance.protobuf.sendModeratorCommand(Data.Command_BanFromServer_ext, create(Data.Command_BanFromServerSchema, {
minutes, userName, address, reason, visibleReason, clientid, removeMessages
}), {
onSuccess: () => {
ModeratorPersistence.banFromServer(userName);
WebClient.instance.response.moderator.banFromServer(userName);
},
});
}

View file

@ -1,14 +1,13 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { ModeratorPersistence } from '../../persistence';
import { Data } from '@app/types';
export function forceActivateUser(usernameToActivate: string, moderatorName: string): void {
const cmd = create(Data.Command_ForceActivateUserSchema, { usernameToActivate, moderatorName });
webClient.protobuf.sendModeratorCommand(Data.Command_ForceActivateUser_ext, cmd, {
WebClient.instance.protobuf.sendModeratorCommand(Data.Command_ForceActivateUser_ext, cmd, {
onSuccess: () => {
ModeratorPersistence.forceActivateUser(usernameToActivate, moderatorName);
WebClient.instance.response.moderator.forceActivateUser(usernameToActivate, moderatorName);
},
});
}

View file

@ -1,14 +1,13 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { ModeratorPersistence } from '../../persistence';
import { Data } from '@app/types';
export function getAdminNotes(userName: string): void {
webClient.protobuf.sendModeratorCommand(Data.Command_GetAdminNotes_ext, create(Data.Command_GetAdminNotesSchema, { userName }), {
WebClient.instance.protobuf.sendModeratorCommand(Data.Command_GetAdminNotes_ext, create(Data.Command_GetAdminNotesSchema, { userName }), {
responseExt: Data.Response_GetAdminNotes_ext,
onSuccess: (response) => {
ModeratorPersistence.getAdminNotes(userName, response.notes);
WebClient.instance.response.moderator.getAdminNotes(userName, response.notes);
},
});
}

View file

@ -1,14 +1,13 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { ModeratorPersistence } from '../../persistence';
import { Data } from '@app/types';
export function getBanHistory(userName: string): void {
webClient.protobuf.sendModeratorCommand(Data.Command_GetBanHistory_ext, create(Data.Command_GetBanHistorySchema, { userName }), {
WebClient.instance.protobuf.sendModeratorCommand(Data.Command_GetBanHistory_ext, create(Data.Command_GetBanHistorySchema, { userName }), {
responseExt: Data.Response_BanHistory_ext,
onSuccess: (response) => {
ModeratorPersistence.banHistory(userName, response.banList);
WebClient.instance.response.moderator.banHistory(userName, response.banList);
},
});
}

View file

@ -1,14 +1,17 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { ModeratorPersistence } from '../../persistence';
import { Data } from '@app/types';
export function getWarnHistory(userName: string): void {
webClient.protobuf.sendModeratorCommand(Data.Command_GetWarnHistory_ext, create(Data.Command_GetWarnHistorySchema, { userName }), {
responseExt: Data.Response_WarnHistory_ext,
onSuccess: (response) => {
ModeratorPersistence.warnHistory(userName, response.warnList);
},
});
WebClient.instance.protobuf.sendModeratorCommand(
Data.Command_GetWarnHistory_ext,
create(Data.Command_GetWarnHistorySchema, { userName }),
{
responseExt: Data.Response_WarnHistory_ext,
onSuccess: (response) => {
WebClient.instance.response.moderator.warnHistory(userName, response.warnList);
},
}
);
}

View file

@ -1,17 +1,16 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { ModeratorPersistence } from '../../persistence';
import { Data } from '@app/types';
export function getWarnList(modName: string, userName: string, userClientid: string): void {
webClient.protobuf.sendModeratorCommand(
WebClient.instance.protobuf.sendModeratorCommand(
Data.Command_GetWarnList_ext,
create(Data.Command_GetWarnListSchema, { modName, userName, userClientid }),
{
responseExt: Data.Response_WarnList_ext,
onSuccess: (response) => {
ModeratorPersistence.warnListOptions([response]);
WebClient.instance.response.moderator.warnListOptions([response]);
},
}
);

View file

@ -1,16 +1,15 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { ModeratorPersistence } from '../../persistence';
import { Data } from '@app/types';
export function grantReplayAccess(replayId: number, moderatorName: string): void {
webClient.protobuf.sendModeratorCommand(
WebClient.instance.protobuf.sendModeratorCommand(
Data.Command_GrantReplayAccess_ext,
create(Data.Command_GrantReplayAccessSchema, { replayId, moderatorName }),
{
onSuccess: () => {
ModeratorPersistence.grantReplayAccess(replayId, moderatorName);
WebClient.instance.response.moderator.grantReplayAccess(replayId, moderatorName);
},
},
);

View file

@ -1,27 +1,28 @@
vi.mock('../../WebClient', () => ({
__esModule: true,
default: { protobuf: { sendModeratorCommand: vi.fn() } },
}));
vi.mock('../../persistence', () => ({
ModeratorPersistence: {
banFromServer: vi.fn(),
forceActivateUser: vi.fn(),
getAdminNotes: vi.fn(),
banHistory: vi.fn(),
warnHistory: vi.fn(),
warnListOptions: vi.fn(),
grantReplayAccess: vi.fn(),
updateAdminNotes: vi.fn(),
viewLogs: vi.fn(),
warnUser: vi.fn(),
WebClient: {
instance: {
protobuf: { sendModeratorCommand: vi.fn() },
response: {
moderator: {
banFromServer: vi.fn(),
forceActivateUser: vi.fn(),
getAdminNotes: vi.fn(),
banHistory: vi.fn(),
warnHistory: vi.fn(),
warnListOptions: vi.fn(),
grantReplayAccess: vi.fn(),
updateAdminNotes: vi.fn(),
viewLogs: vi.fn(),
warnUser: vi.fn(),
},
},
},
},
}));
import { makeCallbackHelpers } from '../../__mocks__/callbackHelpers';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
import { ModeratorPersistence } from '../../persistence';
import { banFromServer } from './banFromServer';
import { forceActivateUser } from './forceActivateUser';
@ -37,7 +38,7 @@ import { create } from '@bufbuild/protobuf';
import { Mock } from 'vitest';
const { invokeOnSuccess } = makeCallbackHelpers(
webClient.protobuf.sendModeratorCommand as Mock,
WebClient.instance.protobuf.sendModeratorCommand as Mock,
2
);
@ -48,17 +49,17 @@ describe('banFromServer', () => {
it('calls sendModeratorCommand with Command_BanFromServer', () => {
banFromServer(30, 'alice', '1.2.3.4', 'reason', 'visible', 'cid', 1);
expect(webClient.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
Data.Command_BanFromServer_ext,
expect.objectContaining({ minutes: 30, userName: 'alice' }),
expect.any(Object)
);
});
it('onSuccess calls ModeratorPersistence.banFromServer', () => {
it('onSuccess calls response.moderator.banFromServer', () => {
banFromServer(30, 'alice');
invokeOnSuccess();
expect(ModeratorPersistence.banFromServer).toHaveBeenCalledWith('alice');
expect(WebClient.instance.response.moderator.banFromServer).toHaveBeenCalledWith('alice');
});
});
@ -69,15 +70,15 @@ describe('forceActivateUser', () => {
it('calls sendModeratorCommand with Command_ForceActivateUser', () => {
forceActivateUser('alice', 'mod1');
expect(webClient.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
Data.Command_ForceActivateUser_ext, expect.any(Object), expect.any(Object)
);
});
it('onSuccess calls ModeratorPersistence.forceActivateUser', () => {
it('onSuccess calls response.moderator.forceActivateUser', () => {
forceActivateUser('alice', 'mod1');
invokeOnSuccess();
expect(ModeratorPersistence.forceActivateUser).toHaveBeenCalledWith('alice', 'mod1');
expect(WebClient.instance.response.moderator.forceActivateUser).toHaveBeenCalledWith('alice', 'mod1');
});
});
@ -88,18 +89,18 @@ describe('getAdminNotes', () => {
it('calls sendModeratorCommand with Command_GetAdminNotes', () => {
getAdminNotes('alice');
expect(webClient.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
Data.Command_GetAdminNotes_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_GetAdminNotes_ext })
);
});
it('onSuccess calls ModeratorPersistence.getAdminNotes with notes', () => {
it('onSuccess calls response.moderator.getAdminNotes with notes', () => {
getAdminNotes('alice');
const resp = { notes: 'some notes' };
invokeOnSuccess(resp, { responseCode: 0 });
expect(ModeratorPersistence.getAdminNotes).toHaveBeenCalledWith('alice', 'some notes');
expect(WebClient.instance.response.moderator.getAdminNotes).toHaveBeenCalledWith('alice', 'some notes');
});
});
@ -110,18 +111,18 @@ describe('getBanHistory', () => {
it('calls sendModeratorCommand with Command_GetBanHistory', () => {
getBanHistory('alice');
expect(webClient.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
Data.Command_GetBanHistory_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_BanHistory_ext })
);
});
it('onSuccess calls ModeratorPersistence.banHistory with banList', () => {
it('onSuccess calls response.moderator.banHistory with banList', () => {
getBanHistory('alice');
const resp = { banList: [{ id: 1 }] };
invokeOnSuccess(resp, { responseCode: 0 });
expect(ModeratorPersistence.banHistory).toHaveBeenCalledWith('alice', [{ id: 1 }]);
expect(WebClient.instance.response.moderator.banHistory).toHaveBeenCalledWith('alice', [{ id: 1 }]);
});
});
@ -132,18 +133,18 @@ describe('getWarnHistory', () => {
it('calls sendModeratorCommand with Command_GetWarnHistory', () => {
getWarnHistory('alice');
expect(webClient.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
Data.Command_GetWarnHistory_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_WarnHistory_ext })
);
});
it('onSuccess calls ModeratorPersistence.warnHistory with warnList', () => {
it('onSuccess calls response.moderator.warnHistory with warnList', () => {
getWarnHistory('alice');
const resp = { warnList: [{ id: 2 }] };
invokeOnSuccess(resp, { responseCode: 0 });
expect(ModeratorPersistence.warnHistory).toHaveBeenCalledWith('alice', [{ id: 2 }]);
expect(WebClient.instance.response.moderator.warnHistory).toHaveBeenCalledWith('alice', [{ id: 2 }]);
});
});
@ -154,18 +155,18 @@ describe('getWarnList', () => {
it('calls sendModeratorCommand with Command_GetWarnList', () => {
getWarnList('mod1', 'alice', 'US');
expect(webClient.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
Data.Command_GetWarnList_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_WarnList_ext })
);
});
it('onSuccess calls ModeratorPersistence.warnListOptions with the response', () => {
it('onSuccess calls response.moderator.warnListOptions with the response', () => {
getWarnList('mod1', 'alice', 'US');
const resp = { warning: ['w1', 'w2'], userName: 'alice', userClientid: 'US' };
invokeOnSuccess(resp, { responseCode: 0 });
expect(ModeratorPersistence.warnListOptions).toHaveBeenCalledWith([resp]);
expect(WebClient.instance.response.moderator.warnListOptions).toHaveBeenCalledWith([resp]);
});
});
@ -176,15 +177,15 @@ describe('grantReplayAccess', () => {
it('calls sendModeratorCommand with Command_GrantReplayAccess', () => {
grantReplayAccess(10, 'mod1');
expect(webClient.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
Data.Command_GrantReplayAccess_ext, expect.any(Object), expect.any(Object)
);
});
it('onSuccess calls ModeratorPersistence.grantReplayAccess', () => {
it('onSuccess calls response.moderator.grantReplayAccess', () => {
grantReplayAccess(10, 'mod1');
invokeOnSuccess();
expect(ModeratorPersistence.grantReplayAccess).toHaveBeenCalledWith(10, 'mod1');
expect(WebClient.instance.response.moderator.grantReplayAccess).toHaveBeenCalledWith(10, 'mod1');
});
});
@ -195,15 +196,15 @@ describe('updateAdminNotes', () => {
it('calls sendModeratorCommand with Command_UpdateAdminNotes', () => {
updateAdminNotes('alice', 'new notes');
expect(webClient.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
Data.Command_UpdateAdminNotes_ext, expect.any(Object), expect.any(Object)
);
});
it('onSuccess calls ModeratorPersistence.updateAdminNotes', () => {
it('onSuccess calls response.moderator.updateAdminNotes', () => {
updateAdminNotes('alice', 'new notes');
invokeOnSuccess();
expect(ModeratorPersistence.updateAdminNotes).toHaveBeenCalledWith('alice', 'new notes');
expect(WebClient.instance.response.moderator.updateAdminNotes).toHaveBeenCalledWith('alice', 'new notes');
});
});
@ -215,19 +216,19 @@ describe('viewLogHistory', () => {
it('calls sendModeratorCommand with Command_ViewLogHistory', () => {
const filters = create(Data.Command_ViewLogHistorySchema, { dateRange: 7 });
viewLogHistory(filters);
expect(webClient.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
Data.Command_ViewLogHistory_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_ViewLogHistory_ext })
);
});
it('onSuccess calls ModeratorPersistence.viewLogs with logMessage', () => {
it('onSuccess calls response.moderator.viewLogs with logMessage', () => {
const filters = create(Data.Command_ViewLogHistorySchema, { dateRange: 7 });
viewLogHistory(filters);
const resp = { logMessage: ['log1'] };
invokeOnSuccess(resp, { responseCode: 0 });
expect(ModeratorPersistence.viewLogs).toHaveBeenCalledWith(['log1']);
expect(WebClient.instance.response.moderator.viewLogs).toHaveBeenCalledWith(['log1']);
});
});
@ -238,12 +239,14 @@ describe('warnUser', () => {
it('calls sendModeratorCommand with Command_WarnUser', () => {
warnUser('alice', 'bad behavior', 'cid');
expect(webClient.protobuf.sendModeratorCommand).toHaveBeenCalledWith(Data.Command_WarnUser_ext, expect.any(Object), expect.any(Object));
expect(WebClient.instance.protobuf.sendModeratorCommand).toHaveBeenCalledWith(
Data.Command_WarnUser_ext, expect.any(Object), expect.any(Object)
);
});
it('onSuccess calls ModeratorPersistence.warnUser', () => {
it('onSuccess calls response.moderator.warnUser', () => {
warnUser('alice', 'bad behavior', 'cid');
invokeOnSuccess();
expect(ModeratorPersistence.warnUser).toHaveBeenCalledWith('alice');
expect(WebClient.instance.response.moderator.warnUser).toHaveBeenCalledWith('alice');
});
});

View file

@ -1,16 +1,15 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { ModeratorPersistence } from '../../persistence';
import { Data } from '@app/types';
export function updateAdminNotes(userName: string, notes: string): void {
webClient.protobuf.sendModeratorCommand(
WebClient.instance.protobuf.sendModeratorCommand(
Data.Command_UpdateAdminNotes_ext,
create(Data.Command_UpdateAdminNotesSchema, { userName, notes }),
{
onSuccess: () => {
ModeratorPersistence.updateAdminNotes(userName, notes);
WebClient.instance.response.moderator.updateAdminNotes(userName, notes);
},
}
);

View file

@ -1,15 +1,13 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { ModeratorPersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function viewLogHistory(filters: Data.ViewLogHistoryParams): void {
webClient.protobuf.sendModeratorCommand(Data.Command_ViewLogHistory_ext, create(Data.Command_ViewLogHistorySchema, filters), {
WebClient.instance.protobuf.sendModeratorCommand(Data.Command_ViewLogHistory_ext, create(Data.Command_ViewLogHistorySchema, filters), {
responseExt: Data.Response_ViewLogHistory_ext,
onSuccess: (response) => {
ModeratorPersistence.viewLogs(response.logMessage);
WebClient.instance.response.moderator.viewLogs(response.logMessage);
},
});
}

View file

@ -1,14 +1,13 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { ModeratorPersistence } from '../../persistence';
import { Data } from '@app/types';
export function warnUser(userName: string, reason: string, clientid?: string, removeMessages?: number): void {
const cmd = create(Data.Command_WarnUserSchema, { userName, reason, clientid, removeMessages });
webClient.protobuf.sendModeratorCommand(Data.Command_WarnUser_ext, cmd, {
WebClient.instance.protobuf.sendModeratorCommand(Data.Command_WarnUser_ext, cmd, {
onSuccess: () => {
ModeratorPersistence.warnUser(userName);
WebClient.instance.response.moderator.warnUser(userName);
},
});
}

View file

@ -1,13 +1,12 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { RoomPersistence } from '../../persistence';
import { Data } from '@app/types';
export function createGame(roomId: number, gameConfig: Data.CreateGameParams): void {
webClient.protobuf.sendRoomCommand(roomId, Data.Command_CreateGame_ext, create(Data.Command_CreateGameSchema, gameConfig), {
WebClient.instance.protobuf.sendRoomCommand(roomId, Data.Command_CreateGame_ext, create(Data.Command_CreateGameSchema, gameConfig), {
onSuccess: () => {
RoomPersistence.gameCreated(roomId);
WebClient.instance.response.room.gameCreated(roomId);
},
});
}

View file

@ -1,13 +1,12 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { RoomPersistence } from '../../persistence';
import { Data } from '@app/types';
export function joinGame(roomId: number, joinGameParams: Data.JoinGameParams): void {
webClient.protobuf.sendRoomCommand(roomId, Data.Command_JoinGame_ext, create(Data.Command_JoinGameSchema, joinGameParams), {
WebClient.instance.protobuf.sendRoomCommand(roomId, Data.Command_JoinGame_ext, create(Data.Command_JoinGameSchema, joinGameParams), {
onSuccess: () => {
RoomPersistence.joinedGame(roomId, joinGameParams.gameId);
WebClient.instance.response.room.joinedGame(roomId, joinGameParams.gameId);
},
});
}

View file

@ -1,13 +1,12 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { RoomPersistence } from '../../persistence';
import { Data } from '@app/types';
export function leaveRoom(roomId: number): void {
webClient.protobuf.sendRoomCommand(roomId, Data.Command_LeaveRoom_ext, create(Data.Command_LeaveRoomSchema), {
WebClient.instance.protobuf.sendRoomCommand(roomId, Data.Command_LeaveRoom_ext, create(Data.Command_LeaveRoomSchema), {
onSuccess: () => {
RoomPersistence.leaveRoom(roomId);
WebClient.instance.response.room.leaveRoom(roomId);
},
});
}

View file

@ -1,20 +1,21 @@
vi.mock('../../WebClient', () => ({
__esModule: true,
default: { protobuf: { sendRoomCommand: vi.fn() } },
}));
vi.mock('../../persistence', () => ({
RoomPersistence: {
gameCreated: vi.fn(),
joinedGame: vi.fn(),
leaveRoom: vi.fn(),
WebClient: {
instance: {
protobuf: { sendRoomCommand: vi.fn() },
response: {
room: {
gameCreated: vi.fn(),
joinedGame: vi.fn(),
leaveRoom: vi.fn(),
},
},
},
},
}));
import { makeCallbackHelpers } from '../../__mocks__/callbackHelpers';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
import { RoomPersistence } from '../../persistence';
import { createGame } from './createGame';
import { joinGame } from './joinGame';
@ -24,7 +25,7 @@ import { create } from '@bufbuild/protobuf';
import { Mock } from 'vitest';
const { invokeOnSuccess } = makeCallbackHelpers(
webClient.protobuf.sendRoomCommand as Mock,
WebClient.instance.protobuf.sendRoomCommand as Mock,
// sendRoomCommand(roomId, ext, value, options) — options at index 3
3
);
@ -36,15 +37,15 @@ describe('createGame', () => {
it('calls sendRoomCommand with Command_CreateGame', () => {
createGame(5, create(Data.Command_CreateGameSchema, { maxPlayers: 4 }));
expect(webClient.protobuf.sendRoomCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendRoomCommand).toHaveBeenCalledWith(
5, Data.Command_CreateGame_ext, expect.objectContaining({ maxPlayers: 4 }), expect.any(Object)
);
});
it('onSuccess calls RoomPersistence.gameCreated with roomId', () => {
it('onSuccess calls response.room.gameCreated with roomId', () => {
createGame(5, create(Data.Command_CreateGameSchema, {}));
invokeOnSuccess();
expect(RoomPersistence.gameCreated).toHaveBeenCalledWith(5);
expect(WebClient.instance.response.room.gameCreated).toHaveBeenCalledWith(5);
});
});
@ -55,15 +56,15 @@ describe('joinGame', () => {
it('calls sendRoomCommand with Command_JoinGame', () => {
joinGame(7, create(Data.Command_JoinGameSchema, { gameId: 42, password: '' }));
expect(webClient.protobuf.sendRoomCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendRoomCommand).toHaveBeenCalledWith(
7, Data.Command_JoinGame_ext, expect.objectContaining({ gameId: 42, password: '' }), expect.any(Object)
);
});
it('onSuccess calls RoomPersistence.joinedGame with roomId and gameId', () => {
it('onSuccess calls response.room.joinedGame with roomId and gameId', () => {
joinGame(7, create(Data.Command_JoinGameSchema, { gameId: 42 }));
invokeOnSuccess();
expect(RoomPersistence.joinedGame).toHaveBeenCalledWith(7, 42);
expect(WebClient.instance.response.room.joinedGame).toHaveBeenCalledWith(7, 42);
});
});
@ -74,13 +75,15 @@ describe('leaveRoom', () => {
it('calls sendRoomCommand with Command_LeaveRoom', () => {
leaveRoom(3);
expect(webClient.protobuf.sendRoomCommand).toHaveBeenCalledWith(3, Data.Command_LeaveRoom_ext, expect.any(Object), expect.any(Object));
expect(WebClient.instance.protobuf.sendRoomCommand).toHaveBeenCalledWith(
3, Data.Command_LeaveRoom_ext, expect.any(Object), expect.any(Object)
);
});
it('onSuccess calls RoomPersistence.leaveRoom with roomId', () => {
it('onSuccess calls response.room.leaveRoom with roomId', () => {
leaveRoom(3);
invokeOnSuccess();
expect(RoomPersistence.leaveRoom).toHaveBeenCalledWith(3);
expect(WebClient.instance.response.room.leaveRoom).toHaveBeenCalledWith(3);
});
});
@ -91,7 +94,7 @@ describe('roomSay', () => {
it('calls sendRoomCommand with trimmed message', () => {
roomSay(2, ' hello ');
expect(webClient.protobuf.sendRoomCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendRoomCommand).toHaveBeenCalledWith(
2,
Data.Command_RoomSay_ext,
expect.objectContaining({ message: 'hello' })
@ -100,11 +103,11 @@ describe('roomSay', () => {
it('does not call sendRoomCommand when message is blank', () => {
roomSay(2, ' ');
expect(webClient.protobuf.sendRoomCommand).not.toHaveBeenCalled();
expect(WebClient.instance.protobuf.sendRoomCommand).not.toHaveBeenCalled();
});
it('does not call sendRoomCommand when message is empty string', () => {
roomSay(2, '');
expect(webClient.protobuf.sendRoomCommand).not.toHaveBeenCalled();
expect(WebClient.instance.protobuf.sendRoomCommand).not.toHaveBeenCalled();
});
});

View file

@ -1,5 +1,5 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function roomSay(roomId: number, message: string): void {
@ -9,5 +9,5 @@ export function roomSay(roomId: number, message: string): void {
return;
}
webClient.protobuf.sendRoomCommand(roomId, Data.Command_RoomSay_ext, create(Data.Command_RoomSaySchema, { message: trimmed }));
WebClient.instance.protobuf.sendRoomCommand(roomId, Data.Command_RoomSay_ext, create(Data.Command_RoomSaySchema, { message: trimmed }));
}

View file

@ -1,14 +1,13 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function accountEdit(passwordCheck: string, realName?: string, email?: string, country?: string): void {
const cmd = create(Data.Command_AccountEditSchema, { passwordCheck, realName, email, country });
webClient.protobuf.sendSessionCommand(Data.Command_AccountEdit_ext, cmd, {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_AccountEdit_ext, cmd, {
onSuccess: () => {
SessionPersistence.accountEditChanged(realName, email, country);
WebClient.instance.response.session.accountEditChanged(realName, email, country);
},
});
}

View file

@ -1,13 +1,12 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function accountImage(image: Uint8Array): void {
webClient.protobuf.sendSessionCommand(Data.Command_AccountImage_ext, create(Data.Command_AccountImageSchema, { image }), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_AccountImage_ext, create(Data.Command_AccountImageSchema, { image }), {
onSuccess: () => {
SessionPersistence.accountImageChanged(image);
WebClient.instance.response.session.accountImageChanged(image);
},
});
}

View file

@ -1,14 +1,13 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function accountPassword(oldPassword: string, newPassword: string, hashedNewPassword: string): void {
const cmd = create(Data.Command_AccountPasswordSchema, { oldPassword, newPassword, hashedNewPassword });
webClient.protobuf.sendSessionCommand(Data.Command_AccountPassword_ext, cmd, {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_AccountPassword_ext, cmd, {
onSuccess: () => {
SessionPersistence.accountPasswordChange();
WebClient.instance.response.session.accountPasswordChange();
},
});
}

View file

@ -2,23 +2,21 @@ import { App, Enriched, Data } from '@app/types';
import { create } from '@bufbuild/protobuf';
import { CLIENT_CONFIG } from '../../config';
import webClient from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
import { disconnect, login, updateStatus } from './';
export function activate(options: Omit<Enriched.ActivateConnectOptions, 'password'>, password?: string, passwordSalt?: string): void {
const { userName, token } = options;
webClient.protobuf.sendSessionCommand(Data.Command_Activate_ext, create(Data.Command_ActivateSchema, {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_Activate_ext, create(Data.Command_ActivateSchema, {
...CLIENT_CONFIG,
userName,
token,
}), {
onResponseCode: {
[Data.Response_ResponseCode.RespActivationAccepted]: () => {
SessionPersistence.accountActivationSuccess();
WebClient.instance.response.session.accountActivationSuccess();
login({
host: options.host,
port: options.port,
@ -30,7 +28,7 @@ export function activate(options: Omit<Enriched.ActivateConnectOptions, 'passwor
onError: () => {
updateStatus(App.StatusEnum.DISCONNECTED, 'Account Activation Failed');
disconnect();
SessionPersistence.accountActivationFailed();
WebClient.instance.response.session.accountActivationFailed();
},
});
}

View file

@ -1,7 +1,6 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function addToBuddyList(userName: string): void {
@ -13,9 +12,9 @@ export function addToIgnoreList(userName: string): void {
}
export function addToList(list: string, userName: string): void {
webClient.protobuf.sendSessionCommand(Data.Command_AddToList_ext, create(Data.Command_AddToListSchema, { list, userName }), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_AddToList_ext, create(Data.Command_AddToListSchema, { list, userName }), {
onSuccess: () => {
SessionPersistence.addToList(list, userName);
WebClient.instance.response.session.addToList(list, userName);
},
});
}

View file

@ -1,5 +1,5 @@
import { App, Enriched } from '@app/types';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { updateStatus } from './';
export function connect(options: Enriched.WebSocketConnectOptions): void {
@ -11,10 +11,10 @@ export function connect(options: Enriched.WebSocketConnectOptions): void {
case App.WebSocketConnectReason.PASSWORD_RESET_CHALLENGE:
case App.WebSocketConnectReason.PASSWORD_RESET:
updateStatus(App.StatusEnum.CONNECTING, 'Connecting...');
webClient.connect(options);
WebClient.instance.connect(options);
return;
case App.WebSocketConnectReason.TEST_CONNECTION:
webClient.testConnect(options);
WebClient.instance.testConnect(options);
return;
default: {
const { reason } = options as Enriched.WebSocketConnectOptions;

View file

@ -1,13 +1,12 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function deckDel(deckId: number): void {
webClient.protobuf.sendSessionCommand(Data.Command_DeckDel_ext, create(Data.Command_DeckDelSchema, { deckId }), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_DeckDel_ext, create(Data.Command_DeckDelSchema, { deckId }), {
onSuccess: () => {
SessionPersistence.deleteServerDeck(deckId);
WebClient.instance.response.session.deleteServerDeck(deckId);
},
});
}

View file

@ -1,13 +1,12 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function deckDelDir(path: string): void {
webClient.protobuf.sendSessionCommand(Data.Command_DeckDelDir_ext, create(Data.Command_DeckDelDirSchema, { path }), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_DeckDelDir_ext, create(Data.Command_DeckDelDirSchema, { path }), {
onSuccess: () => {
SessionPersistence.deleteServerDeckDir(path);
WebClient.instance.response.session.deleteServerDeckDir(path);
},
});
}

View file

@ -1,15 +1,14 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function deckList(): void {
webClient.protobuf.sendSessionCommand(Data.Command_DeckList_ext, create(Data.Command_DeckListSchema), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_DeckList_ext, create(Data.Command_DeckListSchema), {
responseExt: Data.Response_DeckList_ext,
onSuccess: (response) => {
if (response.root) {
SessionPersistence.updateServerDecks(response);
WebClient.instance.response.session.updateServerDecks(response);
}
},
});

View file

@ -1,13 +1,12 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function deckNewDir(path: string, dirName: string): void {
webClient.protobuf.sendSessionCommand(Data.Command_DeckNewDir_ext, create(Data.Command_DeckNewDirSchema, { path, dirName }), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_DeckNewDir_ext, create(Data.Command_DeckNewDirSchema, { path, dirName }), {
onSuccess: () => {
SessionPersistence.createServerDeckDir(path, dirName);
WebClient.instance.response.session.createServerDeckDir(path, dirName);
},
});
}

View file

@ -1,16 +1,19 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function deckUpload(path: string, deckId: number, deckList: string): void {
webClient.protobuf.sendSessionCommand(Data.Command_DeckUpload_ext, create(Data.Command_DeckUploadSchema, { path, deckId, deckList }), {
responseExt: Data.Response_DeckUpload_ext,
onSuccess: (response) => {
if (response.newFile) {
SessionPersistence.uploadServerDeck(path, response.newFile);
}
},
});
WebClient.instance.protobuf.sendSessionCommand(
Data.Command_DeckUpload_ext,
create(Data.Command_DeckUploadSchema, { path, deckId, deckList }),
{
responseExt: Data.Response_DeckUpload_ext,
onSuccess: (response) => {
if (response.newFile) {
WebClient.instance.response.session.uploadServerDeck(path, response.newFile);
}
},
}
);
}

View file

@ -1,5 +1,5 @@
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
export function disconnect(): void {
webClient.disconnect();
WebClient.instance.disconnect();
}

View file

@ -2,28 +2,31 @@ import { App, Enriched, Data } from '@app/types';
import { create } from '@bufbuild/protobuf';
import { CLIENT_CONFIG } from '../../config';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { disconnect, updateStatus } from './';
export function forgotPasswordChallenge(options: Enriched.PasswordResetChallengeConnectOptions): void {
const { userName, email } = options;
webClient.protobuf.sendSessionCommand(Data.Command_ForgotPasswordChallenge_ext, create(Data.Command_ForgotPasswordChallengeSchema, {
...CLIENT_CONFIG,
userName,
email,
}), {
onSuccess: () => {
updateStatus(App.StatusEnum.DISCONNECTED, null);
SessionPersistence.resetPassword();
disconnect();
},
onError: () => {
updateStatus(App.StatusEnum.DISCONNECTED, null);
SessionPersistence.resetPasswordFailed();
disconnect();
},
});
WebClient.instance.protobuf.sendSessionCommand(
Data.Command_ForgotPasswordChallenge_ext,
create(Data.Command_ForgotPasswordChallengeSchema, {
...CLIENT_CONFIG,
userName,
email,
}),
{
onSuccess: () => {
updateStatus(App.StatusEnum.DISCONNECTED, null);
WebClient.instance.response.session.resetPassword();
disconnect();
},
onError: () => {
updateStatus(App.StatusEnum.DISCONNECTED, null);
WebClient.instance.response.session.resetPasswordFailed();
disconnect();
},
}
);
}

View file

@ -2,16 +2,14 @@ import { App, Enriched, Data } from '@app/types';
import { create } from '@bufbuild/protobuf';
import { CLIENT_CONFIG } from '../../config';
import webClient from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
import { disconnect, updateStatus } from './';
export function forgotPasswordRequest(options: Enriched.PasswordResetRequestConnectOptions): void {
const { userName } = options;
webClient.protobuf.sendSessionCommand(Data.Command_ForgotPasswordRequest_ext, create(Data.Command_ForgotPasswordRequestSchema, {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_ForgotPasswordRequest_ext, create(Data.Command_ForgotPasswordRequestSchema, {
...CLIENT_CONFIG,
userName,
}), {
@ -19,16 +17,16 @@ export function forgotPasswordRequest(options: Enriched.PasswordResetRequestConn
onSuccess: (resp) => {
if (resp?.challengeEmail) {
updateStatus(App.StatusEnum.DISCONNECTED, null);
SessionPersistence.resetPasswordChallenge();
WebClient.instance.response.session.resetPasswordChallenge();
} else {
updateStatus(App.StatusEnum.DISCONNECTED, null);
SessionPersistence.resetPassword();
WebClient.instance.response.session.resetPassword();
}
disconnect();
},
onError: () => {
updateStatus(App.StatusEnum.DISCONNECTED, null);
SessionPersistence.resetPasswordFailed();
WebClient.instance.response.session.resetPasswordFailed();
disconnect();
},
});

View file

@ -3,9 +3,8 @@ import { App, Enriched, Data } from '@app/types';
import { create } from '@bufbuild/protobuf';
import type { MessageInitShape } from '@bufbuild/protobuf';
import { CLIENT_CONFIG } from '../../config';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { hashPassword } from '../../utils';
import { disconnect, updateStatus } from '.';
@ -26,16 +25,20 @@ export function forgotPasswordReset(
: { newPassword }),
};
webClient.protobuf.sendSessionCommand(Data.Command_ForgotPasswordReset_ext, create(Data.Command_ForgotPasswordResetSchema, params), {
onSuccess: () => {
updateStatus(App.StatusEnum.DISCONNECTED, null);
SessionPersistence.resetPasswordSuccess();
disconnect();
},
onError: () => {
updateStatus(App.StatusEnum.DISCONNECTED, null);
SessionPersistence.resetPasswordFailed();
disconnect();
},
});
WebClient.instance.protobuf.sendSessionCommand(
Data.Command_ForgotPasswordReset_ext,
create(Data.Command_ForgotPasswordResetSchema, params),
{
onSuccess: () => {
updateStatus(App.StatusEnum.DISCONNECTED, null);
WebClient.instance.response.session.resetPasswordSuccess();
disconnect();
},
onError: () => {
updateStatus(App.StatusEnum.DISCONNECTED, null);
WebClient.instance.response.session.resetPasswordFailed();
disconnect();
},
}
);
}

View file

@ -1,14 +1,13 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function getGamesOfUser(userName: string): void {
webClient.protobuf.sendSessionCommand(Data.Command_GetGamesOfUser_ext, create(Data.Command_GetGamesOfUserSchema, { userName }), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_GetGamesOfUser_ext, create(Data.Command_GetGamesOfUserSchema, { userName }), {
responseExt: Data.Response_GetGamesOfUser_ext,
onSuccess: (response) => {
SessionPersistence.getGamesOfUser(userName, response);
WebClient.instance.response.session.getGamesOfUser(userName, response);
},
});
}

View file

@ -1,14 +1,13 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function getUserInfo(userName: string): void {
webClient.protobuf.sendSessionCommand(Data.Command_GetUserInfo_ext, create(Data.Command_GetUserInfoSchema, { userName }), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_GetUserInfo_ext, create(Data.Command_GetUserInfoSchema, { userName }), {
responseExt: Data.Response_GetUserInfo_ext,
onSuccess: (response) => {
SessionPersistence.getUserInfo(response.userInfo);
WebClient.instance.response.session.getUserInfo(response.userInfo);
},
});
}

View file

@ -1,15 +1,14 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { RoomPersistence } from '../../persistence';
import { Data } from '@app/types';
export function joinRoom(roomId: number): void {
webClient.protobuf.sendSessionCommand(Data.Command_JoinRoom_ext, create(Data.Command_JoinRoomSchema, { roomId }), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_JoinRoom_ext, create(Data.Command_JoinRoomSchema, { roomId }), {
responseExt: Data.Response_JoinRoom_ext,
onSuccess: (response) => {
if (response.roomInfo) {
RoomPersistence.joinRoom(response.roomInfo);
WebClient.instance.response.room.joinRoom(response.roomInfo);
}
},
});

View file

@ -1,7 +1,7 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function listRooms(): void {
webClient.protobuf.sendSessionCommand(Data.Command_ListRooms_ext, create(Data.Command_ListRoomsSchema));
WebClient.instance.protobuf.sendSessionCommand(Data.Command_ListRooms_ext, create(Data.Command_ListRoomsSchema));
}

View file

@ -1,14 +1,13 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function listUsers(): void {
webClient.protobuf.sendSessionCommand(Data.Command_ListUsers_ext, create(Data.Command_ListUsersSchema), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_ListUsers_ext, create(Data.Command_ListUsersSchema), {
responseExt: Data.Response_ListUsers_ext,
onSuccess: (response) => {
SessionPersistence.updateUsers(response.userList);
WebClient.instance.response.session.updateUsers(response.userList);
},
});
}

View file

@ -2,11 +2,9 @@ import { App, Enriched, Data } from '@app/types';
import { create } from '@bufbuild/protobuf';
import type { MessageInitShape } from '@bufbuild/protobuf';
import { CLIENT_CONFIG } from '../../config';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { hashPassword } from '../../utils';
import { SessionPersistence } from '../../persistence';
import {
disconnect,
listUsers,
@ -29,19 +27,19 @@ export function login(options: Omit<Enriched.LoginConnectOptions, 'password'>, p
const onLoginError = (message: string, extra?: () => void) => {
updateStatus(App.StatusEnum.DISCONNECTED, message);
extra?.();
SessionPersistence.loginFailed();
WebClient.instance.response.session.loginFailed();
disconnect();
};
webClient.protobuf.sendSessionCommand(Data.Command_Login_ext, create(Data.Command_LoginSchema, loginConfig), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_Login_ext, create(Data.Command_LoginSchema, loginConfig), {
responseExt: Data.Response_Login_ext,
onSuccess: (resp) => {
const { buddyList, ignoreList, userInfo } = resp;
SessionPersistence.updateBuddyList(buddyList);
SessionPersistence.updateIgnoreList(ignoreList);
SessionPersistence.updateUser(userInfo);
SessionPersistence.loginSuccessful({ hashedPassword: loginConfig.hashedPassword });
WebClient.instance.response.session.updateBuddyList(buddyList);
WebClient.instance.response.session.updateIgnoreList(ignoreList);
WebClient.instance.response.session.updateUser(userInfo);
WebClient.instance.response.session.loginSuccessful({ hashedPassword: loginConfig.hashedPassword });
listUsers();
listRooms();
@ -68,7 +66,7 @@ export function login(options: Omit<Enriched.LoginConnectOptions, 'password'>, p
[Data.Response_ResponseCode.RespAccountNotActivated]: () =>
onLoginError('Login failed: account not activated',
() => {
SessionPersistence.accountAwaitingActivation({
WebClient.instance.response.session.accountAwaitingActivation({
host: options.host,
port: options.port,
userName: options.userName,

View file

@ -1,7 +1,7 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function message(userName: string, message: string): void {
webClient.protobuf.sendSessionCommand(Data.Command_Message_ext, create(Data.Command_MessageSchema, { userName, message }));
WebClient.instance.protobuf.sendSessionCommand(Data.Command_Message_ext, create(Data.Command_MessageSchema, { userName, message }));
}

View file

@ -1,9 +1,9 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function ping(pingReceived: () => void): void {
webClient.protobuf.sendSessionCommand(Data.Command_Ping_ext, create(Data.Command_PingSchema), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_Ping_ext, create(Data.Command_PingSchema), {
onResponse: () => pingReceived(),
});
}

View file

@ -3,9 +3,8 @@ import { App, Enriched, Data } from '@app/types';
import { create, getExtension } from '@bufbuild/protobuf';
import type { MessageInitShape } from '@bufbuild/protobuf';
import { CLIENT_CONFIG } from '../../config';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { hashPassword } from '../../utils';
import { login, disconnect, updateStatus } from './';
@ -30,7 +29,7 @@ export function register(options: Omit<Enriched.RegisterConnectOptions, 'passwor
disconnect();
};
webClient.protobuf.sendSessionCommand(Data.Command_Register_ext, create(Data.Command_RegisterSchema, params), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_Register_ext, create(Data.Command_RegisterSchema, params), {
onResponseCode: {
[Data.Response_ResponseCode.RespRegistrationAccepted]: () => {
login({
@ -39,11 +38,11 @@ export function register(options: Omit<Enriched.RegisterConnectOptions, 'passwor
userName: options.userName,
reason: App.WebSocketConnectReason.LOGIN,
}, password, passwordSalt);
SessionPersistence.registrationSuccess();
WebClient.instance.response.session.registrationSuccess();
},
[Data.Response_ResponseCode.RespRegistrationAcceptedNeedsActivation]: () => {
updateStatus(App.StatusEnum.DISCONNECTED, 'Registration accepted, awaiting activation');
SessionPersistence.accountAwaitingActivation({
WebClient.instance.response.session.accountAwaitingActivation({
host: options.host,
port: options.port,
userName: options.userName,
@ -51,35 +50,35 @@ export function register(options: Omit<Enriched.RegisterConnectOptions, 'passwor
disconnect();
},
[Data.Response_ResponseCode.RespUserAlreadyExists]: () => onRegistrationError(
() => SessionPersistence.registrationUserNameError('Username is taken')
() => WebClient.instance.response.session.registrationUserNameError('Username is taken')
),
[Data.Response_ResponseCode.RespUsernameInvalid]: () => onRegistrationError(
() => SessionPersistence.registrationUserNameError('Invalid username')
() => WebClient.instance.response.session.registrationUserNameError('Invalid username')
),
[Data.Response_ResponseCode.RespPasswordTooShort]: () => onRegistrationError(
() => SessionPersistence.registrationPasswordError('Your password was too short')
() => WebClient.instance.response.session.registrationPasswordError('Your password was too short')
),
[Data.Response_ResponseCode.RespEmailRequiredToRegister]: () => onRegistrationError(
() => SessionPersistence.registrationRequiresEmail()
() => WebClient.instance.response.session.registrationRequiresEmail()
),
[Data.Response_ResponseCode.RespEmailBlackListed]: () => onRegistrationError(
() => SessionPersistence.registrationEmailError('This email provider has been blocked')
() => WebClient.instance.response.session.registrationEmailError('This email provider has been blocked')
),
[Data.Response_ResponseCode.RespTooManyRequests]: () => onRegistrationError(
() => SessionPersistence.registrationEmailError('Max accounts reached for this email')
() => WebClient.instance.response.session.registrationEmailError('Max accounts reached for this email')
),
[Data.Response_ResponseCode.RespRegistrationDisabled]: () => onRegistrationError(
() => SessionPersistence.registrationFailed('Registration is currently disabled')
() => WebClient.instance.response.session.registrationFailed('Registration is currently disabled')
),
[Data.Response_ResponseCode.RespUserIsBanned]: (raw) => {
const register = getExtension(raw, Data.Response_Register_ext);
onRegistrationError(
() => SessionPersistence.registrationFailed(register.deniedReasonStr, Number(register.deniedEndTime))
() => WebClient.instance.response.session.registrationFailed(register.deniedReasonStr, Number(register.deniedEndTime))
);
},
},
onError: () => onRegistrationError(
() => SessionPersistence.registrationFailed('Registration failed due to a server issue')
() => WebClient.instance.response.session.registrationFailed('Registration failed due to a server issue')
),
});
}

View file

@ -1,7 +1,6 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function removeFromBuddyList(userName: string): void {
@ -13,9 +12,13 @@ export function removeFromIgnoreList(userName: string): void {
}
export function removeFromList(list: string, userName: string): void {
webClient.protobuf.sendSessionCommand(Data.Command_RemoveFromList_ext, create(Data.Command_RemoveFromListSchema, { list, userName }), {
onSuccess: () => {
SessionPersistence.removeFromList(list, userName);
},
});
WebClient.instance.protobuf.sendSessionCommand(
Data.Command_RemoveFromList_ext,
create(Data.Command_RemoveFromListSchema, { list, userName }),
{
onSuccess: () => {
WebClient.instance.response.session.removeFromList(list, userName);
},
}
);
}

View file

@ -1,13 +1,16 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function replayDeleteMatch(gameId: number): void {
webClient.protobuf.sendSessionCommand(Data.Command_ReplayDeleteMatch_ext, create(Data.Command_ReplayDeleteMatchSchema, { gameId }), {
onSuccess: () => {
SessionPersistence.replayDeleteMatch(gameId);
},
});
WebClient.instance.protobuf.sendSessionCommand(
Data.Command_ReplayDeleteMatch_ext,
create(Data.Command_ReplayDeleteMatchSchema, { gameId }),
{
onSuccess: () => {
WebClient.instance.response.session.replayDeleteMatch(gameId);
},
}
);
}

View file

@ -1,9 +1,9 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function replayGetCode(gameId: number, onCodeReceived: (code: string) => void): void {
webClient.protobuf.sendSessionCommand(Data.Command_ReplayGetCode_ext, create(Data.Command_ReplayGetCodeSchema, { gameId }), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_ReplayGetCode_ext, create(Data.Command_ReplayGetCodeSchema, { gameId }), {
responseExt: Data.Response_ReplayGetCode_ext,
onSuccess: (response) => {
onCodeReceived(response.replayCode);

View file

@ -1,14 +1,13 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function replayList(): void {
webClient.protobuf.sendSessionCommand(Data.Command_ReplayList_ext, create(Data.Command_ReplayListSchema), {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_ReplayList_ext, create(Data.Command_ReplayListSchema), {
responseExt: Data.Response_ReplayList_ext,
onSuccess: (response) => {
SessionPersistence.replayList(response.matchList);
WebClient.instance.response.session.replayList(response.matchList);
},
});
}

View file

@ -1,16 +1,15 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { Data } from '@app/types';
export function replayModifyMatch(gameId: number, doNotHide: boolean): void {
webClient.protobuf.sendSessionCommand(
WebClient.instance.protobuf.sendSessionCommand(
Data.Command_ReplayModifyMatch_ext,
create(Data.Command_ReplayModifyMatchSchema, { gameId, doNotHide }),
{
onSuccess: () => {
SessionPersistence.replayModifyMatch(gameId, doNotHide);
WebClient.instance.response.session.replayModifyMatch(gameId, doNotHide);
},
}
);

View file

@ -1,5 +1,5 @@
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { Data } from '@app/types';
export function replaySubmitCode(
@ -7,8 +7,12 @@ export function replaySubmitCode(
onSuccess?: () => void,
onError?: (responseCode: number) => void,
): void {
webClient.protobuf.sendSessionCommand(Data.Command_ReplaySubmitCode_ext, create(Data.Command_ReplaySubmitCodeSchema, { replayCode }), {
onSuccess,
onError,
});
WebClient.instance.protobuf.sendSessionCommand(
Data.Command_ReplaySubmitCode_ext,
create(Data.Command_ReplaySubmitCodeSchema, { replayCode }),
{
onSuccess,
onError,
}
);
}

View file

@ -2,9 +2,7 @@ import { App, Enriched, Data } from '@app/types';
import { create } from '@bufbuild/protobuf';
import { CLIENT_CONFIG } from '../../config';
import webClient from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
import {
activate,
@ -25,18 +23,18 @@ export function requestPasswordSalt(options: PasswordSaltOptions, password?: str
const onFailure = () => {
switch (options.reason) {
case App.WebSocketConnectReason.ACTIVATE_ACCOUNT:
SessionPersistence.accountActivationFailed();
WebClient.instance.response.session.accountActivationFailed();
break;
case App.WebSocketConnectReason.PASSWORD_RESET:
SessionPersistence.resetPasswordFailed();
WebClient.instance.response.session.resetPasswordFailed();
break;
default:
SessionPersistence.loginFailed();
WebClient.instance.response.session.loginFailed();
}
disconnect();
};
webClient.protobuf.sendSessionCommand(Data.Command_RequestPasswordSalt_ext, create(Data.Command_RequestPasswordSaltSchema, {
WebClient.instance.protobuf.sendSessionCommand(Data.Command_RequestPasswordSalt_ext, create(Data.Command_RequestPasswordSaltSchema, {
...CLIENT_CONFIG,
userName,
}), {

View file

@ -1,17 +1,9 @@
// Tests for complex session commands that call webClient directly
// Tests for complex session commands that call WebClient directly
// or have multiple branching callbacks.
vi.mock('../../persistence', async () => {
const { makeSessionPersistenceMock } = await import('../../__mocks__/sessionCommandMocks');
return {
SessionPersistence: makeSessionPersistenceMock(),
RoomPersistence: {},
};
});
vi.mock('../../WebClient', async () => {
const { makeWebClientMock } = await import('../../__mocks__/sessionCommandMocks');
return { __esModule: true, default: makeWebClientMock() };
return { WebClient: { instance: makeWebClientMock() } };
});
vi.mock('../../utils', async () => {
@ -27,8 +19,7 @@ vi.mock('./', async () => {
import { Mock } from 'vitest';
import { makeCallbackHelpers } from '../../__mocks__/callbackHelpers';
import { SessionPersistence } from '../../persistence';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import * as SessionIndexMocks from './';
import { App, Enriched, Data } from '@app/types';
import { hashPassword, generateSalt, passwordSaltSupported } from '../../utils';
@ -45,8 +36,9 @@ import { forgotPasswordRequest } from './forgotPasswordRequest';
import { forgotPasswordReset } from './forgotPasswordReset';
import { requestPasswordSalt } from './requestPasswordSalt';
const { invokeOnSuccess, invokeResponseCode, invokeOnError } = makeCallbackHelpers(
webClient.protobuf.sendSessionCommand as Mock,
WebClient.instance.protobuf.sendSessionCommand as Mock,
2
);
@ -118,7 +110,7 @@ describe('connect', () => {
it('calls updateStatus CONNECTING for LOGIN reason', () => {
connect({ host: 'h', port: '1', userName: 'u', reason: App.WebSocketConnectReason.LOGIN });
expect(SessionIndexMocks.updateStatus).toHaveBeenCalledWith(App.StatusEnum.CONNECTING, 'Connecting...');
expect(webClient.connect).toHaveBeenCalled();
expect(WebClient.instance.connect).toHaveBeenCalled();
});
it('calls updateStatus CONNECTING for REGISTER reason', () => {
@ -148,8 +140,8 @@ describe('connect', () => {
it('calls testConnect for TEST_CONNECTION reason', () => {
connect({ host: 'h', port: '1', reason: App.WebSocketConnectReason.TEST_CONNECTION });
expect(webClient.testConnect).toHaveBeenCalled();
expect(webClient.connect).not.toHaveBeenCalled();
expect(WebClient.instance.testConnect).toHaveBeenCalled();
expect(WebClient.instance.connect).not.toHaveBeenCalled();
});
it('calls updateStatus DISCONNECTED for unknown reason', () => {
@ -164,10 +156,10 @@ describe('connect', () => {
// ----------------------------------------------------------------
describe('updateStatus', () => {
it('calls SessionPersistence.updateStatus and webClient.updateStatus', () => {
it('calls WebClient.instance.response.session.updateStatus and WebClient.instance.updateStatus', () => {
updateStatus(App.StatusEnum.CONNECTED, 'OK');
expect(SessionPersistence.updateStatus).toHaveBeenCalledWith(App.StatusEnum.CONNECTED, 'OK');
expect(webClient.updateStatus).toHaveBeenCalledWith(App.StatusEnum.CONNECTED);
expect(WebClient.instance.response.session.updateStatus).toHaveBeenCalledWith(App.StatusEnum.CONNECTED, 'OK');
expect(WebClient.instance.updateStatus).toHaveBeenCalledWith(App.StatusEnum.CONNECTED);
});
});
@ -178,7 +170,7 @@ describe('login', () => {
it('sends Command_Login with plain password when no salt', () => {
login(makeLoginOpts(), 'pw');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_Login_ext,
expect.objectContaining({ password: 'pw' }),
expect.objectContaining({ responseExt: Data.Response_Login_ext })
@ -187,7 +179,7 @@ describe('login', () => {
it('sends Command_Login with hashedPassword when salt is given', () => {
login(makeLoginOpts(), 'pw', 'salt');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_Login_ext,
expect.objectContaining({ hashedPassword: 'hashed_pw' }),
expect.objectContaining({ responseExt: Data.Response_Login_ext })
@ -196,7 +188,7 @@ describe('login', () => {
it('uses options.hashedPassword if provided', () => {
login(makeLoginOpts({ hashedPassword: 'pre_hashed' }), 'pw', 'salt');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_Login_ext,
expect.objectContaining({ hashedPassword: 'pre_hashed' }),
expect.objectContaining({ responseExt: Data.Response_Login_ext })
@ -207,10 +199,10 @@ describe('login', () => {
login(makeLoginOpts(), 'pw');
const loginResp = { buddyList: [], ignoreList: [], userInfo: { name: 'alice' } };
invokeOnSuccess(loginResp, { responseCode: 0 });
expect(SessionPersistence.updateBuddyList).toHaveBeenCalledWith([]);
expect(SessionPersistence.updateIgnoreList).toHaveBeenCalledWith([]);
expect(SessionPersistence.updateUser).toHaveBeenCalledWith({ name: 'alice' });
expect(SessionPersistence.loginSuccessful).toHaveBeenCalled();
expect(WebClient.instance.response.session.updateBuddyList).toHaveBeenCalledWith([]);
expect(WebClient.instance.response.session.updateIgnoreList).toHaveBeenCalledWith([]);
expect(WebClient.instance.response.session.updateUser).toHaveBeenCalledWith({ name: 'alice' });
expect(WebClient.instance.response.session.loginSuccessful).toHaveBeenCalled();
expect(SessionIndexMocks.listUsers).toHaveBeenCalled();
expect(SessionIndexMocks.listRooms).toHaveBeenCalled();
expect(SessionIndexMocks.updateStatus).toHaveBeenCalledWith(App.StatusEnum.LOGGED_IN, 'Logged in.');
@ -220,7 +212,7 @@ describe('login', () => {
login(makeLoginOpts(), 'secret');
const loginResp = { buddyList: [], ignoreList: [], userInfo: { name: 'alice' } };
invokeOnSuccess(loginResp, { responseCode: 0 });
const calledWith = (SessionPersistence.loginSuccessful as Mock).mock.calls[0][0];
const calledWith = (WebClient.instance.response.session.loginSuccessful as Mock).mock.calls[0][0];
expect(calledWith).not.toHaveProperty('password');
});
@ -228,72 +220,72 @@ describe('login', () => {
login({ host: 'h', port: '1', userName: 'alice', reason: App.WebSocketConnectReason.LOGIN }, 'pw', 'salt');
const loginResp = { buddyList: [], ignoreList: [], userInfo: { name: 'alice' } };
invokeOnSuccess(loginResp, { responseCode: 0 });
const calledWith = (SessionPersistence.loginSuccessful as Mock).mock.calls[0][0];
const calledWith = (WebClient.instance.response.session.loginSuccessful as Mock).mock.calls[0][0];
expect(calledWith).toHaveProperty('hashedPassword', 'hashed_pw');
});
it('onResponseCode RespClientUpdateRequired calls onLoginError', () => {
login(makeLoginOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespClientUpdateRequired);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.loginFailed).toHaveBeenCalled();
expect(SessionIndexMocks.disconnect).toHaveBeenCalled();
});
it('onResponseCode RespWrongPassword', () => {
login(makeLoginOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespWrongPassword);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.loginFailed).toHaveBeenCalled();
});
it('onResponseCode RespUsernameInvalid', () => {
login(makeLoginOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespUsernameInvalid);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.loginFailed).toHaveBeenCalled();
});
it('onResponseCode RespWouldOverwriteOldSession', () => {
login(makeLoginOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespWouldOverwriteOldSession);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.loginFailed).toHaveBeenCalled();
});
it('onResponseCode RespUserIsBanned', () => {
login(makeLoginOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespUserIsBanned);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.loginFailed).toHaveBeenCalled();
});
it('onResponseCode RespRegistrationRequired', () => {
login(makeLoginOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespRegistrationRequired);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.loginFailed).toHaveBeenCalled();
});
it('onResponseCode RespClientIdRequired', () => {
login(makeLoginOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespClientIdRequired);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.loginFailed).toHaveBeenCalled();
});
it('onResponseCode RespContextError', () => {
login(makeLoginOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespContextError);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.loginFailed).toHaveBeenCalled();
});
it('onResponseCode RespAccountNotActivated calls accountAwaitingActivation without password in options', () => {
login(makeLoginOpts({ password: 'leaked' }), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespAccountNotActivated);
expect(SessionPersistence.accountAwaitingActivation).toHaveBeenCalledWith(
expect(WebClient.instance.response.session.accountAwaitingActivation).toHaveBeenCalledWith(
expect.not.objectContaining({ password: expect.anything() })
);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.loginFailed).toHaveBeenCalled();
});
it('onError calls onLoginError with unknown error message', () => {
login(makeLoginOpts(), 'pw');
invokeOnError(999);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.loginFailed).toHaveBeenCalled();
});
});
@ -304,7 +296,7 @@ describe('register', () => {
it('sends Command_Register with plain password when no salt', () => {
register(makeRegisterOpts(), 'pw');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_Register_ext,
expect.objectContaining({ password: 'pw' }),
expect.any(Object)
@ -313,7 +305,7 @@ describe('register', () => {
it('uses hashedPassword when salt is provided', () => {
register(makeRegisterOpts(), 'pw', 'salt');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_Register_ext,
expect.objectContaining({ hashedPassword: 'hashed_pw' }),
expect.any(Object)
@ -324,20 +316,20 @@ describe('register', () => {
register(makeRegisterOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespRegistrationAccepted);
expect(SessionIndexMocks.login).toHaveBeenCalledWith(expect.any(Object), 'pw', undefined);
expect(SessionPersistence.registrationSuccess).toHaveBeenCalled();
expect(WebClient.instance.response.session.registrationSuccess).toHaveBeenCalled();
});
it('RespRegistrationAccepted forwards salt to login', () => {
register(makeRegisterOpts(), 'pw', 'mySalt');
invokeResponseCode(Data.Response_ResponseCode.RespRegistrationAccepted);
expect(SessionIndexMocks.login).toHaveBeenCalledWith(expect.any(Object), 'pw', 'mySalt');
expect(SessionPersistence.registrationSuccess).toHaveBeenCalled();
expect(WebClient.instance.response.session.registrationSuccess).toHaveBeenCalled();
});
it('RespRegistrationAcceptedNeedsActivation calls accountAwaitingActivation without password in options', () => {
register(makeRegisterOpts({ password: 'leaked' }), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespRegistrationAcceptedNeedsActivation);
expect(SessionPersistence.accountAwaitingActivation).toHaveBeenCalledWith(
expect(WebClient.instance.response.session.accountAwaitingActivation).toHaveBeenCalledWith(
expect.not.objectContaining({ password: expect.anything() })
);
expect(SessionIndexMocks.disconnect).toHaveBeenCalled();
@ -346,43 +338,43 @@ describe('register', () => {
it('RespUserAlreadyExists calls registrationUserNameError', () => {
register(makeRegisterOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespUserAlreadyExists);
expect(SessionPersistence.registrationUserNameError).toHaveBeenCalled();
expect(WebClient.instance.response.session.registrationUserNameError).toHaveBeenCalled();
});
it('RespUsernameInvalid calls registrationUserNameError', () => {
register(makeRegisterOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespUsernameInvalid);
expect(SessionPersistence.registrationUserNameError).toHaveBeenCalled();
expect(WebClient.instance.response.session.registrationUserNameError).toHaveBeenCalled();
});
it('RespPasswordTooShort calls registrationPasswordError', () => {
register(makeRegisterOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespPasswordTooShort);
expect(SessionPersistence.registrationPasswordError).toHaveBeenCalled();
expect(WebClient.instance.response.session.registrationPasswordError).toHaveBeenCalled();
});
it('RespEmailRequiredToRegister calls registrationRequiresEmail', () => {
register(makeRegisterOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespEmailRequiredToRegister);
expect(SessionPersistence.registrationRequiresEmail).toHaveBeenCalled();
expect(WebClient.instance.response.session.registrationRequiresEmail).toHaveBeenCalled();
});
it('RespEmailBlackListed calls registrationEmailError', () => {
register(makeRegisterOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespEmailBlackListed);
expect(SessionPersistence.registrationEmailError).toHaveBeenCalled();
expect(WebClient.instance.response.session.registrationEmailError).toHaveBeenCalled();
});
it('RespTooManyRequests calls registrationEmailError', () => {
register(makeRegisterOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespTooManyRequests);
expect(SessionPersistence.registrationEmailError).toHaveBeenCalled();
expect(WebClient.instance.response.session.registrationEmailError).toHaveBeenCalled();
});
it('RespRegistrationDisabled calls registrationFailed', () => {
register(makeRegisterOpts(), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespRegistrationDisabled);
expect(SessionPersistence.registrationFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.registrationFailed).toHaveBeenCalled();
});
it('RespUserIsBanned calls registrationFailed with deniedReasonStr and deniedEndTime', () => {
@ -392,13 +384,13 @@ describe('register', () => {
deniedReasonStr: 'bad user', deniedEndTime: 9999n,
}));
invokeResponseCode(Data.Response_ResponseCode.RespUserIsBanned, raw);
expect(SessionPersistence.registrationFailed).toHaveBeenCalledWith('bad user', 9999);
expect(WebClient.instance.response.session.registrationFailed).toHaveBeenCalledWith('bad user', 9999);
});
it('onError calls registrationFailed', () => {
register(makeRegisterOpts(), 'pw');
invokeOnError();
expect(SessionPersistence.registrationFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.registrationFailed).toHaveBeenCalled();
});
});
@ -409,12 +401,12 @@ describe('activate', () => {
it('sends Command_Activate with userName and token, not password', () => {
activate(makeActivateOpts(), 'pw');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_Activate_ext,
expect.objectContaining({ userName: 'alice', token: 'tok' }),
expect.any(Object)
);
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_Activate_ext,
expect.not.objectContaining({ password: expect.anything() }),
expect.any(Object)
@ -424,14 +416,14 @@ describe('activate', () => {
it('RespActivationAccepted calls accountActivationSuccess and forwards password+salt to login', () => {
activate(makeActivateOpts(), 'pw', 'salt');
invokeResponseCode(Data.Response_ResponseCode.RespActivationAccepted);
expect(SessionPersistence.accountActivationSuccess).toHaveBeenCalled();
expect(WebClient.instance.response.session.accountActivationSuccess).toHaveBeenCalled();
expect(SessionIndexMocks.login).toHaveBeenCalledWith(expect.any(Object), 'pw', 'salt');
});
it('onError calls accountActivationFailed and disconnect', () => {
activate(makeActivateOpts());
invokeOnError();
expect(SessionPersistence.accountActivationFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.accountActivationFailed).toHaveBeenCalled();
expect(SessionIndexMocks.disconnect).toHaveBeenCalled();
});
});
@ -443,7 +435,7 @@ describe('forgotPasswordChallenge', () => {
it('sends Command_ForgotPasswordChallenge', () => {
forgotPasswordChallenge(makeForgotChallengeOpts());
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ForgotPasswordChallenge_ext, expect.any(Object), expect.any(Object)
);
});
@ -451,14 +443,14 @@ describe('forgotPasswordChallenge', () => {
it('onSuccess calls resetPassword and disconnect', () => {
forgotPasswordChallenge(makeForgotChallengeOpts());
invokeOnSuccess();
expect(SessionPersistence.resetPassword).toHaveBeenCalled();
expect(WebClient.instance.response.session.resetPassword).toHaveBeenCalled();
expect(SessionIndexMocks.disconnect).toHaveBeenCalled();
});
it('onError calls resetPasswordFailed and disconnect', () => {
forgotPasswordChallenge(makeForgotChallengeOpts());
invokeOnError();
expect(SessionPersistence.resetPasswordFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.resetPasswordFailed).toHaveBeenCalled();
expect(SessionIndexMocks.disconnect).toHaveBeenCalled();
});
});
@ -470,7 +462,7 @@ describe('forgotPasswordRequest', () => {
it('sends Command_ForgotPasswordRequest', () => {
forgotPasswordRequest(makeForgotRequestOpts());
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ForgotPasswordRequest_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_ForgotPasswordRequest_ext })
@ -481,7 +473,7 @@ describe('forgotPasswordRequest', () => {
forgotPasswordRequest(makeForgotRequestOpts());
const resp = { challengeEmail: true };
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.resetPasswordChallenge).toHaveBeenCalled();
expect(WebClient.instance.response.session.resetPasswordChallenge).toHaveBeenCalled();
expect(SessionIndexMocks.disconnect).toHaveBeenCalled();
});
@ -489,14 +481,14 @@ describe('forgotPasswordRequest', () => {
forgotPasswordRequest(makeForgotRequestOpts());
const resp = { challengeEmail: false };
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.resetPassword).toHaveBeenCalled();
expect(WebClient.instance.response.session.resetPassword).toHaveBeenCalled();
expect(SessionIndexMocks.disconnect).toHaveBeenCalled();
});
it('onError calls resetPasswordFailed and disconnect', () => {
forgotPasswordRequest(makeForgotRequestOpts());
invokeOnError();
expect(SessionPersistence.resetPasswordFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.resetPasswordFailed).toHaveBeenCalled();
expect(SessionIndexMocks.disconnect).toHaveBeenCalled();
});
});
@ -508,7 +500,7 @@ describe('forgotPasswordReset', () => {
it('sends Command_ForgotPasswordReset with plain newPassword when no salt', () => {
forgotPasswordReset(makeForgotResetOpts(), 'newpw');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ForgotPasswordReset_ext,
expect.objectContaining({ newPassword: 'newpw' }),
expect.any(Object)
@ -517,7 +509,7 @@ describe('forgotPasswordReset', () => {
it('sends hashed new password when salt provided', () => {
forgotPasswordReset(makeForgotResetOpts(), 'newpw', 'salt');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ForgotPasswordReset_ext,
expect.objectContaining({ hashedNewPassword: 'hashed_pw' }),
expect.any(Object)
@ -527,14 +519,14 @@ describe('forgotPasswordReset', () => {
it('onSuccess calls resetPasswordSuccess and disconnect', () => {
forgotPasswordReset(makeForgotResetOpts(), 'newpw');
invokeOnSuccess();
expect(SessionPersistence.resetPasswordSuccess).toHaveBeenCalled();
expect(WebClient.instance.response.session.resetPasswordSuccess).toHaveBeenCalled();
expect(SessionIndexMocks.disconnect).toHaveBeenCalled();
});
it('onError calls resetPasswordFailed and disconnect', () => {
forgotPasswordReset(makeForgotResetOpts(), 'newpw');
invokeOnError();
expect(SessionPersistence.resetPasswordFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.resetPasswordFailed).toHaveBeenCalled();
expect(SessionIndexMocks.disconnect).toHaveBeenCalled();
});
});
@ -546,7 +538,7 @@ describe('requestPasswordSalt', () => {
it('sends Command_RequestPasswordSalt', () => {
requestPasswordSalt(makeSaltOpts(App.WebSocketConnectReason.LOGIN), 'pw');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_RequestPasswordSalt_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_PasswordSalt_ext })
@ -588,7 +580,7 @@ describe('requestPasswordSalt', () => {
it('onResponseCode RespRegistrationRequired with ACTIVATE_ACCOUNT calls accountActivationFailed', () => {
requestPasswordSalt(makeSaltOpts(App.WebSocketConnectReason.ACTIVATE_ACCOUNT, { token: 'tok' }), 'pw');
invokeResponseCode(Data.Response_ResponseCode.RespRegistrationRequired);
expect(SessionPersistence.accountActivationFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.accountActivationFailed).toHaveBeenCalled();
});
it('onError calls updateStatus DISCONNECTED and disconnect', () => {
@ -605,6 +597,6 @@ describe('requestPasswordSalt', () => {
'newpw'
);
invokeOnError();
expect(SessionPersistence.resetPasswordFailed).toHaveBeenCalled();
expect(WebClient.instance.response.session.resetPasswordFailed).toHaveBeenCalled();
});
});

View file

@ -1,16 +1,8 @@
// Shared mock setup for session command tests
vi.mock('../../persistence', async () => {
const { makeSessionPersistenceMock } = await import('../../__mocks__/sessionCommandMocks');
return {
SessionPersistence: makeSessionPersistenceMock(),
RoomPersistence: { joinRoom: vi.fn() },
};
});
vi.mock('../../WebClient', async () => {
const { makeWebClientMock } = await import('../../__mocks__/sessionCommandMocks');
return { __esModule: true, default: makeWebClientMock() };
return { WebClient: { instance: makeWebClientMock() } };
});
vi.mock('../../utils', async () => {
@ -27,9 +19,7 @@ vi.mock('./', async () => {
import { Mock } from 'vitest';
import { makeCallbackHelpers } from '../../__mocks__/callbackHelpers';
import { SessionPersistence } from '../../persistence';
import { RoomPersistence } from '../../persistence';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { hashPassword, generateSalt, passwordSaltSupported } from '../../utils';
import { accountEdit } from './accountEdit';
@ -57,8 +47,9 @@ import { replayGetCode } from './replayGetCode';
import { replaySubmitCode } from './replaySubmitCode';
import { Data } from '@app/types';
const { invokeOnSuccess, invokeCallback } = makeCallbackHelpers(
webClient.protobuf.sendSessionCommand as Mock,
WebClient.instance.protobuf.sendSessionCommand as Mock,
2
);
@ -73,17 +64,17 @@ beforeEach(() => {
describe('accountEdit', () => {
it('sends Command_AccountEdit with correct params', () => {
accountEdit('pw', 'Alice', 'a@b.com', 'US');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_AccountEdit_ext,
expect.objectContaining({ passwordCheck: 'pw', realName: 'Alice', email: 'a@b.com', country: 'US' }),
expect.any(Object)
);
});
it('calls SessionPersistence.accountEditChanged on success', () => {
it('calls WebClient.instance.response.session.accountEditChanged on success', () => {
accountEdit('pw', 'Alice', 'a@b.com', 'US');
invokeOnSuccess();
expect(SessionPersistence.accountEditChanged).toHaveBeenCalledWith('Alice', 'a@b.com', 'US');
expect(WebClient.instance.response.session.accountEditChanged).toHaveBeenCalledWith('Alice', 'a@b.com', 'US');
});
});
@ -91,40 +82,40 @@ describe('accountImage', () => {
it('sends Command_AccountImage', () => {
const img = new Uint8Array([1, 2]);
accountImage(img);
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_AccountImage_ext, expect.objectContaining({ image: img }), expect.any(Object)
);
});
it('calls SessionPersistence.accountImageChanged on success', () => {
it('calls WebClient.instance.response.session.accountImageChanged on success', () => {
const img = new Uint8Array([1, 2]);
accountImage(img);
invokeOnSuccess();
expect(SessionPersistence.accountImageChanged).toHaveBeenCalledWith(img);
expect(WebClient.instance.response.session.accountImageChanged).toHaveBeenCalledWith(img);
});
});
describe('accountPassword', () => {
it('sends Command_AccountPassword', () => {
accountPassword('old', 'new', 'hashed');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_AccountPassword_ext,
expect.objectContaining({ oldPassword: 'old', newPassword: 'new', hashedNewPassword: 'hashed' }),
expect.any(Object)
);
});
it('calls SessionPersistence.accountPasswordChange on success', () => {
it('calls WebClient.instance.response.session.accountPasswordChange on success', () => {
accountPassword('old', 'new', 'hashed');
invokeOnSuccess();
expect(SessionPersistence.accountPasswordChange).toHaveBeenCalled();
expect(WebClient.instance.response.session.accountPasswordChange).toHaveBeenCalled();
});
});
describe('deckDel', () => {
it('sends Command_DeckDel', () => {
deckDel(42);
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_DeckDel_ext,
expect.objectContaining({ deckId: 42 }),
expect.any(Object)
@ -134,14 +125,14 @@ describe('deckDel', () => {
it('calls deleteServerDeck on success', () => {
deckDel(42);
invokeOnSuccess();
expect(SessionPersistence.deleteServerDeck).toHaveBeenCalledWith(42);
expect(WebClient.instance.response.session.deleteServerDeck).toHaveBeenCalledWith(42);
});
});
describe('deckDelDir', () => {
it('sends Command_DeckDelDir', () => {
deckDelDir('/path');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_DeckDelDir_ext, expect.objectContaining({ path: '/path' }), expect.any(Object)
);
});
@ -149,14 +140,14 @@ describe('deckDelDir', () => {
it('calls deleteServerDeckDir on success', () => {
deckDelDir('/path');
invokeOnSuccess();
expect(SessionPersistence.deleteServerDeckDir).toHaveBeenCalledWith('/path');
expect(WebClient.instance.response.session.deleteServerDeckDir).toHaveBeenCalledWith('/path');
});
});
describe('deckList', () => {
it('sends Command_DeckList', () => {
deckList();
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_DeckList_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_DeckList_ext })
@ -167,14 +158,14 @@ describe('deckList', () => {
deckList();
const root = { items: [] };
invokeOnSuccess({ root }, { responseCode: 0 });
expect(SessionPersistence.updateServerDecks).toHaveBeenCalledWith({ root });
expect(WebClient.instance.response.session.updateServerDecks).toHaveBeenCalledWith({ root });
});
});
describe('deckNewDir', () => {
it('sends Command_DeckNewDir', () => {
deckNewDir('/path', 'dir');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_DeckNewDir_ext, expect.objectContaining({ path: '/path', dirName: 'dir' }), expect.any(Object)
);
});
@ -182,14 +173,14 @@ describe('deckNewDir', () => {
it('calls createServerDeckDir on success', () => {
deckNewDir('/path', 'dir');
invokeOnSuccess();
expect(SessionPersistence.createServerDeckDir).toHaveBeenCalledWith('/path', 'dir');
expect(WebClient.instance.response.session.createServerDeckDir).toHaveBeenCalledWith('/path', 'dir');
});
});
describe('deckUpload', () => {
it('sends Command_DeckUpload', () => {
deckUpload('/path', 1, 'content');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_DeckUpload_ext,
expect.objectContaining({ path: '/path', deckId: 1, deckList: 'content' }),
expect.objectContaining({ responseExt: Data.Response_DeckUpload_ext })
@ -200,21 +191,21 @@ describe('deckUpload', () => {
deckUpload('/path', 1, 'content');
const resp = { newFile: { id: 1 } };
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.uploadServerDeck).toHaveBeenCalledWith('/path', resp.newFile);
expect(WebClient.instance.response.session.uploadServerDeck).toHaveBeenCalledWith('/path', resp.newFile);
});
});
describe('disconnect', () => {
it('calls webClient.disconnect', () => {
it('calls WebClient.instance.disconnect', () => {
disconnect();
expect(webClient.disconnect).toHaveBeenCalled();
expect(WebClient.instance.disconnect).toHaveBeenCalled();
});
});
describe('getGamesOfUser', () => {
it('sends Command_GetGamesOfUser', () => {
getGamesOfUser('alice');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_GetGamesOfUser_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_GetGamesOfUser_ext })
@ -225,14 +216,14 @@ describe('getGamesOfUser', () => {
getGamesOfUser('alice');
const resp = { gameList: [] };
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.getGamesOfUser).toHaveBeenCalledWith('alice', resp);
expect(WebClient.instance.response.session.getGamesOfUser).toHaveBeenCalledWith('alice', resp);
});
});
describe('getUserInfo', () => {
it('sends Command_GetUserInfo', () => {
getUserInfo('alice');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_GetUserInfo_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_GetUserInfo_ext })
@ -243,57 +234,57 @@ describe('getUserInfo', () => {
getUserInfo('alice');
const resp = { userInfo: { name: 'alice' } };
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.getUserInfo).toHaveBeenCalledWith(resp.userInfo);
expect(WebClient.instance.response.session.getUserInfo).toHaveBeenCalledWith(resp.userInfo);
});
});
describe('joinRoom', () => {
it('sends Command_JoinRoom', () => {
joinRoom(5);
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_JoinRoom_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_JoinRoom_ext })
);
});
it('calls RoomPersistence.joinRoom on success', () => {
it('calls WebClient.instance.response.room.joinRoom on success', () => {
joinRoom(5);
const resp = { roomInfo: { roomId: 5 } };
invokeOnSuccess(resp, { responseCode: 0 });
expect(RoomPersistence.joinRoom).toHaveBeenCalledWith(resp.roomInfo);
expect(WebClient.instance.response.room.joinRoom).toHaveBeenCalledWith(resp.roomInfo);
});
});
describe('listRooms (command)', () => {
it('sends Command_ListRooms', () => {
listRooms();
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(Data.Command_ListRooms_ext, expect.any(Object));
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(Data.Command_ListRooms_ext, expect.any(Object));
});
});
describe('listUsers', () => {
it('sends Command_ListUsers', () => {
listUsers();
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ListUsers_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_ListUsers_ext })
);
});
it('calls SessionPersistence.updateUsers with the user list on success', () => {
it('calls WebClient.instance.response.session.updateUsers with the user list on success', () => {
listUsers();
const resp = { userList: [{ name: 'Alice' }] };
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.updateUsers).toHaveBeenCalledWith([{ name: 'Alice' }]);
expect(WebClient.instance.response.session.updateUsers).toHaveBeenCalledWith([{ name: 'Alice' }]);
});
});
describe('message', () => {
it('sends Command_Message', () => {
message('bob', 'hi');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_Message_ext, expect.objectContaining({ userName: 'bob', message: 'hi' })
);
});
@ -304,7 +295,9 @@ describe('ping', () => {
it('sends Command_Ping', () => {
const pingReceived = vi.fn();
ping(pingReceived);
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(Data.Command_Ping_ext, expect.any(Object), expect.any(Object));
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_Ping_ext, expect.any(Object), expect.any(Object)
);
});
it('calls pingReceived via onResponse', () => {
@ -318,7 +311,7 @@ describe('ping', () => {
describe('replayDeleteMatch', () => {
it('sends Command_ReplayDeleteMatch', () => {
replayDeleteMatch(7);
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ReplayDeleteMatch_ext,
expect.objectContaining({ gameId: 7 }),
expect.any(Object)
@ -328,14 +321,14 @@ describe('replayDeleteMatch', () => {
it('calls replayDeleteMatch on success', () => {
replayDeleteMatch(7);
invokeOnSuccess();
expect(SessionPersistence.replayDeleteMatch).toHaveBeenCalledWith(7);
expect(WebClient.instance.response.session.replayDeleteMatch).toHaveBeenCalledWith(7);
});
});
describe('replayList', () => {
it('sends Command_ReplayList', () => {
replayList();
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ReplayList_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_ReplayList_ext })
@ -346,14 +339,14 @@ describe('replayList', () => {
replayList();
const resp = { matchList: [] };
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.replayList).toHaveBeenCalledWith([]);
expect(WebClient.instance.response.session.replayList).toHaveBeenCalledWith([]);
});
});
describe('replayModifyMatch', () => {
it('sends Command_ReplayModifyMatch', () => {
replayModifyMatch(7, true);
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ReplayModifyMatch_ext, expect.objectContaining({ gameId: 7, doNotHide: true }), expect.any(Object)
);
});
@ -361,14 +354,14 @@ describe('replayModifyMatch', () => {
it('calls replayModifyMatch on success', () => {
replayModifyMatch(7, true);
invokeOnSuccess();
expect(SessionPersistence.replayModifyMatch).toHaveBeenCalledWith(7, true);
expect(WebClient.instance.response.session.replayModifyMatch).toHaveBeenCalledWith(7, true);
});
});
describe('addToList / addToBuddyList / addToIgnoreList', () => {
it('addToBuddyList sends Command_AddToList with list=buddy', () => {
addToBuddyList('alice');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_AddToList_ext,
expect.objectContaining({ list: 'buddy' }),
expect.any(Object)
@ -377,24 +370,24 @@ describe('addToList / addToBuddyList / addToIgnoreList', () => {
it('addToIgnoreList sends Command_AddToList with list=ignore', () => {
addToIgnoreList('bob');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_AddToList_ext,
expect.objectContaining({ list: 'ignore' }),
expect.any(Object)
);
});
it('onSuccess calls SessionPersistence.addToList', () => {
it('onSuccess calls WebClient.instance.response.session.addToList', () => {
addToList('buddy', 'alice');
invokeOnSuccess();
expect(SessionPersistence.addToList).toHaveBeenCalledWith('buddy', 'alice');
expect(WebClient.instance.response.session.addToList).toHaveBeenCalledWith('buddy', 'alice');
});
});
describe('removeFromList / removeFromBuddyList / removeFromIgnoreList', () => {
it('removeFromBuddyList sends Command_RemoveFromList with list=buddy', () => {
removeFromBuddyList('alice');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_RemoveFromList_ext,
expect.objectContaining({ list: 'buddy' }),
expect.any(Object)
@ -403,24 +396,24 @@ describe('removeFromList / removeFromBuddyList / removeFromIgnoreList', () => {
it('removeFromIgnoreList sends Command_RemoveFromList with list=ignore', () => {
removeFromIgnoreList('bob');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_RemoveFromList_ext,
expect.objectContaining({ list: 'ignore' }),
expect.any(Object)
);
});
it('onSuccess calls SessionPersistence.removeFromList', () => {
it('onSuccess calls WebClient.instance.response.session.removeFromList', () => {
removeFromList('buddy', 'alice');
invokeOnSuccess();
expect(SessionPersistence.removeFromList).toHaveBeenCalledWith('buddy', 'alice');
expect(WebClient.instance.response.session.removeFromList).toHaveBeenCalledWith('buddy', 'alice');
});
});
describe('replayGetCode', () => {
it('sends Command_ReplayGetCode with gameId and responseExt', () => {
replayGetCode(42, vi.fn());
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ReplayGetCode_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_ReplayGetCode_ext })
@ -438,7 +431,7 @@ describe('replayGetCode', () => {
describe('replaySubmitCode', () => {
it('sends Command_ReplaySubmitCode with replayCode', () => {
replaySubmitCode('42-abc123');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ReplaySubmitCode_ext, expect.objectContaining({ replayCode: '42-abc123' }), expect.any(Object)
);
});

View file

@ -1,9 +1,7 @@
import { App } from '@app/types';
import webClient from '../../WebClient';
import { SessionPersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
export function updateStatus(status: App.StatusEnum, description: string): void {
SessionPersistence.updateStatus(status, description);
WebClient.instance.response.session.updateStatus(status, description);
webClient.updateStatus(status);
WebClient.instance.updateStatus(status);
}

View file

@ -1,6 +1,6 @@
import type { Data, Enriched } from '@app/types';
import { GamePersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
export function attachCard(data: Data.Event_AttachCard, meta: Enriched.GameEventMeta): void {
GamePersistence.cardAttached(meta.gameId, meta.playerId, data);
WebClient.instance.response.game.cardAttached(meta.gameId, meta.playerId, data);
}

View file

@ -1,6 +1,6 @@
import type { Data, Enriched } from '@app/types';
import { GamePersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
export function changeZoneProperties(data: Data.Event_ChangeZoneProperties, meta: Enriched.GameEventMeta): void {
GamePersistence.zonePropertiesChanged(meta.gameId, meta.playerId, data);
WebClient.instance.response.game.zonePropertiesChanged(meta.gameId, meta.playerId, data);
}

View file

@ -1,6 +1,6 @@
import type { Data, Enriched } from '@app/types';
import { GamePersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
export function createArrow(data: Data.Event_CreateArrow, meta: Enriched.GameEventMeta): void {
GamePersistence.arrowCreated(meta.gameId, meta.playerId, data);
WebClient.instance.response.game.arrowCreated(meta.gameId, meta.playerId, data);
}

View file

@ -1,6 +1,6 @@
import type { Data, Enriched } from '@app/types';
import { GamePersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
export function createCounter(data: Data.Event_CreateCounter, meta: Enriched.GameEventMeta): void {
GamePersistence.counterCreated(meta.gameId, meta.playerId, data);
WebClient.instance.response.game.counterCreated(meta.gameId, meta.playerId, data);
}

View file

@ -1,6 +1,6 @@
import type { Data, Enriched } from '@app/types';
import { GamePersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
export function createToken(data: Data.Event_CreateToken, meta: Enriched.GameEventMeta): void {
GamePersistence.tokenCreated(meta.gameId, meta.playerId, data);
WebClient.instance.response.game.tokenCreated(meta.gameId, meta.playerId, data);
}

View file

@ -1,6 +1,6 @@
import type { Data, Enriched } from '@app/types';
import { GamePersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
export function delCounter(data: Data.Event_DelCounter, meta: Enriched.GameEventMeta): void {
GamePersistence.counterDeleted(meta.gameId, meta.playerId, data);
WebClient.instance.response.game.counterDeleted(meta.gameId, meta.playerId, data);
}

View file

@ -1,6 +1,6 @@
import type { Data, Enriched } from '@app/types';
import { GamePersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
export function deleteArrow(data: Data.Event_DeleteArrow, meta: Enriched.GameEventMeta): void {
GamePersistence.arrowDeleted(meta.gameId, meta.playerId, data);
WebClient.instance.response.game.arrowDeleted(meta.gameId, meta.playerId, data);
}

View file

@ -1,6 +1,6 @@
import type { Data, Enriched } from '@app/types';
import { GamePersistence } from '../../persistence';
import { WebClient } from '../../WebClient';
export function destroyCard(data: Data.Event_DestroyCard, meta: Enriched.GameEventMeta): void {
GamePersistence.cardDestroyed(meta.gameId, meta.playerId, data);
WebClient.instance.response.game.cardDestroyed(meta.gameId, meta.playerId, data);
}

Some files were not shown because too many files have changed in this diff Show more