Update Owliver language and suggestions
This commit is contained in:
@@ -1513,6 +1513,14 @@ record('the Owliver skill offers its chip, and draws no card',
|
||||
*/
|
||||
console.log('\n── Owliver suggestions from the API ──');
|
||||
|
||||
/* i18next, before ANY component renders. Every t() in the tree returns its own
|
||||
key until this has run, so a block that forgets it fails its parity check for
|
||||
a reason that has nothing to do with what it is testing. The app gets this
|
||||
from App.tsx; a harness that loads page modules directly has to ask. Loaded
|
||||
once here rather than per block, which is how two blocks came to be missing
|
||||
it while a third had it. */
|
||||
await server.ssrLoadModule('/src/lib/i18n/index.ts');
|
||||
|
||||
const serverSuggestions = await server.ssrLoadModule('/src/lib/skills/serverSuggestions.js');
|
||||
|
||||
/* ── The request ─────────────────────────────────────────────────────────── */
|
||||
@@ -3869,7 +3877,11 @@ record('both agent menus offer removal, and neither offers it unconditionally',
|
||||
`${removeItems.length} remove item(s), each behind (overridden || !shipped)`);
|
||||
|
||||
record('a custom agent is offered Remove, and a built-in one is only offered a revert',
|
||||
removeItems.every((block) => / Remove</.test(block) && /Revert to shipped/.test(block)
|
||||
/* Matched by translation KEY rather than by the English words: the labels
|
||||
now come from the catalogue, and a check that greps for "Remove" would be
|
||||
asserting on which language the file happens to be written in. */
|
||||
removeItems.every((block) => /workspaceAgents\.remove/.test(block)
|
||||
&& /workspaceAgents\.revertToShipped/.test(block)
|
||||
&& /shipped\s*\?/.test(block)),
|
||||
'the label is chosen by `shipped`, not by name');
|
||||
|
||||
@@ -3923,7 +3935,7 @@ record('...while Archived stays reachable as the place removal is undone',
|
||||
the confirmation names "Archived" while no such tab is on screen. */
|
||||
record('...and Archived is offered even when it is empty',
|
||||
!/\{archivedCount > 0 && \(/.test(agentsListSource)
|
||||
&& /Archived <span[^>]*>\(\{archivedCount\}\)/.test(agentsListSource),
|
||||
&& /workspaceAgents\.archived'\)\} <span[^>]*>\(\{archivedCount\}\)/.test(agentsListSource),
|
||||
'the tab is unconditional and shows its count');
|
||||
|
||||
record('...and reverting a built-in agent is not worded as a removal',
|
||||
@@ -4880,7 +4892,7 @@ record('every field the editor had is still editable',
|
||||
record('the header still states version, status and customization',
|
||||
/v\{agent\.version\}/.test(detailSource)
|
||||
&& /\{agent\.status\}/.test(detailSource)
|
||||
&& /isOverridden\(id\) && <Badge variant="info">Customized/.test(detailSource)
|
||||
&& /isOverridden\(id\) && <Badge variant="info">\{t\('agentDetail\.customized'\)\}/.test(detailSource)
|
||||
&& /Publish update/.test(detailSource),
|
||||
'v · status · Customized · Publish update');
|
||||
|
||||
@@ -9082,7 +9094,14 @@ console.log('\n── Candidates vs Talent Pool ──');
|
||||
}
|
||||
html = html.slice(0, at) + html.slice(i);
|
||||
}
|
||||
return { pass: html === positionsBefore, detail: html === positionsBefore ? '' : `len ${positionsBefore.length}->${html.length}` };
|
||||
/* Compared with runs of whitespace collapsed. Text that used to be
|
||||
written across several lines of JSX now comes from the catalogue as
|
||||
one line, so the markup differs by newlines and indentation that HTML
|
||||
collapses anyway. Everything this check exists to catch — a changed
|
||||
element, class, attribute or word — still fails it. */
|
||||
const ws = (h) => h.replace(/\s+/g, ' ').trim();
|
||||
const same = ws(html) === ws(positionsBefore);
|
||||
return { pass: same, detail: same ? '' : `len ${positionsBefore.length}->${html.length}` };
|
||||
})().pass);
|
||||
}
|
||||
}
|
||||
@@ -9323,6 +9342,106 @@ console.log('\n── Candidates vs Talent Pool ──');
|
||||
return { pass: ids.length > 0, detail: ids.join(', ') };
|
||||
})().pass);
|
||||
}
|
||||
|
||||
/* ── The page actually translates ───────────────────────────────────────
|
||||
*
|
||||
* The parity checks above all run in English, so every one of them would
|
||||
* still pass if `t()` were wired up and the Spanish catalogue were empty.
|
||||
* This renders the same page in Spanish and insists the words CHANGED.
|
||||
*
|
||||
* Asserting on specific strings rather than merely "different": a page
|
||||
* where t() returns raw keys also differs from English, and that is the
|
||||
* failure this is most likely to catch.
|
||||
*/
|
||||
{
|
||||
const i18n = (await server.ssrLoadModule('/src/lib/i18n/index.ts')).default;
|
||||
const Page = (await server.ssrLoadModule('/src/pages/admin/ControlCenter.jsx')).default;
|
||||
const render = () => {
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false, enabled: false } } });
|
||||
return stripControls(renderToStaticMarkup(
|
||||
React.createElement(R, { initialEntries: ['/admin'] },
|
||||
React.createElement(QueryClientProvider, { client },
|
||||
React.createElement(UiEditingProvider, { page: 'control-center' },
|
||||
React.createElement(Page))))
|
||||
));
|
||||
};
|
||||
|
||||
const en = words(render());
|
||||
await i18n.changeLanguage('es');
|
||||
const es = words(render());
|
||||
await i18n.changeLanguage('en');
|
||||
|
||||
record('Control Center renders in Spanish', es !== en);
|
||||
record('Control Center shows Spanish words, not keys', (() => {
|
||||
const want = ['Centro de control', 'Vacantes abiertas', 'Candidatos'];
|
||||
const missing = want.filter((w) => !es.includes(w));
|
||||
const keys = /controlCenter\.title|metrics\.|sections\./.test(es);
|
||||
return {
|
||||
pass: missing.length === 0 && !keys,
|
||||
detail: keys ? 'raw keys rendered' : (missing.length ? `missing ${missing.join(', ')}` : 'translated'),
|
||||
};
|
||||
})().pass);
|
||||
record('switching back restores English', words(render()) === en);
|
||||
|
||||
/* THE CHECK THAT MATTERS, and the one whose absence let a half-translated
|
||||
page be reported as finished. "es !== en" passes when a single word
|
||||
changed, which is exactly the state a reader sees as broken: Spanish
|
||||
headings sitting on top of English captions, legends and stage names.
|
||||
So this names the English the page used to show and insists none of it
|
||||
survives the switch. A key added to the catalogue but never wired into
|
||||
the component fails here, where every other check stays green. */
|
||||
record('no English survives on the Spanish Control Center', (() => {
|
||||
const english = [
|
||||
'Applications, screening, interviews and hires',
|
||||
'entered · conversion and drop-off by stage',
|
||||
'Entry Stage', 'Primary Pipeline Bottleneck',
|
||||
'pass through and', 'Open positions', 'In review', 'AI screened',
|
||||
'Avg KROW score', 'Hiring activity', 'Pipeline intelligence',
|
||||
'Position performance', 'Action center', 'Active positions',
|
||||
'Top talent', 'Recent activity', 'Shortlisted', 'Interview',
|
||||
];
|
||||
const left = english.filter((e) => es.includes(e));
|
||||
return { pass: left.length === 0, detail: left.length ? `still English: ${left.join(' | ')}` : 'fully translated' };
|
||||
})().pass);
|
||||
|
||||
/* The panel writes its own greeting, description and placeholders in the
|
||||
browser — `dynamic.ts` — so a translated DASHBOARD with an English
|
||||
Owliver is the exact half-done state this is here to catch. */
|
||||
const dyn = await server.ssrLoadModule('/src/components/ai-assistant/dynamic.ts');
|
||||
const facts = {
|
||||
today: new Date('2026-10-07T09:00:00'),
|
||||
total: 24, postings: new Array(8),
|
||||
unscoredProfiles: [], activity24h: [],
|
||||
};
|
||||
const esIntro = dyn.buildIntro('admin.controlCenter', facts, 'Aravind', i18n.getFixedT('es'));
|
||||
const enIntro = dyn.buildIntro('admin.controlCenter', facts, 'Aravind', i18n.getFixedT('en'));
|
||||
|
||||
record('the assistant greeting translates', (() => {
|
||||
const ok = esIntro.title.startsWith('Buenos días') && enIntro.title.startsWith('Good morning');
|
||||
return { pass: ok, detail: `${enIntro.title} / ${esIntro.title}` };
|
||||
})().pass);
|
||||
|
||||
record('the assistant greeting agrees in gender and number', (() => {
|
||||
/* "Buenos días" and "Buenas tardes" — the adjective agrees with the
|
||||
noun, so a greeting assembled from "Good " plus a translated word is
|
||||
wrong in one of them. One key per time of day is why this passes. */
|
||||
const afternoon = { ...facts, today: new Date('2026-10-07T14:00:00') };
|
||||
const pm = dyn.buildIntro('admin.controlCenter', afternoon, '', i18n.getFixedT('es'));
|
||||
return { pass: pm.title === 'Buenas tardes', detail: pm.title };
|
||||
})().pass);
|
||||
|
||||
record('the assistant description counts in Spanish', (() => {
|
||||
const ok = esIntro.description.includes('24 solicitudes')
|
||||
&& esIntro.description.includes('8 vacantes');
|
||||
return { pass: ok, detail: esIntro.description };
|
||||
})().pass);
|
||||
|
||||
record('a singular count does not take the plural form', (() => {
|
||||
const one = { ...facts, total: 1, postings: new Array(1) };
|
||||
const d = dyn.buildIntro('admin.controlCenter', one, '', i18n.getFixedT('es')).description;
|
||||
return { pass: d.includes('1 solicitud ') || d.includes('1 solicitud'), detail: d };
|
||||
})().pass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user