fix(builder): deterministic ids for NumberCounter + form-relay marker
Two components of the det-id bug class were missed in the earlier migration: NumberCounter's wrapper/counter ids and count-up script, and the shared form-relay-wiring fid (used by ContactForm/FormContainer). Both used Math.random() for exported HTML ids, breaking caching/diffing across exports. Migrate both to scopeId(nodeId, fallbackSeed, prefix), threading nodeId through NumberCounter.toHtml and relayFormWiring (via ContactForm.toHtml and FormContainer.toHtml, both now passing nodeId as their 3rd arg).
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { NumberCounter } from './NumberCounter';
|
||||
|
||||
const toHtml = (NumberCounter as any).toHtml;
|
||||
|
||||
const counters = [
|
||||
{ number: 150, suffix: '+', label: 'Projects' },
|
||||
{ number: 50, suffix: '+', label: 'Clients' },
|
||||
];
|
||||
|
||||
describe('NumberCounter.toHtml deterministic + unique scope ids (thread node id, no Math.random)', () => {
|
||||
test('same node id -> identical output across calls (deterministic)', () => {
|
||||
const { html: html1 } = toHtml({ counters }, '', 'node-nc1');
|
||||
const { html: html2 } = toHtml({ counters }, '', 'node-nc1');
|
||||
expect(html1).toBe(html2);
|
||||
});
|
||||
|
||||
test('different node ids -> distinct, non-colliding nc_ scopes (identical props, no collision)', () => {
|
||||
const { html: html1 } = toHtml({ counters }, '', 'node-nc1');
|
||||
const { html: html2 } = toHtml({ counters }, '', 'node-nc2');
|
||||
const wrapId1 = html1.match(/<div id="(nc_[^"]+)"/)![1];
|
||||
const wrapId2 = html2.match(/<div id="(nc_[^"]+)"/)![1];
|
||||
expect(wrapId1).not.toBe(wrapId2);
|
||||
});
|
||||
|
||||
test('wrapper id, per-counter ids, and inline script agree on the same uid', () => {
|
||||
const { html } = toHtml({ counters }, '', 'node-nc1');
|
||||
const wrapId = html.match(/<div id="(nc_[^"]+)"/)![1];
|
||||
expect(html).toContain(`id="${wrapId}_n0"`);
|
||||
expect(html).toContain(`id="${wrapId}_n1"`);
|
||||
expect(html).toContain(`var uid="${wrapId}"`);
|
||||
expect(html).toContain('document.getElementById(uid)');
|
||||
expect(html).toContain('document.getElementById(uid+"_n"+i)');
|
||||
});
|
||||
|
||||
test('no nodeId (legacy 2-arg call): still deterministic across repeated calls, not random', () => {
|
||||
const { html: html1 } = toHtml({ counters }, '');
|
||||
const { html: html2 } = toHtml({ counters }, '');
|
||||
expect(html1).toBe(html2);
|
||||
});
|
||||
|
||||
test('two different node ids never collide even with default (no counters override) props', () => {
|
||||
const { html: html1 } = toHtml({}, '', 'node-a');
|
||||
const { html: html2 } = toHtml({}, '', 'node-b');
|
||||
const wrapId1 = html1.match(/<div id="(nc_[^"]+)"/)![1];
|
||||
const wrapId2 = html2.match(/<div id="(nc_[^"]+)"/)![1];
|
||||
expect(wrapId1).not.toBe(wrapId2);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user