fix: all tabs inside the page container + compact RAM row (v3.6.46)

The max-w-7xl mx-auto page container closed before #tab-scp81, so SCP81
and Cards rendered directly under <body> and stretched to the whole window
while the other five tabs were capped at 1280 px.  The closing </div> moved
after #tab-cards (before the modals); a structural test walks the markup
and asserts every tab-content has the container as an ancestor (verified to
fail on the old nesting with 'tab-scp81 must be inside the max-w-7xl page
container').

The SCP80/RAM form's Card preset and Operation selects now share one
flex gap-2 items-end row with Execute (guarded by a markup test).

sw cache simple-v319; tests: 531 Python / 683 Node.
This commit is contained in:
2026-09-29 23:38:47 +03:00
parent cf9c9b7bab
commit 55adeb0fbd
5 changed files with 49 additions and 12 deletions
+38
View File
@@ -448,3 +448,41 @@ test('every data-l10n attribute resolves in LANG_RU after HTML decoding', () =>
const missing = [...new Set([...attrs, ...titles].filter(k => !(k in dict)))];
assert.deepStrictEqual(missing, [], 'data-l10n keys with no LANG_RU entry:\n' + missing.join('\n'));
});
test('every tab-content lives inside the max-w-7xl page container', () => {
// SCP81 and Cards used to sit outside the container (its closing </div>
// came before them), so they stretched to the whole window while the
// other tabs were capped - v3.6.46.
const markup = htmlOnly(html);
const re = /<(\/?)([a-zA-Z][a-zA-Z0-9-]*)\b([^>]*?)>/g;
let m;
let depth = 0;
let containerDepth = null;
const seen = new Set();
while ((m = re.exec(markup))) {
const closing = m[1] === '/';
const tag = m[2].toLowerCase();
const attrs = m[3] || '';
const selfClosing = attrs.trim().endsWith('/');
if (VOID_TAGS.has(tag) || selfClosing) continue;
if (closing) {
if (containerDepth !== null && depth === containerDepth) containerDepth = null;
depth--;
continue;
}
if (tag === 'div' && /class="[^"]*max-w-7xl/.test(attrs)) containerDepth = depth + 1;
if (tag === 'div' && /class="[^"]*tab-content/.test(attrs)) {
const id = (/id="([^"]+)"/.exec(attrs) || [])[1];
assert.notStrictEqual(containerDepth, null,
id + ' must be inside the max-w-7xl page container');
seen.add(id);
}
depth++;
}
assert.strictEqual(seen.size, 7, 'expected all seven tabs, saw ' + seen.size);
});
test('the RAM form shows Card preset and Operation in one row', () => {
const row = /<div class="mb-3 flex gap-2 items-end">\s*<div class="flex-1">\s*<label[^>]*data-l10n="Card preset"[\s\S]*?id="ram-card-sel"[\s\S]*?data-l10n="Operation"[\s\S]*?id="ram-op"[\s\S]*?data-l10n="Execute"/.exec(html);
assert.ok(row, 'Card preset + Operation + Execute must share one row');
});