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>
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
// @ts-nocheck
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import { createFoundryAdapterMock } from '../../helpers/foundryAdapterMock.js';
|
||||
|
||||
// Mock ScryingPoolStrip before it's imported so Application global isn't needed
|
||||
vi.mock('../../../src/ui/gm/ScryingPoolStrip.js', () => ({
|
||||
ScryingPoolStrip: vi.fn().mockImplementation(() => ({
|
||||
render: vi.fn().mockResolvedValue(undefined),
|
||||
close: vi.fn(),
|
||||
rendered: false,
|
||||
})),
|
||||
}));
|
||||
|
||||
import { RoleRenderer } from '../../../src/ui/RoleRenderer.js';
|
||||
|
||||
function makeAVTileAdapter() {
|
||||
return {
|
||||
mount: vi.fn(),
|
||||
unmount: vi.fn(),
|
||||
setStateClass: vi.fn(),
|
||||
disconnect: vi.fn(),
|
||||
onTileRerender: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
function makeStateStore() {
|
||||
const states = new Map();
|
||||
return {
|
||||
getState: vi.fn(userId => states.get(userId) ?? 'active'),
|
||||
_states: states,
|
||||
};
|
||||
}
|
||||
|
||||
function makeController() {
|
||||
return {
|
||||
action: vi.fn(),
|
||||
getRevision: vi.fn(() => 0),
|
||||
hasPendingOp: vi.fn(() => false),
|
||||
};
|
||||
}
|
||||
|
||||
describe('RoleRenderer', () => {
|
||||
let adapter;
|
||||
let avTileAdapter;
|
||||
let stateStore;
|
||||
let controller;
|
||||
let renderer;
|
||||
let hooksStub;
|
||||
|
||||
beforeEach(() => {
|
||||
hooksStub = { on: vi.fn(), off: vi.fn(), once: vi.fn(), callAll: vi.fn() };
|
||||
vi.stubGlobal('Hooks', hooksStub);
|
||||
vi.stubGlobal('game', { webrtc: {}, user: { setFlag: vi.fn(), getFlag: vi.fn(() => null) } });
|
||||
|
||||
adapter = createFoundryAdapterMock();
|
||||
avTileAdapter = makeAVTileAdapter();
|
||||
stateStore = makeStateStore();
|
||||
controller = makeController();
|
||||
renderer = new RoleRenderer(stateStore, controller, avTileAdapter, adapter);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('stores all injected dependencies without side effects', () => {
|
||||
expect(renderer._stateStore).toBe(stateStore);
|
||||
expect(renderer._controller).toBe(controller);
|
||||
expect(renderer._avTileAdapter).toBe(avTileAdapter);
|
||||
expect(renderer._adapter).toBe(adapter);
|
||||
});
|
||||
|
||||
it('does not register any Hooks in constructor', () => {
|
||||
expect(hooksStub.on).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('_strip is null before openStrip()', () => {
|
||||
expect(renderer._strip).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('init()', () => {
|
||||
it('registers scrying-pool:stateChanged hook', () => {
|
||||
renderer.init();
|
||||
expect(hooksStub.on).toHaveBeenCalledWith(
|
||||
'scrying-pool:stateChanged',
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('registers scrying-pool:controllerAction hook', () => {
|
||||
renderer.init();
|
||||
expect(hooksStub.on).toHaveBeenCalledWith(
|
||||
'scrying-pool:controllerAction',
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('registers updateUser hook', () => {
|
||||
renderer.init();
|
||||
expect(hooksStub.on).toHaveBeenCalledWith('updateUser', expect.any(Function));
|
||||
});
|
||||
});
|
||||
|
||||
describe('_applyAVTileState()', () => {
|
||||
it('calls setStateClass on avTileAdapter with userId and state', () => {
|
||||
renderer._applyAVTileState('user-1', 'active');
|
||||
expect(avTileAdapter.setStateClass).toHaveBeenCalledWith('user-1', 'active');
|
||||
});
|
||||
|
||||
it('mounts lock-overlay element when state is hidden', () => {
|
||||
renderer._applyAVTileState('user-1', 'hidden');
|
||||
expect(avTileAdapter.mount).toHaveBeenCalled();
|
||||
const el = avTileAdapter.mount.mock.calls[0][1];
|
||||
expect(el.dataset.spRole).toBe('lock-overlay');
|
||||
});
|
||||
|
||||
it('unmounts lock-overlay when state transitions away from hidden', () => {
|
||||
renderer._applyAVTileState('user-1', 'active');
|
||||
expect(avTileAdapter.unmount).toHaveBeenCalledWith('user-1');
|
||||
});
|
||||
|
||||
it('mounts portrait-fallback when state is never-connected', () => {
|
||||
renderer._applyAVTileState('user-1', 'never-connected');
|
||||
expect(avTileAdapter.mount).toHaveBeenCalled();
|
||||
const el = avTileAdapter.mount.mock.calls[0][1];
|
||||
expect(el.dataset.spRole).toBe('portrait-fallback');
|
||||
});
|
||||
|
||||
it('mounts portrait-fallback when state is cam-lost', () => {
|
||||
renderer._applyAVTileState('user-1', 'cam-lost');
|
||||
expect(avTileAdapter.mount).toHaveBeenCalled();
|
||||
const el = avTileAdapter.mount.mock.calls[0][1];
|
||||
expect(el.dataset.spRole).toBe('portrait-fallback');
|
||||
});
|
||||
|
||||
it('unmounts portrait-fallback when state is not camera-absent', () => {
|
||||
renderer._applyAVTileState('user-1', 'active');
|
||||
expect(avTileAdapter.unmount).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not mount any overlay for active state', () => {
|
||||
renderer._applyAVTileState('user-1', 'active');
|
||||
expect(avTileAdapter.mount).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('stateChanged hook handler', () => {
|
||||
it('calls _applyAVTileState when scrying-pool:stateChanged fires', () => {
|
||||
renderer.init();
|
||||
const spy = vi.spyOn(renderer, '_applyAVTileState');
|
||||
const handler = hooksStub.on.mock.calls.find(
|
||||
c => c[0] === 'scrying-pool:stateChanged'
|
||||
)[1];
|
||||
handler({ userId: 'user-1', state: 'hidden' });
|
||||
expect(spy).toHaveBeenCalledWith('user-1', 'hidden');
|
||||
});
|
||||
|
||||
it('handles bulk matrix payload gracefully', () => {
|
||||
renderer.init();
|
||||
const handler = hooksStub.on.mock.calls.find(
|
||||
c => c[0] === 'scrying-pool:stateChanged'
|
||||
)[1];
|
||||
// bulk payload has no userId
|
||||
expect(() => handler({ matrix: {}, timestamp: Date.now(), revision: 1 })).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('openStrip()', () => {
|
||||
it('constructs ScryingPoolStrip lazily on first call', async () => {
|
||||
const { ScryingPoolStrip } = await import('../../../src/ui/gm/ScryingPoolStrip.js');
|
||||
vi.clearAllMocks();
|
||||
renderer.openStrip();
|
||||
expect(ScryingPoolStrip).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('reuses existing strip instance on second call', async () => {
|
||||
const { ScryingPoolStrip } = await import('../../../src/ui/gm/ScryingPoolStrip.js');
|
||||
vi.clearAllMocks();
|
||||
renderer.openStrip();
|
||||
renderer.openStrip();
|
||||
expect(ScryingPoolStrip).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('calls render on the strip', () => {
|
||||
renderer.openStrip();
|
||||
expect(renderer._strip.render).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('closeStrip()', () => {
|
||||
it('calls close on the strip if it exists', () => {
|
||||
renderer.openStrip();
|
||||
const strip = renderer._strip;
|
||||
renderer.closeStrip();
|
||||
expect(strip.close).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('is no-op if strip is not open', () => {
|
||||
expect(() => renderer.closeStrip()).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user