/** * Regression test for the Content Editor browser script: deleting an array * item via its ❌ button must reindex the remaining items, otherwise collect() * produces sparse arrays (null holes) that fail schema validation * ("$.details.services[1].specs.items[0]: string érték szükséges"). * * Runs the REAL scripts/cms-editor-client.js in jsdom and clicks the actual * delete buttons — earlier coverage only exercised reindexItems() directly, * which missed that the onclick handler removed the node BEFORE looking up * its container (detached node → closest() === null → no reindex). */ import fs from 'fs' import path from 'path' const clientJs = fs.readFileSync(path.join(__dirname, '../../../scripts/cms-editor-client.js'), 'utf8') const service = (n: number) => ({ id: `svc-${n}`, title: `Szolgáltatás ${n}`, description: `Leírás ${n}`, icon: '🔧', features: [`feature ${n}`], ctaText: 'CTA', }) const makeData = () => ({ details: { title: 'Részletek', subtitle: 'Alcím', services: [ { icon: 'a', title: 's0', description: 'd0', specs: { title: 't0', items: ['a0', 'b0', 'c0'] } }, { icon: 'b', title: 's1', description: 'd1', specs: { title: 't1', items: ['a1', 'b1', 'c1'] } }, { icon: 'c', title: 's2', description: 'd2', specs: { title: 't2', items: ['a2', 'b2', 'c2'] } }, ], }, }) function bootClient(data: unknown) { ;(global as any).DATA = data ;(global as any).FILE = 'services' ;(global as any).CSRF_TOKEN = 'test-token' ;(global as any).fetch = jest.fn() document.body.innerHTML = '
' // sloppy-mode eval publishes the script's functions on the global object ;(0, eval)(clientJs) } function deleteButtonFor(dataPath: string): HTMLButtonElement { const field = document.querySelector(`[data-path="${CSS.escape(dataPath)}"]`) as HTMLElement expect(field).not.toBeNull() const wrap = field.closest('.str-item') as HTMLElement expect(wrap).not.toBeNull() return wrap.querySelector('.btn-del') as HTMLButtonElement } afterEach(() => { delete (global as any).DATA delete (global as any).FILE delete (global as any).CSRF_TOKEN }) describe('Content Editor client delete/reindex', () => { it('deleting a nested string array item keeps the remaining items dense', () => { const data = makeData() bootClient(data) deleteButtonFor('details.services[1].specs.items[0]').click() const collected = (global as any).collect() expect(collected.details.services[1].specs.items).toEqual(['b1', 'c1']) expect(collected.details.services[0].specs.items).toEqual(['a0', 'b0', 'c0']) expect(collected.details.services[2].specs.items).toEqual(['a2', 'b2', 'c2']) }) it('deleting an object card reindexes the outer array', () => { const data = makeData() bootClient(data) const cardHeader = Array.from(document.querySelectorAll('.card-header')) .find(h => h.textContent === 'details.services[1]') as HTMLElement expect(cardHeader).not.toBeNull() const card = cardHeader.closest('.obj-card') as HTMLElement ;(card.querySelector('.btn-del-card') as HTMLButtonElement).click() const collected = (global as any).collect() expect(collected.details.services).toHaveLength(2) expect(collected.details.services[0].title).toBe('s0') expect(collected.details.services[1].title).toBe('s2') expect(collected.details.services[1].specs.items).toEqual(['a2', 'b2', 'c2']) }) })