fix(career): passport review polish — a11y semantics + seen-state guard (#937)

CodeRabbit follow-up on #936 (the one Major — overlay outside the click
root — was verified false: the host mounts every screen.html root inside
#plugin-career, ✕-close confirmed working live):

- Tabs: aria-selected/aria-controls + role=tabpanel/aria-labelledby.
- Book overlay: role=dialog + aria-modal + aria-label; focus moves to
  the close button on open and returns to the opener on close.
- seenBadges(): guard non-object JSON so a corrupt stored value cannot
  throw on every passport refresh (covered by a new corruption test).
- Fresh-session suppression test (badge seen → no re-notification).
- Stylelint declaration-empty-line-before nit in .pp-stamp.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Byron Gamatos
2026-07-13 12:14:24 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent 7ffa6e2c51
commit 99b974a5a1
5 changed files with 49 additions and 9 deletions
+20 -2
View File
@@ -6,8 +6,8 @@ const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
function load() {
const store = {};
function load(seed) {
const store = Object.assign({}, seed);
const window = {
console,
localStorage: {
@@ -80,4 +80,22 @@ test('detectNewBadges notifies once per badge, never after it is seen', () => {
t.markBadgeSeen('guitar', 'blues');
// JSON-compare: vm objects carry a foreign Object prototype.
assert.equal(JSON.stringify(t.seenBadges()), '{"guitar/blues":1}');
// Fresh session (new vm, empty notify cache) with the badge already seen:
// detection must stay silent.
const w2 = load({ 'feedBack-career-badges-seen': '{"guitar/blues":1}' });
w2.__careerPassportTest.detectNewBadges(view);
assert.equal(w2.notifications.length, 0);
});
test('seenBadges tolerates corrupt stored values', () => {
for (const bad of ['null', '[1,2]', '"x"', '{{{']) {
const w = load({ 'feedBack-career-badges-seen': bad });
const t = w.__careerPassportTest;
assert.equal(JSON.stringify(t.seenBadges()), '{}', `stored ${bad}`);
// And detection still works on top of the recovered empty state.
t.detectNewBadges({ instruments: { guitar: { passports: [
{ genre_key: 'blues', genre: 'Blues', badge: 'earned' }] } } });
assert.equal(w.notifications.length, 1, `stored ${bad}`);
}
});