Skip to content

Commit ca59bc7

Browse files
committed
test(reference-card): the fold that fixed a production regression had no test on the component it regressed
`<roxy-data>` got component-level fold coverage when its duplicate-column bug was fixed, sabotage-verified and documented. `<roxy-reference-card>` has the same exposure, builds its ENTIRE output from `Object.entries`, and had none. That asymmetry is why this one reached production: the utility was tested, the caller was not, and nothing asserted the rendered shape. Five tests against the real `/human-design/gates/51?lang=es` payload, the response that exposed it live. They cover all three levels that read a key, because folding at only one looks fixed from the outside: the record, a nested object one level down, and the primitive join in `objectLabel` that labels an object inside an array. Sabotage-verified per call site rather than as a group. Removing the top-level fold, the nested fold, or the `objectLabel` fold each turns exactly one test red, so no site is guarded only by another site's assertion. Also asserts the English path is untouched, since the fix must be invisible to a response that carries no localized field. Separately confirmed in real chromium against the built bundle at `<html lang="es">`: headings `Number, Center Name` with no `* Localized` twin, values `51, Corazón`, title `Conmoción`. Not added as an e2e, since the fold is plain logic and this repo reserves e2e for what happy-dom cannot prove, the real cascade and `::part()`.
1 parent 3145f40 commit ca59bc7

1 file changed

Lines changed: 99 additions & 0 deletions

File tree

packages/ui/tests/components.test.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4851,3 +4851,102 @@ describe('the natal chart declares its equal-sector fallback', () => {
48514851
el.remove();
48524852
});
48534853
});
4854+
4855+
/**
4856+
* The second generic renderer against a response that carries its own translation.
4857+
*
4858+
* @remarks
4859+
* `<roxy-reference-card>` builds its ENTIRE output from `Object.entries`, so it shares `<roxy-data>`'s exposure and had none of its repair: the day the API began echoing `nameLocalized` beside `name`, every non-English reference lookup drew one fact as two rows, each under its own heading, on a page whose owner changed nothing. It reached production, which `<roxy-data>` did not, because nothing here asserted the shape.
4860+
*
4861+
* The payload below is the real `/human-design/gates/51?lang=es` response, the one that exposed it live.
4862+
*
4863+
* Three levels read a key and each is covered, because folding at only one of them looks fixed from the outside: the record itself, a nested object one level down (`collect` recurses to depth 2), and the primitive join in `objectLabel` that labels an object inside an array.
4864+
*
4865+
* Sabotage-verified by dropping each `foldLocalized` call in turn: every one of these goes red on its own.
4866+
*/
4867+
describe('roxy-reference-card folds a localized field into the field it translates', () => {
4868+
const mount = async (data: unknown) => {
4869+
const el = document.createElement('roxy-reference-card') as HTMLElement & {
4870+
data?: unknown;
4871+
};
4872+
document.body.appendChild(el);
4873+
el.data = data;
4874+
await settled(el);
4875+
const root = el.shadowRoot as ShadowRoot;
4876+
return {
4877+
el,
4878+
labels: [...root.querySelectorAll('dt')].map((dt) =>
4879+
dt.textContent?.trim(),
4880+
),
4881+
values: [...root.querySelectorAll('dd')].map((dd) =>
4882+
dd.textContent?.trim(),
4883+
),
4884+
text: (root.innerHTML ?? '').replace(/<style[\s\S]*?<\/style>/g, ''),
4885+
};
4886+
};
4887+
4888+
/** The live Spanish gate lookup, verbatim. */
4889+
const GATE = {
4890+
number: 51,
4891+
name: 'Shock',
4892+
nameLocalized: 'Conmoción',
4893+
centerName: 'Heart',
4894+
centerNameLocalized: 'Corazón',
4895+
};
4896+
4897+
test('a translated lookup prints each fact ONCE, in the reader language', async () => {
4898+
const { el, labels, values } = await mount(GATE);
4899+
expect(labels).not.toContain('Name Localized');
4900+
expect(labels).not.toContain('Center Name Localized');
4901+
expect(labels.filter((l) => l === 'Center Name')).toHaveLength(1);
4902+
expect(values).toContain('Corazón');
4903+
expect(values).not.toContain('Heart');
4904+
el.remove();
4905+
});
4906+
4907+
test('an English lookup, which carries no localized field, renders exactly as before', async () => {
4908+
const { nameLocalized, centerNameLocalized, ...english } = GATE;
4909+
expect([nameLocalized, centerNameLocalized]).toEqual([
4910+
'Conmoción',
4911+
'Corazón',
4912+
]);
4913+
const { el, values } = await mount(english);
4914+
expect(values).toContain('Heart');
4915+
expect(values).not.toContain('Corazón');
4916+
el.remove();
4917+
});
4918+
4919+
test('a localized field with no canonical partner still renders', async () => {
4920+
// Suppressing on the name alone would delete the only copy of the value.
4921+
const { el, labels, values } = await mount({
4922+
number: 51,
4923+
noteLocalized: 'Nota',
4924+
});
4925+
expect(labels).toContain('Note Localized');
4926+
expect(values).toContain('Nota');
4927+
el.remove();
4928+
});
4929+
4930+
test('a nested object one level down folds too', async () => {
4931+
const { el, labels, values } = await mount({
4932+
number: 51,
4933+
channel: { name: 'Shock', nameLocalized: 'Conmoción' },
4934+
});
4935+
expect(labels.some((l) => l?.includes('Localized'))).toBe(false);
4936+
expect(values).toContain('Conmoción');
4937+
expect(values).not.toContain('Shock');
4938+
el.remove();
4939+
});
4940+
4941+
test('an object inside an array is labelled from the folded values only', async () => {
4942+
// objectLabel joins every primitive, so an unfolded row reads
4943+
// "Shock · Conmoción": the same fact twice inside one chip.
4944+
const { el, text } = await mount({
4945+
number: 51,
4946+
partners: [{ name: 'Shock', nameLocalized: 'Conmoción' }],
4947+
});
4948+
expect(text).toContain('Conmoción');
4949+
expect(text).not.toContain('Shock · Conmoción');
4950+
el.remove();
4951+
});
4952+
});

0 commit comments

Comments
 (0)