First round of test
This commit is contained in:
@@ -275,8 +275,21 @@ Hooks.once("ready", () => {
|
||||
if (adapter.users.isGM()) {
|
||||
directorsBoard = new DirectorsBoard(stateStore, scryingPoolController, adapter, scenePresetManager, playerPrivacyManager);
|
||||
directorsBoard.init();
|
||||
window.directorsBoard = directorsBoard;
|
||||
}
|
||||
|
||||
// Pre-load participant-card as a Handlebars partial for directors-board
|
||||
// ApplicationV2 requires partials to be registered explicitly
|
||||
(async () => {
|
||||
try {
|
||||
const resp = await fetch('modules/video-view-manager/templates/participant-card.hbs');
|
||||
const source = await resp.text();
|
||||
Handlebars.registerPartial('modules/video-view-manager/templates/participant-card.hbs', source);
|
||||
} catch (err) {
|
||||
console.warn('[ScryingPool] Failed to register participant-card partial:', err);
|
||||
}
|
||||
})();
|
||||
|
||||
// Story 4.1: Initialize PlayerPrivacyPanelMenu with DI dependencies
|
||||
// Story 4.2: Pass portraitFallbackHandler for portrait selection
|
||||
initPlayerPrivacyPanelMenu(adapter, playerPrivacyManager, portraitFallbackHandler);
|
||||
|
||||
@@ -49,11 +49,29 @@ export function isInitialized() {
|
||||
return _isInitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditional base class — test environment lacks foundry globals.
|
||||
*/
|
||||
const _MenuAppBase =
|
||||
typeof foundry !== 'undefined' &&
|
||||
foundry.applications?.api?.ApplicationV2
|
||||
? foundry.applications.api.ApplicationV2
|
||||
: class _FallbackMenuApp {
|
||||
static DEFAULT_OPTIONS = {};
|
||||
get rendered() { return this._rendered ?? false; }
|
||||
set rendered(v) { this._rendered = v; }
|
||||
get element() { return this._element ?? null; }
|
||||
set element(v) { this._element = v; }
|
||||
async render() { this._rendered = true; }
|
||||
async close() { this._rendered = false; }
|
||||
};
|
||||
|
||||
/**
|
||||
* PlayerPrivacyPanelMenu - Wrapper for Foundry settings menu.
|
||||
* When instantiated by Foundry, it creates a PlayerPrivacyPanel with the current user as target.
|
||||
* Extends ApplicationV2 so it passes Foundry's registerMenu validation.
|
||||
*/
|
||||
export class PlayerPrivacyPanelMenu {
|
||||
export class PlayerPrivacyPanelMenu extends _MenuAppBase {
|
||||
/**
|
||||
* @param {object} [options] - Foundry options (unused, but required by settings menu API)
|
||||
*/
|
||||
|
||||
@@ -1,57 +1,61 @@
|
||||
{{!-- Director's Board — GM camera-management overview window --}}
|
||||
<section class="scrying-pool directors-board__content"
|
||||
role="list"
|
||||
aria-label="{{localize "video-view-manager.directorsBoard.title"}}">
|
||||
<div class="directors-board__inner">
|
||||
|
||||
{{#unless isEmpty}}
|
||||
{{#each participants}}
|
||||
{{> "modules/video-view-manager/templates/participant-card.hbs"}}
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<p class="directors-board__empty" role="listitem">
|
||||
{{localize "video-view-manager.directorsBoard.empty"}}
|
||||
</p>
|
||||
{{/unless}}
|
||||
<section class="scrying-pool directors-board__content"
|
||||
role="list"
|
||||
aria-label="{{localize "video-view-manager.directorsBoard.title"}}">
|
||||
|
||||
</section>
|
||||
{{#unless isEmpty}}
|
||||
{{#each participants}}
|
||||
{{> "modules/video-view-manager/templates/participant-card.hbs"}}
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<p class="directors-board__empty" role="listitem">
|
||||
{{localize "video-view-manager.directorsBoard.empty"}}
|
||||
</p>
|
||||
{{/unless}}
|
||||
|
||||
</section>
|
||||
|
||||
<div class="directors-board__bulk-bar">
|
||||
<button type="button" class="directors-board__bulk-btn" data-action="show-all">
|
||||
{{localize "video-view-manager.directorsBoard.bulk.showAll"}}
|
||||
</button>
|
||||
<button type="button" class="directors-board__bulk-btn" data-action="hide-all">
|
||||
{{localize "video-view-manager.directorsBoard.bulk.hideAll"}}
|
||||
</button>
|
||||
{{#if hasUndo}}
|
||||
<button type="button" class="directors-board__bulk-btn directors-board__bulk-btn--undo" data-action="undo">
|
||||
{{localize "video-view-manager.directorsBoard.bulk.undo"}}
|
||||
</button>
|
||||
{{/if}}
|
||||
{{#if hasRestore}}
|
||||
<button type="button" class="directors-board__bulk-btn directors-board__bulk-btn--restore" data-action="restore-spotlight">
|
||||
{{localize "video-view-manager.directorsBoard.bulk.restore"}}
|
||||
</button>
|
||||
{{/if}}
|
||||
<button type="button" class="directors-board__help-btn" data-action="open-shortcut-panel" aria-label="{{localize "video-view-manager.directorsBoard.shortcuts.openPanel"}}">?</button>
|
||||
</div>
|
||||
|
||||
<footer class="directors-board__footer">
|
||||
<button type="button" class="directors-board__footer-btn" data-action="save-preset">
|
||||
{{localize "video-view-manager.directorsBoard.footer.savePreset"}}
|
||||
</button>
|
||||
<button type="button" class="directors-board__footer-btn" data-action="load-preset" {{#unless hasPresets}}disabled{{/unless}}>
|
||||
{{localize "video-view-manager.directorsBoard.footer.loadPreset"}}
|
||||
</button>
|
||||
<button type="button" class="directors-board__footer-btn" data-action="export-presets">
|
||||
{{localize "video-view-manager.directorsBoard.footer.exportPresets"}}
|
||||
</button>
|
||||
<button type="button" class="directors-board__footer-btn" data-action="import-presets">
|
||||
{{localize "video-view-manager.directorsBoard.footer.importPresets"}}
|
||||
</button>
|
||||
<button type="button" class="directors-board__footer-btn" data-action="toggle-preset-panel">
|
||||
{{localize "video-view-manager.directorsBoard.footer.autoApplySettings"}}
|
||||
</button>
|
||||
</footer>
|
||||
|
||||
{{!-- Scene Preset Panel - rendered via JavaScript, not Handlebars --}}
|
||||
{{!-- Panel is appended dynamically in DirectorsBoard._appendPresetPanel() --}}
|
||||
|
||||
<div class="directors-board__bulk-bar">
|
||||
<button type="button" class="directors-board__bulk-btn" data-action="show-all">
|
||||
{{localize "video-view-manager.directorsBoard.bulk.showAll"}}
|
||||
</button>
|
||||
<button type="button" class="directors-board__bulk-btn" data-action="hide-all">
|
||||
{{localize "video-view-manager.directorsBoard.bulk.hideAll"}}
|
||||
</button>
|
||||
{{#if hasUndo}}
|
||||
<button type="button" class="directors-board__bulk-btn directors-board__bulk-btn--undo" data-action="undo">
|
||||
{{localize "video-view-manager.directorsBoard.bulk.undo"}}
|
||||
</button>
|
||||
{{/if}}
|
||||
{{#if hasRestore}}
|
||||
<button type="button" class="directors-board__bulk-btn directors-board__bulk-btn--restore" data-action="restore-spotlight">
|
||||
{{localize "video-view-manager.directorsBoard.bulk.restore"}}
|
||||
</button>
|
||||
{{/if}}
|
||||
<button type="button" class="directors-board__help-btn" data-action="open-shortcut-panel" aria-label="{{localize "video-view-manager.directorsBoard.shortcuts.openPanel"}}">?</button>
|
||||
</div>
|
||||
|
||||
<footer class="directors-board__footer">
|
||||
<button type="button" class="directors-board__footer-btn" data-action="save-preset">
|
||||
{{localize "video-view-manager.directorsBoard.footer.savePreset"}}
|
||||
</button>
|
||||
<button type="button" class="directors-board__footer-btn" data-action="load-preset" {{#unless hasPresets}}disabled{{/unless}}>
|
||||
{{localize "video-view-manager.directorsBoard.footer.loadPreset"}}
|
||||
</button>
|
||||
<button type="button" class="directors-board__footer-btn" data-action="export-presets">
|
||||
{{localize "video-view-manager.directorsBoard.footer.exportPresets"}}
|
||||
</button>
|
||||
<button type="button" class="directors-board__footer-btn" data-action="import-presets">
|
||||
{{localize "video-view-manager.directorsBoard.footer.importPresets"}}
|
||||
</button>
|
||||
<button type="button" class="directors-board__footer-btn" data-action="toggle-preset-panel">
|
||||
{{localize "video-view-manager.directorsBoard.footer.autoApplySettings"}}
|
||||
</button>
|
||||
</footer>
|
||||
|
||||
{{!-- Scene Preset Panel - rendered via JavaScript, not Handlebars --}}
|
||||
{{!-- Panel is appended dynamically in DirectorsBoard._appendPresetPanel() --}}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
@@ -322,10 +322,10 @@ describe('DirectorsBoard', () => {
|
||||
expect(controller.action).toHaveBeenCalledWith('board', 'u2', 'active', expect.any(String), expect.any(Number));
|
||||
});
|
||||
|
||||
it('clears _undoSnapshot after use (single-step only)', () => {
|
||||
it('clears _undoSnapshot after use (single-step only)', async () => {
|
||||
board._undoSnapshot = new Map([['u1', 'hidden']]);
|
||||
stateStore.getState.mockReturnValue('active');
|
||||
board.undo();
|
||||
await board.undo();
|
||||
expect(board._undoSnapshot).toBeNull();
|
||||
});
|
||||
|
||||
@@ -335,10 +335,10 @@ describe('DirectorsBoard', () => {
|
||||
expect(controller.action).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('second undo is unavailable after first (no-op)', () => {
|
||||
it('second undo is unavailable after first (no-op)', async () => {
|
||||
board._undoSnapshot = new Map([['u1', 'hidden']]);
|
||||
stateStore.getState.mockReturnValue('active');
|
||||
board.undo();
|
||||
await board.undo();
|
||||
board.undo();
|
||||
expect(controller.action).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
@@ -430,10 +430,10 @@ describe('DirectorsBoard spotlight', () => {
|
||||
expect(controller.action).toHaveBeenCalledWith('board', 'u2', 'active', expect.any(String), expect.any(Number));
|
||||
});
|
||||
|
||||
it('clears _spotlightSnapshot after restore', () => {
|
||||
it('clears _spotlightSnapshot after restore', async () => {
|
||||
board._spotlightSnapshot = new Map([['u1', 'active']]);
|
||||
stateStore.getState.mockReturnValue('active');
|
||||
board.restoreSpotlight();
|
||||
await board.restoreSpotlight();
|
||||
expect(board._spotlightSnapshot).toBeNull();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user