* feat(inspector): expose bounded physical file evidence * fix(inspector): distinguish ambiguous saved associations * feat(inspector): derive conservative generation facts * fix(inspector): guard fact ancestry and inspection state * feat(inspector): share complete file details and exports * fix(inspector): preserve fact states and touch targets * feat(viewer): navigate the loaded media collection Refs #216 * fix(viewer): refit after current stage geometry changes Ref #216. Observe the current media stage after readiness and wrapped title changes without rebuilding focused controls or resetting transforms at unchanged dimensions. * feat(viewer): add lifecycle-safe slideshow and media preferences * fix(viewer): resume gallery continuation after close (#216) * fix(images): guard source recovery and wrap viewer actions Clear stale root and folder intent, cancel old pages, and wait for current source discovery before loading a refreshed collection. Preserve safe retry through failed, superseded, and disposed discovery. Wrap narrow viewer footer actions and wait for measured gallery viewport settling in responsive checks. Refs #216; addresses PR #224 review. * test(viewer): await gallery layout after viewport changes Require actual inner, root client, and document widths to agree after each responsive-case viewport and CSS zoom transition. Keep the existing exact frame, overflow, focus, and coarse target assertions unchanged. Observed PR #224 CI initial mobile resize at537px before pending card relayout settled to356px. Refs #216.
45 lines
3.0 KiB
JavaScript
45 lines
3.0 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { inspectPath, promptRoles, createInspectionLoader, mountPromptBlocks } from '../../web/js/workspace/prompt-inspector.mjs';
|
|
|
|
test('inspection keeps exact physical root and encodes every path component', () => {
|
|
assert.equal(inspectPath({root_index:2,path:'a b/x#%.png'}), '/prompt_manager/images/inspect/a%20b/x%23%25.png?root=2');
|
|
});
|
|
test('only confirmed strings can be sent, including explicit empty negatives', () => {
|
|
const record = {positive:{status:'resolved',text:'full\npositive'},negative:{status:'resolved',text:''}};
|
|
assert.deepEqual(promptRoles(record), {positive:'full\npositive',negative:''});
|
|
for (const status of ['absent','ambiguous','unresolved','other']) {
|
|
assert.deepEqual(promptRoles({positive:{status,text:'placeholder'},negative:{status:'resolved',text:null}}), {});
|
|
}
|
|
assert.deepEqual(promptRoles(null), {});
|
|
});
|
|
test('already aborted prompt blocks touch no container DOM or active handlers', () => {
|
|
const parent = new AbortController(); parent.abort();
|
|
const container = new Proxy({}, { get() { assert.fail('aborted mount accessed the container'); } });
|
|
const parentScope = { signal: parent.signal };
|
|
const view = mountPromptBlocks(container, { parentScope, sender: { open() { assert.fail('aborted action'); } } });
|
|
view.update({ positive: { status: 'resolved', text: 'late' } });
|
|
view.setState({ loading: true }); view.dispose();
|
|
});
|
|
test('superseded requests and disposed loaders cannot replace current metadata', async () => {
|
|
const pending = [], changes = [];
|
|
const loader = createInspectionLoader({get:(url,{signal})=>new Promise(resolve=>pending.push({url,signal,resolve}))}, value=>changes.push(value));
|
|
const first=loader.load({root_index:0,path:'one.png'});
|
|
const second=loader.load({root_index:1,path:'one.png'});
|
|
assert.equal(pending[0].signal.aborted,true);
|
|
pending[1].resolve({prompts:{positive:{status:'resolved',text:'latest'}}}); await second;
|
|
pending[0].resolve({prompts:{positive:{status:'resolved',text:'old'}}}); await first;
|
|
assert.equal(changes.at(-1).prompts.positive.text,'latest');
|
|
const third=loader.load({root_index:0,path:'third.png'}); loader.dispose();
|
|
assert.equal(pending[2].signal.aborted,true);
|
|
pending[2].resolve({prompts:{positive:{status:'resolved',text:'disposed'}}}); await third;
|
|
assert.equal(changes.at(-1).loading,true);
|
|
});
|
|
test('failed reads report a retryable error and later reads recover', async () => {
|
|
let fail=true; const changes=[];
|
|
const loader=createInspectionLoader({get:async()=>{if(fail)throw Error('bad');return {prompts:{}};}},value=>changes.push(value));
|
|
await loader.load({root_index:0,path:'one.png'}); assert.equal(changes.at(-1).error,true);
|
|
fail=false; await loader.load({root_index:0,path:'one.png'}); assert.deepEqual(changes.at(-1).prompts,{});
|
|
loader.dispose(); await loader.load({root_index:0,path:'one.png'}); assert.equal(changes.at(-1).loading,undefined);
|
|
});
|