const test=require('node:test'); const assert=require('node:assert/strict'); const H=require('../app/static/editor-helpers.js'); function sample(){return [ {id:'root1111',type:'layout',columns:[ {width:1,blocks:[{id:'inner2222',type:'layout',gap:20,columns:[ {width:1,blocks:[{id:'deep3333',type:'image_text',title:'Original',description:'Text'}]}, {width:1,blocks:[{id:'deep4444',type:'cta',title:'CTA'}]}]}]}, {width:1,blocks:[{id:'shallow5',type:'features',title:'Features'}]} ]}, {id:'otherRoot',type:'layout',columns:[{width:1,blocks:[{id:'foreign6',type:'cta',title:'Other'}]}]} ]} const types={image_text:{fields:['title','description']},cta:{fields:['title']},layout:{fields:['columns']},features:{fields:['title']}}; test('locate second-level block by immediate parent and id, not client-supplied indexes',()=>{ let rows=sample();let f=H.findNestedChild(rows,'inner2222','deep3333'); assert.equal(f.parentIndex,0);assert.equal(f.depth,1);assert.equal(f.parent.id,'inner2222'); assert.equal(f.child.title,'Original');assert.deepEqual(f.ancestors,['root1111','inner2222']); assert.equal(H.findNestedChild(rows,'root1111','deep3333'),null); assert.equal(H.findNestedChild(rows,'otherRoot','deep3333'),null); assert.equal(H.findChildInRoot(rows,'root1111','deep3333').child.id,'deep3333'); assert.equal(H.findChildInRoot(rows,'otherRoot','deep3333'),null); }); test('only allow safe operations and supported inline text changes',()=>{ const rows=sample();const ok={action:'child-inline-text',blockId:'inner2222',childId:'deep3333',field:'title',value:'Safe title'}; assert.equal(H.safeNestedCanvasCommand(ok,rows,types),true); assert.equal(H.safeNestedCanvasCommand({...ok,field:'body'},rows,types),false); assert.equal(H.safeNestedCanvasCommand({...ok,field:'title',value:'x'.repeat(501)},rows,types),false); assert.equal(H.safeNestedCanvasCommand({...ok,blockId:'root1111'},rows,types),false); assert.equal(H.safeNestedCanvasCommand({...ok,action:'child-script'},rows,types),false); assert.equal(H.safeNestedCanvasCommand({...ok,childId:'foreign6'},rows,types),false); }); test('nested layout resolution preserves site/page boundaries and width constraints',()=>{ let rows=sample();let inner=H.findLayout(rows,'inner2222'); assert.equal(inner.rootIndex,0);assert.equal(inner.depth,1); assert.equal(H.findLayout(rows,'shallow5'),null); assert.deepEqual(H.layoutPreset(inner.layout,'left-wide'),[2,1]); assert.deepEqual(H.adjacentWidths(inner.layout,[2,1]),[2,1]); assert.equal(H.adjacentWidths(inner.layout,[.1,1]),null); assert.equal(H.findLayout(rows,'nonexistent'),null); assert.equal(H.findLayout(rows,'otherRoot').rootIndex,1); }); test('duplication rewrites all nested IDs and reordering is scoped',()=>{ const rows=sample(),seen=[]; const copy=H.duplicate(rows[0].columns[0].blocks[0],()=>('copy'+(++seen.length)).padEnd(8,'z')); assert.equal(copy.type,'layout'); assert.notEqual(copy.id,rows[0].columns[0].blocks[0].id); assert.notEqual(copy.columns[0].blocks[0].id,'deep3333'); const original=rows[0].columns[0].blocks[0].columns[0].blocks; assert.equal(H.moveNested(rows[0].columns[0].blocks[0],0,0,-1),null); assert.equal(original[0].id,'deep3333'); });