mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-04-27 07:48:01 -07:00
complete unit testing of redux and api layers
This commit is contained in:
parent
367852866f
commit
3001925430
19 changed files with 2808 additions and 5 deletions
526
webclient/src/store/server/server.reducer.spec.ts
Normal file
526
webclient/src/store/server/server.reducer.spec.ts
Normal file
|
|
@ -0,0 +1,526 @@
|
|||
import { StatusEnum, UserLevelFlag } from 'types';
|
||||
import { serverReducer } from './server.reducer';
|
||||
import { Types } from './server.types';
|
||||
import {
|
||||
makeBanHistoryItem,
|
||||
makeConnectOptions,
|
||||
makeDeckList,
|
||||
makeDeckTreeItem,
|
||||
makeLogItem,
|
||||
makeReplayMatch,
|
||||
makeServerState,
|
||||
makeUser,
|
||||
makeWarnHistoryItem,
|
||||
makeWarnListItem,
|
||||
} from './__mocks__/server-fixtures';
|
||||
|
||||
// ── Initialisation ───────────────────────────────────────────────────────────
|
||||
|
||||
describe('Initialisation', () => {
|
||||
it('returns initialState when called with undefined state', () => {
|
||||
const result = serverReducer(undefined, { type: '@@INIT' });
|
||||
expect(result.initialized).toBe(false);
|
||||
expect(result.buddyList).toEqual([]);
|
||||
expect(result.status.state).toBe(StatusEnum.DISCONNECTED);
|
||||
});
|
||||
|
||||
it('INITIALIZED → resets to initialState with initialized: true', () => {
|
||||
const state = makeServerState({ banUser: 'someone', initialized: false });
|
||||
const result = serverReducer(state, { type: Types.INITIALIZED });
|
||||
expect(result.initialized).toBe(true);
|
||||
expect(result.banUser).toBe('');
|
||||
expect(result.buddyList).toEqual([]);
|
||||
});
|
||||
|
||||
it('CLEAR_STORE → resets to initialState but preserves status', () => {
|
||||
const status = { state: StatusEnum.LOGGED_IN, description: 'logged in' };
|
||||
const state = makeServerState({ status, banUser: 'someone' });
|
||||
const result = serverReducer(state, { type: Types.CLEAR_STORE });
|
||||
expect(result.banUser).toBe('');
|
||||
expect(result.status).toEqual(status);
|
||||
expect(result.initialized).toBe(false);
|
||||
});
|
||||
|
||||
it('default → returns state unchanged for unknown action', () => {
|
||||
const state = makeServerState();
|
||||
const result = serverReducer(state, { type: '@@UNKNOWN' });
|
||||
expect(result).toBe(state);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Account & Connection ─────────────────────────────────────────────────────
|
||||
|
||||
describe('Account & Connection', () => {
|
||||
it('ACCOUNT_AWAITING_ACTIVATION → sets connectOptions from action.options', () => {
|
||||
const options = makeConnectOptions();
|
||||
const state = makeServerState();
|
||||
const result = serverReducer(state, { type: Types.ACCOUNT_AWAITING_ACTIVATION, options });
|
||||
expect(result.connectOptions).toEqual(options);
|
||||
});
|
||||
|
||||
it('ACCOUNT_ACTIVATION_SUCCESS → clears connectOptions to {}', () => {
|
||||
const state = makeServerState({ connectOptions: makeConnectOptions() });
|
||||
const result = serverReducer(state, { type: Types.ACCOUNT_ACTIVATION_SUCCESS });
|
||||
expect(result.connectOptions).toEqual({});
|
||||
});
|
||||
|
||||
it('ACCOUNT_ACTIVATION_FAILED → clears connectOptions to {}', () => {
|
||||
const state = makeServerState({ connectOptions: makeConnectOptions() });
|
||||
const result = serverReducer(state, { type: Types.ACCOUNT_ACTIVATION_FAILED });
|
||||
expect(result.connectOptions).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
// ── Server Info & Status ──────────────────────────────────────────────────────
|
||||
|
||||
describe('Server Info & Status', () => {
|
||||
it('SERVER_MESSAGE → merges message into state.info', () => {
|
||||
const state = makeServerState({ info: { message: null, name: 'Old', version: '1.0' } });
|
||||
const result = serverReducer(state, { type: Types.SERVER_MESSAGE, message: 'Welcome!' });
|
||||
expect(result.info.message).toBe('Welcome!');
|
||||
expect(result.info.name).toBe('Old');
|
||||
expect(result.info.version).toBe('1.0');
|
||||
});
|
||||
|
||||
it('UPDATE_INFO → merges name and version into state.info (not message)', () => {
|
||||
const state = makeServerState({ info: { message: 'hi', name: null, version: null } });
|
||||
const result = serverReducer(state, {
|
||||
type: Types.UPDATE_INFO,
|
||||
info: { name: 'Servatrice', version: '2.9.0' },
|
||||
});
|
||||
expect(result.info.name).toBe('Servatrice');
|
||||
expect(result.info.version).toBe('2.9.0');
|
||||
expect(result.info.message).toBe('hi');
|
||||
});
|
||||
|
||||
it('UPDATE_STATUS → replaces state.status entirely', () => {
|
||||
const state = makeServerState();
|
||||
const status = { state: StatusEnum.LOGGED_IN, description: 'ok' };
|
||||
const result = serverReducer(state, { type: Types.UPDATE_STATUS, status });
|
||||
expect(result.status).toEqual(status);
|
||||
});
|
||||
});
|
||||
|
||||
// ── User ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('User', () => {
|
||||
it('UPDATE_USER → merges action.user into state.user', () => {
|
||||
const state = makeServerState({ user: makeUser({ name: 'Alice', userLevel: 1 }) });
|
||||
const result = serverReducer(state, {
|
||||
type: Types.UPDATE_USER,
|
||||
user: { userLevel: 8 },
|
||||
});
|
||||
expect(result.user.name).toBe('Alice');
|
||||
expect(result.user.userLevel).toBe(8);
|
||||
});
|
||||
|
||||
it('ACCOUNT_EDIT_CHANGED → merges action.user into state.user', () => {
|
||||
const state = makeServerState({ user: makeUser({ name: 'Alice' }) });
|
||||
const result = serverReducer(state, { type: Types.ACCOUNT_EDIT_CHANGED, user: { realName: 'Alice Smith' } });
|
||||
expect(result.user.realName).toBe('Alice Smith');
|
||||
expect(result.user.name).toBe('Alice');
|
||||
});
|
||||
|
||||
it('ACCOUNT_IMAGE_CHANGED → merges action.user into state.user', () => {
|
||||
const state = makeServerState({ user: makeUser({ name: 'Alice' }) });
|
||||
const result = serverReducer(state, { type: Types.ACCOUNT_IMAGE_CHANGED, user: { country: 'US' } });
|
||||
expect(result.user.country).toBe('US');
|
||||
});
|
||||
});
|
||||
|
||||
// ── Users List ────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('Users List', () => {
|
||||
it('UPDATE_USERS → replaces users list and sorts by name ASC', () => {
|
||||
const state = makeServerState();
|
||||
const users = [makeUser({ name: 'Zane' }), makeUser({ name: 'Alice' })];
|
||||
const result = serverReducer(state, { type: Types.UPDATE_USERS, users });
|
||||
expect(result.users[0].name).toBe('Alice');
|
||||
expect(result.users[1].name).toBe('Zane');
|
||||
});
|
||||
|
||||
it('USER_JOINED → appends user and sorts', () => {
|
||||
const state = makeServerState({ users: [makeUser({ name: 'Zane' })] });
|
||||
const result = serverReducer(state, { type: Types.USER_JOINED, user: makeUser({ name: 'Alice' }) });
|
||||
expect(result.users[0].name).toBe('Alice');
|
||||
expect(result.users[1].name).toBe('Zane');
|
||||
});
|
||||
|
||||
it('USER_LEFT → removes user by name', () => {
|
||||
const state = makeServerState({ users: [makeUser({ name: 'Alice' }), makeUser({ name: 'Bob' })] });
|
||||
const result = serverReducer(state, { type: Types.USER_LEFT, name: 'Alice' });
|
||||
expect(result.users).toHaveLength(1);
|
||||
expect(result.users[0].name).toBe('Bob');
|
||||
});
|
||||
});
|
||||
|
||||
// ── Buddy & Ignore Lists ──────────────────────────────────────────────────────
|
||||
|
||||
describe('Buddy List', () => {
|
||||
it('UPDATE_BUDDY_LIST → replaces and sorts buddy list', () => {
|
||||
const state = makeServerState();
|
||||
const buddyList = [makeUser({ name: 'Zane' }), makeUser({ name: 'Alice' })];
|
||||
const result = serverReducer(state, { type: Types.UPDATE_BUDDY_LIST, buddyList });
|
||||
expect(result.buddyList[0].name).toBe('Alice');
|
||||
});
|
||||
|
||||
it('ADD_TO_BUDDY_LIST → appends user and sorts', () => {
|
||||
const state = makeServerState({ buddyList: [makeUser({ name: 'Zane' })] });
|
||||
const result = serverReducer(state, { type: Types.ADD_TO_BUDDY_LIST, user: makeUser({ name: 'Alice' }) });
|
||||
expect(result.buddyList[0].name).toBe('Alice');
|
||||
expect(result.buddyList).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('REMOVE_FROM_BUDDY_LIST → removes user by name', () => {
|
||||
const state = makeServerState({ buddyList: [makeUser({ name: 'Alice' }), makeUser({ name: 'Bob' })] });
|
||||
const result = serverReducer(state, { type: Types.REMOVE_FROM_BUDDY_LIST, userName: 'Alice' });
|
||||
expect(result.buddyList).toHaveLength(1);
|
||||
expect(result.buddyList[0].name).toBe('Bob');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Ignore List', () => {
|
||||
it('UPDATE_IGNORE_LIST → replaces and sorts ignore list', () => {
|
||||
const state = makeServerState();
|
||||
const ignoreList = [makeUser({ name: 'Zane' }), makeUser({ name: 'Alice' })];
|
||||
const result = serverReducer(state, { type: Types.UPDATE_IGNORE_LIST, ignoreList });
|
||||
expect(result.ignoreList[0].name).toBe('Alice');
|
||||
});
|
||||
|
||||
it('ADD_TO_IGNORE_LIST → appends user and sorts', () => {
|
||||
const state = makeServerState({ ignoreList: [makeUser({ name: 'Zane' })] });
|
||||
const result = serverReducer(state, { type: Types.ADD_TO_IGNORE_LIST, user: makeUser({ name: 'Alice' }) });
|
||||
expect(result.ignoreList[0].name).toBe('Alice');
|
||||
expect(result.ignoreList).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('REMOVE_FROM_IGNORE_LIST → removes user by name', () => {
|
||||
const state = makeServerState({ ignoreList: [makeUser({ name: 'Alice' }), makeUser({ name: 'Bob' })] });
|
||||
const result = serverReducer(state, { type: Types.REMOVE_FROM_IGNORE_LIST, userName: 'Alice' });
|
||||
expect(result.ignoreList).toHaveLength(1);
|
||||
expect(result.ignoreList[0].name).toBe('Bob');
|
||||
});
|
||||
});
|
||||
|
||||
// ── Logs ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('Logs', () => {
|
||||
it('VIEW_LOGS → replaces logs entirely', () => {
|
||||
const logs = { room: [makeLogItem()], game: [], chat: [] };
|
||||
const state = makeServerState();
|
||||
const result = serverReducer(state, { type: Types.VIEW_LOGS, logs });
|
||||
expect(result.logs).toEqual(logs);
|
||||
});
|
||||
|
||||
it('CLEAR_LOGS → resets logs to empty arrays', () => {
|
||||
const state = makeServerState({ logs: { room: [makeLogItem()], game: [], chat: [] } });
|
||||
const result = serverReducer(state, { type: Types.CLEAR_LOGS });
|
||||
expect(result.logs.room).toEqual([]);
|
||||
expect(result.logs.game).toEqual([]);
|
||||
expect(result.logs.chat).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Messaging ─────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('Messaging', () => {
|
||||
it('USER_MESSAGE → uses receiverName as key when current user is sender', () => {
|
||||
const state = makeServerState({ user: makeUser({ name: 'Alice' }), messages: {} });
|
||||
const messageData = { senderName: 'Alice', receiverName: 'Bob', message: 'hi' };
|
||||
const result = serverReducer(state, { type: Types.USER_MESSAGE, messageData });
|
||||
expect(result.messages['Bob']).toHaveLength(1);
|
||||
expect(result.messages['Bob'][0]).toBe(messageData);
|
||||
});
|
||||
|
||||
it('USER_MESSAGE → uses senderName as key when current user is receiver', () => {
|
||||
const state = makeServerState({ user: makeUser({ name: 'Bob' }), messages: {} });
|
||||
const messageData = { senderName: 'Alice', receiverName: 'Bob', message: 'yo' };
|
||||
const result = serverReducer(state, { type: Types.USER_MESSAGE, messageData });
|
||||
expect(result.messages['Alice']).toHaveLength(1);
|
||||
expect(result.messages['Alice'][0]).toBe(messageData);
|
||||
});
|
||||
|
||||
it('USER_MESSAGE → appends to existing messages for that user', () => {
|
||||
const existingMsg = { senderName: 'Alice', receiverName: 'Bob', message: 'first' };
|
||||
const state = makeServerState({
|
||||
user: makeUser({ name: 'Bob' }),
|
||||
messages: { Alice: [existingMsg] },
|
||||
});
|
||||
const newMsg = { senderName: 'Alice', receiverName: 'Bob', message: 'second' };
|
||||
const result = serverReducer(state, { type: Types.USER_MESSAGE, messageData: newMsg });
|
||||
expect(result.messages['Alice']).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
// ── User Info & Notifications ─────────────────────────────────────────────────
|
||||
|
||||
describe('User Info & Notifications', () => {
|
||||
it('GET_USER_INFO → adds userInfo keyed by name', () => {
|
||||
const userInfo = makeUser({ name: 'Eve' });
|
||||
const state = makeServerState();
|
||||
const result = serverReducer(state, { type: Types.GET_USER_INFO, userInfo });
|
||||
expect(result.userInfo['Eve']).toBe(userInfo);
|
||||
});
|
||||
|
||||
it('NOTIFY_USER → appends notification to list', () => {
|
||||
const state = makeServerState({ notifications: [] });
|
||||
const notification = { type: 1, warningReason: '', customTitle: '', customContent: '' };
|
||||
const result = serverReducer(state, { type: Types.NOTIFY_USER, notification });
|
||||
expect(result.notifications).toHaveLength(1);
|
||||
expect(result.notifications[0]).toBe(notification);
|
||||
});
|
||||
|
||||
it('SERVER_SHUTDOWN → sets serverShutdown to action.data', () => {
|
||||
const data = { reason: 'maintenance', minutes: 10 };
|
||||
const state = makeServerState();
|
||||
const result = serverReducer(state, { type: Types.SERVER_SHUTDOWN, data });
|
||||
expect(result.serverShutdown).toBe(data);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Moderation ────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('Moderation', () => {
|
||||
it('BAN_FROM_SERVER → sets banUser', () => {
|
||||
const state = makeServerState();
|
||||
const result = serverReducer(state, { type: Types.BAN_FROM_SERVER, userName: 'Frank' });
|
||||
expect(result.banUser).toBe('Frank');
|
||||
});
|
||||
|
||||
it('BAN_HISTORY → adds banHistory keyed by userName', () => {
|
||||
const history = [makeBanHistoryItem()];
|
||||
const state = makeServerState();
|
||||
const result = serverReducer(state, { type: Types.BAN_HISTORY, userName: 'Frank', banHistory: history });
|
||||
expect(result.banHistory['Frank']).toBe(history);
|
||||
});
|
||||
|
||||
it('WARN_HISTORY → adds warnHistory keyed by userName', () => {
|
||||
const history = [makeWarnHistoryItem()];
|
||||
const state = makeServerState();
|
||||
const result = serverReducer(state, { type: Types.WARN_HISTORY, userName: 'Grace', warnHistory: history });
|
||||
expect(result.warnHistory['Grace']).toBe(history);
|
||||
});
|
||||
|
||||
it('WARN_LIST_OPTIONS → replaces warnListOptions', () => {
|
||||
const list = [makeWarnListItem()];
|
||||
const state = makeServerState();
|
||||
const result = serverReducer(state, { type: Types.WARN_LIST_OPTIONS, warnList: list });
|
||||
expect(result.warnListOptions).toBe(list);
|
||||
});
|
||||
|
||||
it('WARN_USER → sets warnUser', () => {
|
||||
const state = makeServerState();
|
||||
const result = serverReducer(state, { type: Types.WARN_USER, userName: 'Hank' });
|
||||
expect(result.warnUser).toBe('Hank');
|
||||
});
|
||||
|
||||
it('GET_ADMIN_NOTES → adds adminNotes keyed by userName', () => {
|
||||
const state = makeServerState();
|
||||
const result = serverReducer(state, { type: Types.GET_ADMIN_NOTES, userName: 'Ira', notes: 'note1' });
|
||||
expect(result.adminNotes['Ira']).toBe('note1');
|
||||
});
|
||||
|
||||
it('UPDATE_ADMIN_NOTES → updates adminNotes keyed by userName', () => {
|
||||
const state = makeServerState({ adminNotes: { Ira: 'old' } });
|
||||
const result = serverReducer(state, { type: Types.UPDATE_ADMIN_NOTES, userName: 'Ira', notes: 'new' });
|
||||
expect(result.adminNotes['Ira']).toBe('new');
|
||||
});
|
||||
});
|
||||
|
||||
// ── ADJUST_MOD ────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('ADJUST_MOD', () => {
|
||||
const baseUserLevel = UserLevelFlag.IsUser | UserLevelFlag.IsRegistered | UserLevelFlag.IsModerator | UserLevelFlag.IsJudge;
|
||||
|
||||
it('shouldBeMod=true, shouldBeJudge=true → keeps IsModerator and IsJudge bits', () => {
|
||||
const state = makeServerState({ users: [makeUser({ name: 'Dan', userLevel: baseUserLevel })] });
|
||||
const result = serverReducer(state, { type: Types.ADJUST_MOD, userName: 'Dan', shouldBeMod: true, shouldBeJudge: true });
|
||||
// IsModerator(4) | IsJudge(16)
|
||||
expect(result.users[0].userLevel).toBe(20);
|
||||
});
|
||||
|
||||
it('shouldBeMod=true, shouldBeJudge=false → keeps only IsModerator bit', () => {
|
||||
const state = makeServerState({ users: [makeUser({ name: 'Dan', userLevel: baseUserLevel })] });
|
||||
const result = serverReducer(state, { type: Types.ADJUST_MOD, userName: 'Dan', shouldBeMod: true, shouldBeJudge: false });
|
||||
// IsModerator(4)
|
||||
expect(result.users[0].userLevel).toBe(4);
|
||||
});
|
||||
|
||||
it('shouldBeMod=false, shouldBeJudge=true → keeps only IsJudge bit', () => {
|
||||
const state = makeServerState({ users: [makeUser({ name: 'Dan', userLevel: baseUserLevel })] });
|
||||
const result = serverReducer(state, { type: Types.ADJUST_MOD, userName: 'Dan', shouldBeMod: false, shouldBeJudge: true });
|
||||
// IsJudge(16)
|
||||
expect(result.users[0].userLevel).toBe(16);
|
||||
});
|
||||
|
||||
it('shouldBeMod=false, shouldBeJudge=false → clears both bits', () => {
|
||||
const state = makeServerState({ users: [makeUser({ name: 'Dan', userLevel: baseUserLevel })] });
|
||||
const result = serverReducer(state, { type: Types.ADJUST_MOD, userName: 'Dan', shouldBeMod: false, shouldBeJudge: false });
|
||||
expect(result.users[0].userLevel).toBe(0);
|
||||
});
|
||||
|
||||
it('non-matching users are left unchanged', () => {
|
||||
const alice = makeUser({ name: 'Alice', userLevel: 7 });
|
||||
const state = makeServerState({ users: [alice, makeUser({ name: 'Dan', userLevel: baseUserLevel })] });
|
||||
const result = serverReducer(state, { type: Types.ADJUST_MOD, userName: 'Dan', shouldBeMod: false, shouldBeJudge: false });
|
||||
expect(result.users.find(u => u.name === 'Alice').userLevel).toBe(7);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Replays ───────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('Replays', () => {
|
||||
it('REPLAY_LIST → replaces replays list', () => {
|
||||
const matchList = [makeReplayMatch({ gameId: 10 })];
|
||||
const state = makeServerState({ replays: [makeReplayMatch({ gameId: 99 })] });
|
||||
const result = serverReducer(state, { type: Types.REPLAY_LIST, matchList });
|
||||
expect(result.replays).toHaveLength(1);
|
||||
expect(result.replays[0].gameId).toBe(10);
|
||||
});
|
||||
|
||||
it('REPLAY_ADDED → appends matchInfo to replays', () => {
|
||||
const existing = makeReplayMatch({ gameId: 1 });
|
||||
const added = makeReplayMatch({ gameId: 2 });
|
||||
const state = makeServerState({ replays: [existing] });
|
||||
const result = serverReducer(state, { type: Types.REPLAY_ADDED, matchInfo: added });
|
||||
expect(result.replays).toHaveLength(2);
|
||||
expect(result.replays[1]).toBe(added);
|
||||
});
|
||||
|
||||
it('REPLAY_MODIFY_MATCH → updates doNotHide for matching gameId', () => {
|
||||
const state = makeServerState({ replays: [makeReplayMatch({ gameId: 5, doNotHide: false })] });
|
||||
const result = serverReducer(state, { type: Types.REPLAY_MODIFY_MATCH, gameId: 5, doNotHide: true });
|
||||
expect(result.replays[0].doNotHide).toBe(true);
|
||||
});
|
||||
|
||||
it('REPLAY_MODIFY_MATCH → leaves non-matching replays unchanged', () => {
|
||||
const r1 = makeReplayMatch({ gameId: 1, doNotHide: false });
|
||||
const r2 = makeReplayMatch({ gameId: 2, doNotHide: false });
|
||||
const state = makeServerState({ replays: [r1, r2] });
|
||||
const result = serverReducer(state, { type: Types.REPLAY_MODIFY_MATCH, gameId: 1, doNotHide: true });
|
||||
expect(result.replays[1].doNotHide).toBe(false);
|
||||
});
|
||||
|
||||
it('REPLAY_DELETE_MATCH → removes replay by gameId', () => {
|
||||
const state = makeServerState({ replays: [makeReplayMatch({ gameId: 5 }), makeReplayMatch({ gameId: 6 })] });
|
||||
const result = serverReducer(state, { type: Types.REPLAY_DELETE_MATCH, gameId: 5 });
|
||||
expect(result.replays).toHaveLength(1);
|
||||
expect(result.replays[0].gameId).toBe(6);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Deck Storage ──────────────────────────────────────────────────────────────
|
||||
|
||||
describe('Deck Storage', () => {
|
||||
it('BACKEND_DECKS → sets backendDecks', () => {
|
||||
const deckList = makeDeckList();
|
||||
const state = makeServerState();
|
||||
const result = serverReducer(state, { type: Types.BACKEND_DECKS, deckList });
|
||||
expect(result.backendDecks).toBe(deckList);
|
||||
});
|
||||
|
||||
it('DECK_UPLOAD with null backendDecks → returns state unchanged', () => {
|
||||
const state = makeServerState({ backendDecks: null });
|
||||
const result = serverReducer(state, { type: Types.DECK_UPLOAD, path: '', treeItem: makeDeckTreeItem() });
|
||||
expect(result).toBe(state);
|
||||
});
|
||||
|
||||
it('DECK_UPLOAD with flat path → appends item to root', () => {
|
||||
const state = makeServerState({ backendDecks: makeDeckList() });
|
||||
const item = makeDeckTreeItem({ name: 'deck.cod' });
|
||||
const result = serverReducer(state, { type: Types.DECK_UPLOAD, path: '', treeItem: item });
|
||||
expect(result.backendDecks.root.items).toHaveLength(1);
|
||||
expect(result.backendDecks.root.items[0]).toBe(item);
|
||||
});
|
||||
|
||||
it('DECK_UPLOAD with nested path → inserts into matching subfolder', () => {
|
||||
const subfolder = { id: 0, name: 'myDecks', file: null, folder: { items: [] } };
|
||||
const state = makeServerState({ backendDecks: { root: { items: [subfolder] } } });
|
||||
const item = makeDeckTreeItem({ name: 'new.cod' });
|
||||
const result = serverReducer(state, { type: Types.DECK_UPLOAD, path: 'myDecks', treeItem: item });
|
||||
const folder = result.backendDecks.root.items.find(i => i.name === 'myDecks');
|
||||
expect(folder.folder.items).toHaveLength(1);
|
||||
expect(folder.folder.items[0]).toBe(item);
|
||||
});
|
||||
|
||||
it('DECK_UPLOAD with non-existent intermediate folder → creates folder and inserts', () => {
|
||||
const state = makeServerState({ backendDecks: makeDeckList() });
|
||||
const item = makeDeckTreeItem({ name: 'deck.cod' });
|
||||
const result = serverReducer(state, { type: Types.DECK_UPLOAD, path: 'newFolder', treeItem: item });
|
||||
expect(result.backendDecks.root.items).toHaveLength(1);
|
||||
expect(result.backendDecks.root.items[0].name).toBe('newFolder');
|
||||
expect(result.backendDecks.root.items[0].folder.items[0]).toBe(item);
|
||||
});
|
||||
|
||||
it('DECK_DELETE with null backendDecks → returns state unchanged', () => {
|
||||
const state = makeServerState({ backendDecks: null });
|
||||
const result = serverReducer(state, { type: Types.DECK_DELETE, deckId: 1 });
|
||||
expect(result).toBe(state);
|
||||
});
|
||||
|
||||
it('DECK_DELETE → removes item by id from tree', () => {
|
||||
const item = makeDeckTreeItem({ id: 7 });
|
||||
const state = makeServerState({ backendDecks: { root: { items: [item] } } });
|
||||
const result = serverReducer(state, { type: Types.DECK_DELETE, deckId: 7 });
|
||||
expect(result.backendDecks.root.items).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('DECK_DELETE → recursively removes item nested inside a subfolder', () => {
|
||||
const nested = makeDeckTreeItem({ id: 9, name: 'nested.cod' });
|
||||
const subfolder = { id: 0, name: 'sub', file: null, folder: { items: [nested] } };
|
||||
const state = makeServerState({ backendDecks: { root: { items: [subfolder] } } });
|
||||
const result = serverReducer(state, { type: Types.DECK_DELETE, deckId: 9 });
|
||||
expect(result.backendDecks.root.items[0].folder.items).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('DECK_NEW_DIR with null backendDecks → returns state unchanged', () => {
|
||||
const state = makeServerState({ backendDecks: null });
|
||||
const result = serverReducer(state, { type: Types.DECK_NEW_DIR, path: '', dirName: 'newDir' });
|
||||
expect(result).toBe(state);
|
||||
});
|
||||
|
||||
it('DECK_NEW_DIR at root → appends folder to root items', () => {
|
||||
const state = makeServerState({ backendDecks: makeDeckList() });
|
||||
const result = serverReducer(state, { type: Types.DECK_NEW_DIR, path: '', dirName: 'myDir' });
|
||||
expect(result.backendDecks.root.items).toHaveLength(1);
|
||||
expect(result.backendDecks.root.items[0].name).toBe('myDir');
|
||||
expect(result.backendDecks.root.items[0].folder).toEqual({ items: [] });
|
||||
});
|
||||
|
||||
it('DECK_NEW_DIR nested → inserts folder inside matching subfolder', () => {
|
||||
const subfolder = { id: 0, name: 'parent', file: null, folder: { items: [] } };
|
||||
const state = makeServerState({ backendDecks: { root: { items: [subfolder] } } });
|
||||
const result = serverReducer(state, { type: Types.DECK_NEW_DIR, path: 'parent', dirName: 'child' });
|
||||
const parent = result.backendDecks.root.items.find(i => i.name === 'parent');
|
||||
expect(parent.folder.items).toHaveLength(1);
|
||||
expect(parent.folder.items[0].name).toBe('child');
|
||||
});
|
||||
|
||||
it('DECK_DEL_DIR with null backendDecks → returns state unchanged', () => {
|
||||
const state = makeServerState({ backendDecks: null });
|
||||
const result = serverReducer(state, { type: Types.DECK_DEL_DIR, path: 'myDir' });
|
||||
expect(result).toBe(state);
|
||||
});
|
||||
|
||||
it('DECK_DEL_DIR → removes folder from root by name', () => {
|
||||
const subfolder = { id: 0, name: 'myDir', file: null, folder: { items: [] } };
|
||||
const state = makeServerState({ backendDecks: { root: { items: [subfolder] } } });
|
||||
const result = serverReducer(state, { type: Types.DECK_DEL_DIR, path: 'myDir' });
|
||||
expect(result.backendDecks.root.items).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('DECK_DEL_DIR → returns deck tree unchanged when path is empty', () => {
|
||||
const subfolder = { id: 0, name: 'keep', file: null, folder: { items: [] } };
|
||||
const state = makeServerState({ backendDecks: { root: { items: [subfolder] } } });
|
||||
const result = serverReducer(state, { type: Types.DECK_DEL_DIR, path: '' });
|
||||
expect(result.backendDecks.root.items).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('DECK_DEL_DIR → recursively removes nested subfolder via multi-segment path', () => {
|
||||
const child = { id: 0, name: 'child', file: null, folder: { items: [] } };
|
||||
const parent = { id: 0, name: 'parent', file: null, folder: { items: [child] } };
|
||||
const state = makeServerState({ backendDecks: { root: { items: [parent] } } });
|
||||
const result = serverReducer(state, { type: Types.DECK_DEL_DIR, path: 'parent/child' });
|
||||
expect(result.backendDecks.root.items[0].folder.items).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue