Execute prefs.js in a test
prefs.js had never run. It lives in its own process rather than in the compositor, so enabling the extension in a nested shell does not reach it, and every other check in this repo was blind to it -- it would first have executed when someone opened the preferences and found it broken. It builds under real Adw, and the hook-status line correctly reports all nine registered events. The shell's ExtensionPreferences base class and its gettext are stubbed rather than loaded: both resolve an extension by walking the caller's URL up to a registered UUID, which needs the whole extension manager for no gain here. The stub keeps the test on this extension's own code. The switch count is asserted against the schema's key count, so a setting added without a row to change it fails the test.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/gjs -m
|
||||
// Builds the preferences window for real, under real Adw.
|
||||
//
|
||||
// prefs.js runs in its own process, not in the compositor, so a nested-shell
|
||||
// check cannot reach it -- without this it is the one file in the extension
|
||||
// that never executes until a user opens it and finds it broken.
|
||||
//
|
||||
// The shell's own base class and gettext are stubbed rather than loaded: they
|
||||
// resolve an extension by walking the caller's URL up to a registered UUID,
|
||||
// which needs the whole extension manager. Stubbing them keeps the test about
|
||||
// this extension's code, which is the part that can be wrong.
|
||||
//
|
||||
// Run: gjs -m tests/test-prefs.js
|
||||
|
||||
import Gio from 'gi://Gio';
|
||||
import GLib from 'gi://GLib';
|
||||
import Adw from 'gi://Adw';
|
||||
|
||||
Adw.init();
|
||||
|
||||
const HERE = GLib.path_get_dirname(
|
||||
GLib.filename_from_uri(import.meta.url)[0]);
|
||||
const EXT = GLib.path_get_dirname(HERE);
|
||||
const SHELL_PREFS = 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
||||
|
||||
const tmp = GLib.dir_make_tmp('ccs-prefs-XXXXXX');
|
||||
const [, source] = GLib.file_get_contents(`${EXT}/prefs.js`);
|
||||
GLib.file_set_contents(`${tmp}/stub.js`,
|
||||
'export class ExtensionPreferences {}\nexport const gettext = s => s;\n');
|
||||
GLib.file_set_contents(`${tmp}/prefs.js`,
|
||||
new TextDecoder().decode(source).replace(SHELL_PREFS, './stub.js'));
|
||||
|
||||
const schemas = Gio.SettingsSchemaSource.new_from_directory(
|
||||
`${EXT}/schemas`, Gio.SettingsSchemaSource.get_default(), false);
|
||||
|
||||
const { default: Prefs } = await import(`file://${tmp}/prefs.js`);
|
||||
const prefs = new Prefs();
|
||||
Object.defineProperty(prefs, 'path', { value: EXT }); // a getter upstream
|
||||
prefs.getSettings = () => new Gio.Settings({
|
||||
settings_schema: schemas.lookup(
|
||||
'org.gnome.shell.extensions.claude-code-status', true),
|
||||
});
|
||||
|
||||
const window = new Adw.PreferencesWindow();
|
||||
prefs.fillPreferencesWindow(window);
|
||||
|
||||
const rows = [];
|
||||
const walk = widget => {
|
||||
for (let c = widget.get_first_child?.(); c; c = c.get_next_sibling()) {
|
||||
const type = c.constructor.$gtype.name;
|
||||
if (type === 'AdwSwitchRow' || type === 'AdwActionRow')
|
||||
rows.push({ type, title: c.title, subtitle: c.subtitle });
|
||||
walk(c);
|
||||
}
|
||||
};
|
||||
walk(window);
|
||||
|
||||
let failures = 0;
|
||||
function check(name, condition, detail = '') {
|
||||
if (!condition)
|
||||
failures++;
|
||||
print(`${condition ? 'ok ' : 'FAIL'} ${name}${detail ? ` (${detail})` : ''}`);
|
||||
}
|
||||
|
||||
for (const row of rows)
|
||||
print(` ${row.type.replace('Adw', '').padEnd(10)} ${row.title}`);
|
||||
|
||||
// One switch per settings key, so a key added without a row is caught.
|
||||
const keys = schemas.lookup('org.gnome.shell.extensions.claude-code-status', true)
|
||||
.list_keys().length;
|
||||
const switches = rows.filter(r => r.type === 'AdwSwitchRow').length;
|
||||
check('a switch for every settings key', switches === keys, `${switches} of ${keys}`);
|
||||
|
||||
// The hook status line is the reason this page is worth opening at all: a
|
||||
// silent panel looks the same whether nothing runs or nothing is installed.
|
||||
const status = rows.find(r => r.title === 'Status');
|
||||
check('hook status reported', !!status?.subtitle, status?.subtitle);
|
||||
check('status names the events it found',
|
||||
/SessionStart|Not installed/.test(status?.subtitle ?? ''), status?.subtitle);
|
||||
|
||||
const dir = rows.find(r => r.title === 'State directory');
|
||||
check('state directory shown', (dir?.subtitle ?? '').endsWith('claude-code-status'),
|
||||
dir?.subtitle);
|
||||
|
||||
GLib.spawn_command_line_sync(`rm -rf ${tmp}`);
|
||||
print(failures ? `\n${failures} failure(s)` : '\nall passed');
|
||||
imports.system.exit(failures ? 1 : 0);
|
||||
Reference in New Issue
Block a user