mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
test(mobile): the gate checks the File Viewer button's phone default again (#809)
Until #809 a display:none rule in mobile.css and its KNOWN_PHONE_HIDDEN entry kept the File Viewer button off phones, and the gate checked both. With that rule gone, the handheld default in getDefaultSettings() and the html.mobile-init startup rule are the whole lock, and only the browser test saw them, which npm test does not run. The policy test now loads the real getDefaultSettings() on a handheld and on a desktop, checks that every reader of the setting goes through the device default, and that mobile.css keeps the startup hide. Docs: architecture-invariants, src/web/CLAUDE.md and the wiki say the button is off on a phone until it is switched on there, and that a window narrower than 600px on a device that is not a handheld now shows it. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
1 parent
b587ac71f4
commit
d192eb6825
8 files changed
+86
-11
No files matched your search
@@ -20,6 +20,7 @@ import { describe, it, expect } from 'vitest';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { join } from 'node:path';
|
||||
import vm from 'node:vm';
|
||||
import postcss from 'postcss';
|
||||
|
||||
const HERE = fileURLToPath(new URL('.', import.meta.url));
|
||||
@@ -30,7 +31,9 @@ const PUBLIC = join(HERE, '../src/web/public');
|
||||
const PHONE_WIDTH = 393;
|
||||
|
||||
// Issue #759: the maintainer approved File Viewer on phones when explicitly enabled.
|
||||
// Its per-device setting still defaults off; the browser test covers that default.
|
||||
// Its per-device setting still defaults off on a handheld. With the blanket phone
|
||||
// hide gone, that default and the startup hide are the whole lock, so both are
|
||||
// pinned below ("File Viewer on a phone: off until it is switched on").
|
||||
const MOBILE_VISIBLE_ALLOWLIST = new Set<string>(['btn-file-viewer']);
|
||||
|
||||
// Buttons we expect to STAY hidden on phones — an explicit lock so a future edit
|
||||
@@ -152,6 +155,74 @@ describe('Mobile header button policy (static guard)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Until #759 the gate kept the File Viewer button off phones with a blanket
|
||||
// display:none rule and its KNOWN_PHONE_HIDDEN entry. That rule is gone, so what
|
||||
// keeps the button off a phone nobody switched it on for is now two things, and
|
||||
// the real-page test that exercises them (test/mobile/file-browser.test.ts) is
|
||||
// not part of `npm test`:
|
||||
// 1. the handheld branch of getDefaultSettings() says false, and the header
|
||||
// reads the setting through that default (a bare `?? true` would show it);
|
||||
// 2. `html.mobile-init` hides the button until the settings have been applied,
|
||||
// or it would flash on every phone page load (the template ships it visible).
|
||||
describe('File Viewer on a phone: off until it is switched on (#759)', () => {
|
||||
const settingsUi = readFileSync(join(PUBLIC, 'settings-ui.js'), 'utf-8');
|
||||
|
||||
/** The real getDefaultSettings() of settings-ui.js on one kind of device. */
|
||||
function defaultsOn(handheld: boolean): Record<string, unknown> {
|
||||
const CodemanApp = function CodemanApp(this: unknown) {};
|
||||
const context = vm.createContext({
|
||||
CodemanApp,
|
||||
VoiceInput: {},
|
||||
localStorage: { getItem: () => null, setItem: () => {} },
|
||||
document: { getElementById: () => null },
|
||||
console,
|
||||
MobileDetection: {
|
||||
isHandheldDevice: () => handheld,
|
||||
isTouchDevice: () => handheld,
|
||||
getDeviceType: () => (handheld ? 'mobile' : 'desktop'),
|
||||
},
|
||||
window: { matchMedia: () => ({ matches: false }) },
|
||||
});
|
||||
vm.runInContext(settingsUi, context, { filename: 'settings-ui.js' });
|
||||
const app = Object.create(CodemanApp.prototype) as { getDefaultSettings(): Record<string, unknown> };
|
||||
return app.getDefaultSettings();
|
||||
}
|
||||
|
||||
it('a handheld defaults the button off', () => {
|
||||
expect(defaultsOn(true).showFileViewerButton).toBe(false);
|
||||
});
|
||||
|
||||
it('a desktop leaves the key unset, which every reader resolves to on', () => {
|
||||
expect(defaultsOn(false)).not.toHaveProperty('showFileViewerButton');
|
||||
});
|
||||
|
||||
it('the header and the App Settings chip both read the setting through the device default', () => {
|
||||
const read = 'settings.showFileViewerButton ?? defaults.showFileViewerButton ?? true;';
|
||||
expect(settingsUi).toContain(`const showFileViewerButton = ${read}`);
|
||||
expect(settingsUi).toContain(`document.getElementById('appSettingsShowFileViewerButton').checked = ${read}`);
|
||||
// Every reader goes through it: one that skipped the device default would resolve to on.
|
||||
const reads = [...settingsUi.matchAll(/\bsettings\.showFileViewerButton\b(.{0,40})/g)];
|
||||
expect(reads.length).toBeGreaterThanOrEqual(2);
|
||||
for (const [line, rest] of reads) {
|
||||
expect(rest.startsWith(' ?? defaults.showFileViewerButton'), `"${line}" skips the device default`).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('html.mobile-init hides the button until the settings have been applied', () => {
|
||||
const css = readFileSync(join(PUBLIC, 'mobile.css'), 'utf-8');
|
||||
let hidesAtStartup = false;
|
||||
postcss.parse(css).each((node) => {
|
||||
// Top level only: the rule must apply at every width mobile.css is loaded for.
|
||||
if (node.type !== 'rule') return;
|
||||
if (!node.selectors.includes('html.mobile-init .btn-file-viewer')) return;
|
||||
node.walkDecls('display', (decl) => {
|
||||
if (decl.value.trim() === 'none' && decl.important) hidesAtStartup = true;
|
||||
});
|
||||
});
|
||||
expect(hidesAtStartup, 'mobile.css must keep .btn-file-viewer in its html.mobile-init display:none rule').toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
// The flip side of the policy above: the ONE header control phones do keep has
|
||||
// to be pressable. The brand "C" is the way back to the home screen and was a
|
||||
// 0.85rem inline span — roughly a 12x13px target, well under the 44px minimum.
|
||||
|
||||
Reference in new issue
Block a user