Files
scrying-pool/tests/unit/ui/gm/ScryingPoolStrip.test.js
T
uberwald 7918792f4e Fix Story 2.3 code review findings: remove duplicate ParticipantCard.js, fix lint in ScryingPoolStrip.js
- Delete src/ui/shared/ParticipantCard.js (duplicate of boardUtils.js with conflicting implementations)
- Delete tests/unit/ui/shared/ParticipantCard.test.js (tests for deleted file)
- Add directorsBoard to global declarations in ScryingPoolStrip.js to fix lint errors

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-05-23 11:31:01 +02:00

207 lines
6.0 KiB
JavaScript

// @ts-nocheck
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
// Stub Application globally before importing ScryingPoolStrip
beforeEach(() => {
vi.stubGlobal('Application', class {
static get defaultOptions() { return {}; }
constructor() { this.position = { left: 0, top: 0 }; this.rendered = false; }
render() {}
close() {}
activateListeners() {}
});
vi.stubGlobal('foundry', {
utils: {
mergeObject: (base, override) => Object.assign({}, base, override),
},
});
vi.stubGlobal('game', {
user: {
setFlag: vi.fn(),
getFlag: vi.fn(() => null),
},
});
});
afterEach(() => {
vi.unstubAllGlobals();
vi.clearAllMocks();
});
import {
LABELS,
resolveTargetState,
buildParticipantList,
ScryingPoolStrip,
} from '../../../../src/ui/gm/ScryingPoolStrip.js';
describe('LABELS', () => {
it('has HIDE_FROM_TABLE equal to exact canonical string', () => {
expect(LABELS.HIDE_FROM_TABLE).toBe('Hide from table');
});
it('has SHOW_TO_TABLE equal to exact canonical string', () => {
expect(LABELS.SHOW_TO_TABLE).toBe('Show to table');
});
it('has FIRST_TOOLTIP set', () => {
expect(LABELS.FIRST_TOOLTIP).toBe('Hide this participant from other players.');
});
it('is frozen (immutable)', () => {
expect(Object.isFrozen(LABELS)).toBe(true);
});
});
describe('resolveTargetState()', () => {
it('returns active when current state is hidden', () => {
expect(resolveTargetState('hidden')).toBe('active');
});
it('returns hidden when current state is active', () => {
expect(resolveTargetState('active')).toBe('hidden');
});
it('returns hidden when current state is self-muted', () => {
expect(resolveTargetState('self-muted')).toBe('hidden');
});
it('returns hidden when current state is cam-lost', () => {
expect(resolveTargetState('cam-lost')).toBe('hidden');
});
it('returns hidden when current state is never-connected', () => {
expect(resolveTargetState('never-connected')).toBe('hidden');
});
});
describe('buildParticipantList()', () => {
let stateStore;
let controller;
let adapter;
beforeEach(() => {
stateStore = {
getState: vi.fn(userId => userId === 'user-1' ? 'active' : 'hidden'),
};
controller = {
hasPendingOp: vi.fn(() => false),
};
adapter = {
users: {
get: vi.fn(userId => ({
id: userId,
name: `User ${userId}`,
avatar: `avatars/${userId}.png`,
})),
current: vi.fn(() => ({ id: 'user-1' })),
},
};
});
it('returns correct shape for each participant', () => {
const list = buildParticipantList(['user-1', 'user-2'], stateStore, controller, adapter);
expect(list).toHaveLength(2);
expect(list[0]).toMatchObject({
userId: 'user-1',
name: 'User user-1',
avatarSrc: 'avatars/user-1.png',
state: 'active',
stateLabel: 'Active',
hasPendingOp: false,
isCurrentUser: true,
});
});
it('returns isEmpty-compatible empty array for no userIds', () => {
const list = buildParticipantList([], stateStore, controller, adapter);
expect(list).toHaveLength(0);
});
it('sets hasPendingOp to true when controller reports pending', () => {
controller.hasPendingOp.mockReturnValue(true);
const list = buildParticipantList(['user-1'], stateStore, controller, adapter);
expect(list[0].hasPendingOp).toBe(true);
});
it('uses mystery-man.svg fallback when avatar is null', () => {
adapter.users.get.mockReturnValue({ id: 'user-1', name: 'Alice', avatar: null });
const list = buildParticipantList(['user-1'], stateStore, controller, adapter);
expect(list[0].avatarSrc).toBe('icons/svg/mystery-man.svg');
});
it('marks only the current user as isCurrentUser', () => {
const list = buildParticipantList(['user-1', 'user-2'], stateStore, controller, adapter);
expect(list[0].isCurrentUser).toBe(true);
expect(list[1].isCurrentUser).toBe(false);
});
it('correctly maps hidden state label', () => {
const list = buildParticipantList(['user-2'], stateStore, controller, adapter);
expect(list[0].stateLabel).toBe('Hidden');
});
});
describe('ScryingPoolStrip', () => {
let stateStore;
let controller;
let avTileAdapter;
let adapter;
let strip;
beforeEach(() => {
stateStore = { getState: vi.fn(() => 'active') };
controller = { action: vi.fn(), getRevision: vi.fn(() => 0), hasPendingOp: vi.fn(() => false) };
avTileAdapter = { mount: vi.fn(), unmount: vi.fn(), setStateClass: vi.fn(), disconnect: vi.fn() };
adapter = {
users: {
get: vi.fn(() => ({ id: 'u1', name: 'Alice', avatar: 'av.png' })),
all: vi.fn(() => [{ id: 'u1' }]),
current: vi.fn(() => ({ id: 'u1' })),
},
};
strip = new ScryingPoolStrip(stateStore, controller, avTileAdapter, adapter);
});
describe('defaultOptions', () => {
it('has correct id', () => {
expect(ScryingPoolStrip.defaultOptions.id).toBe('scrying-pool-strip');
});
it('has correct template path', () => {
expect(ScryingPoolStrip.defaultOptions.template).toContain('roster-strip.hbs');
});
it('is not resizable', () => {
expect(ScryingPoolStrip.defaultOptions.resizable).toBe(false);
});
it('popOut is true', () => {
expect(ScryingPoolStrip.defaultOptions.popOut).toBe(true);
});
});
describe('getData()', () => {
it('returns participants array', () => {
const data = strip.getData();
expect(Array.isArray(data.participants)).toBe(true);
});
it('returns isExpanded property', () => {
const data = strip.getData();
expect(typeof data.isExpanded).toBe('boolean');
});
it('returns isEmpty true when no participants', () => {
adapter.users.all.mockReturnValue([]);
const data = strip.getData();
expect(data.isEmpty).toBe(true);
});
it('returns isEmpty false when participants exist', () => {
const data = strip.getData();
expect(data.isEmpty).toBe(false);
});
});
});