mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-15 15:46:30 +03:00
Carry native comp capture and completion integrity fixes into the reviewed component workflow. Bump the skill to 4.4.0 and engine/platform pins to 0.1.6; keep publication separate from this release candidate. AI assistance: implemented and validated with OpenAI Codex.
48 lines
4.5 KiB
TypeScript
48 lines
4.5 KiB
TypeScript
import { describe, expect, test } from 'bun:test';
|
|
import { approveRemaining, newDraft, submission, summarize, validBox, type ReviewPacket } from './model';
|
|
const packet: ReviewPacket = { id:'review-1', revision:'packet-1', title:'Test', round:1, comp:{url:'/comp.png',width:100,height:100}, components:[{id:'art',revision:'art-1',name:'Art',medium:'Raster',note:'',box:{x:0,y:0,w:1,h:1},preview:{kind:'image',url:'/art.png'}},{id:'control',revision:'control-1',name:'Button',medium:'HTML',note:'',box:{x:0,y:0,w:.1,h:.1},preview:{kind:'page',url:'/page.html'}}] };
|
|
describe('component review drafts',()=>{
|
|
test('bulk approval still requires explicit inventory confirmation',()=>{const draft=approveRemaining(packet,newDraft(packet));expect(summarize(packet,draft).canSubmit).toBe(false);draft.inventoryConfirmed=true;expect(submission(packet,draft).requestId).toBe(packet.id);});
|
|
test('approve remaining preserves requests for repair and their feedback',()=>{const draft=newDraft(packet);draft.decisions.art={revision:'art-1',action:'revise',feedback:'Keep the original motif',split:true};const bulk=approveRemaining(packet,draft);expect(bulk.decisions.art).toEqual(draft.decisions.art);expect(summarize(packet,bulk)).toMatchObject({approved:1,revisions:1,canSubmit:true});});
|
|
test('stale packet cannot submit',()=>{const draft=approveRemaining(packet,newDraft(packet));draft.inventoryConfirmed=true;expect(()=>submission({...packet,revision:'packet-2'},draft)).toThrow('stale');});
|
|
test('changed component version invalidates only that decision',()=>{const draft=approveRemaining(packet,newDraft(packet));draft.inventoryConfirmed=true;const changed={...packet,components:packet.components.map(c=>c.id==='art'?{...c,revision:'art-2'}:c)};expect(summarize(changed,draft)).toMatchObject({approved:1,pending:1,canSubmit:false});});
|
|
test('a missing item can be submitted without approving unrelated components',()=>{const draft=newDraft(packet);draft.missing.push({id:'missing-1',name:'Brushwork',box:{x:.2,y:.3,w:.2,h:.2},feedback:'Missing texture'});expect(summarize(packet,draft)).toMatchObject({pending:2,hasFeedback:true,canSubmit:true});draft.missing[0].name=' ';expect(summarize(packet,draft).canSubmit).toBe(false);});
|
|
test('rejects invalid regions and does not share mutable submission state',()=>{expect(validBox({x:0,y:0,w:0,h:1})).toBe(false);expect(validBox({x:.9,y:0,w:.5,h:1})).toBe(false);expect(validBox({x:NaN,y:0,w:1,h:1})).toBe(false);const draft=approveRemaining(packet,newDraft(packet));draft.inventoryConfirmed=true;const receipt=submission(packet,draft);draft.decisions.art.feedback='Changed';expect(receipt.decisions.art.feedback).toBe('');});
|
|
});
|
|
|
|
|
|
test('draft-only or changed prior decisions never count as carried approval', async () => {
|
|
const { repairStatus } = await import('./model');
|
|
const history: any = {
|
|
submitted: false, packet: { round: 1 },
|
|
draft: { decisions: { art: { action: 'approve' } } },
|
|
changes: { art: { kind: 'unchanged', files: [], reasons: [] } },
|
|
};
|
|
expect(repairStatus('art', history).carried).toBe(false);
|
|
history.submitted = true;
|
|
history.changes.art.carried = true;
|
|
expect(repairStatus('art', history).carried).toBe(true);
|
|
history.changes.art.kind = 'changed';
|
|
expect(repairStatus('art', history).carried).toBe(false);
|
|
expect(repairStatus('art', history).label).toBe('Review again');
|
|
});
|
|
|
|
|
|
test('attention states cannot hide a component behind a stale approval', async () => {
|
|
const { componentState } = await import('./model');
|
|
const draft=approveRemaining(packet,newDraft(packet));
|
|
expect(componentState(packet.components[0],draft).kind).toBe('approved');
|
|
expect(componentState({...packet.components[0],revision:'new'},draft).kind).toBe('pending');
|
|
draft.decisions.art.action='revise';
|
|
expect(componentState(packet.components[0],draft)).toMatchObject({kind:'feedback',label:'Feedback ready'});
|
|
});
|
|
|
|
test('captured code keeps its implementation identity without trusting medium as proof', async () => {
|
|
const { componentPresentation } = await import('./model');
|
|
const image = packet.components[0];
|
|
expect(componentPresentation({...image, medium:'SVG'}).code).toBe(false);
|
|
const captured = {...packet.components[1], preview:{kind:'image' as const, sourceKind:'page' as const, url:'/capture.png'}};
|
|
expect(componentPresentation(captured)).toMatchObject({code:true,captured:true,label:'HTML',caption:'Rendered component',fileLabel:'Open captured preview'});
|
|
expect(componentPresentation(packet.components[1]).caption).toBe('Live component');
|
|
});
|