Files
gnome-jarvis/test/gnome-extension.test.js
T
snxraven a78708f8ff
Rolling release / release (push) Failing after 1m24s
Updates
2026-09-11 19:36:12 -04:00

115 lines
4.7 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import vm from 'node:vm';
const source = readFileSync(new URL('../apps/gnome-extension/[email protected]/extension.js', import.meta.url), 'utf8');
function harness() {
const timers = new Map();
class Actor {
constructor(props = {}) {
if ('hexpand' in props) throw new Error('No property hexpand on StWidget');
Object.assign(this, props);
this.children = [];
this.visible = props.visible !== false;
this.clutter_text = { connect() {}, ellipsize: null };
}
add_child(child) { this.children.push(child); }
destroy_all_children() { this.children = []; }
get_n_children() { return this.children.length; }
contains() { return false; }
get_last_child() { return this.children[this.children.length - 1] || null; }
connect() { return 1; }
hide() { this.visible = false; }
show() { this.visible = true; }
destroy() { this.destroyed = true; }
set_position() {}
grab_key_focus() {}
set_style() {}
add_style_class_name() {}
}
const context = vm.createContext({
Extension: class {},
global: { stage: { get_key_focus() { return null; } } },
St: { BoxLayout: Actor, Label: Actor, Widget: Actor, Button: Actor, Entry: Actor, ScrollView: Actor, PolicyType: { NEVER: 0, AUTOMATIC: 1 } },
Clutter: { ActorAlign: { CENTER: 0, START: 1 }, EVENT_STOP: 1, EVENT_PROPAGATE: 0 },
Pango: { WrapMode: { WORD_CHAR: 2 }, EllipsizeMode: { NONE: 0, END: 3 } },
Main: { layoutManager: { addChrome() {} } },
GLib: {
PRIORITY_DEFAULT: 0, SOURCE_REMOVE: false,
timeout_add(_priority, _delay, callback) { const id = timers.size + 1; timers.set(id, callback); return id; },
Source: { remove(id) { timers.delete(id); } },
},
log() {},
});
vm.runInContext(source.replace(/^import .*;\n/gm, '').replace('export default class', 'class') +
'\nglobalThis.classes = { ArcOverlay, JarvisExtension, JarvisProxy };', context);
return { ...context.classes, timers };
}
test('overlay constructs and destroys with pending halo animation', () => {
const { ArcOverlay, timers } = harness();
const overlay = new ArcOverlay();
assert.equal(overlay.header.children[1].x_expand, true);
overlay.attach();
overlay.showHalo();
overlay.showHalo();
assert.equal(timers.size, 1);
overlay.destroy();
assert.equal(timers.size, 0);
});
test('empty chrome widgets start hidden', () => {
const { ArcOverlay } = harness();
const overlay = new ArcOverlay();
assert.equal(overlay.job.visible, false);
assert.equal(overlay.target.visible, false);
assert.equal(overlay.cursor.visible, false);
assert.equal(overlay.wave.visible, false);
});
test('Reply finalizes a streaming row instead of duplicating [object Object]', () => {
const { ArcOverlay } = harness();
const overlay = new ArcOverlay();
overlay.token('Hello! I am ready.');
overlay.finalizeReply('[object Object]');
overlay.finalizeReply({ text: 'ignored duplicate' });
assert.equal(overlay.transcript.children.length, 1);
assert.match(overlay.transcript.children[0].text, /Hello! I am ready/);
assert.doesNotMatch(overlay.transcript.children[0].text, /object Object/);
});
test('addRow and token coerce objects to readable text', () => {
const { ArcOverlay } = harness();
const overlay = new ArcOverlay();
overlay.addRow('U', { text: 'Hi there' });
overlay.token({ text: 'Hello from Jarvis' });
assert.equal(overlay.transcript.children.length, 2);
assert.match(overlay.transcript.children[0].text, /Hi there/);
assert.match(overlay.transcript.children[1].text, /Hello from Jarvis/);
assert.doesNotMatch(overlay.transcript.children.map((row) => row.text).join('\n'), /object Object/);
});
for (const fails of [false, true]) {
test('daemon connection finishing after disable is ignored: ' + (fails ? 'failure' : 'success'), async () => {
const { JarvisExtension } = harness();
const extension = new JarvisExtension();
let finish;
extension.proxy = { connect: () => new Promise((resolve, reject) => { finish = fails ? reject : resolve; }) };
const pending = extension._connectDaemon();
extension.proxy = null;
extension.overlay = null;
finish(fails ? new Error('Disconnected') : undefined);
await pending;
});
}
test('GNOME GI imports expose default namespaces and panel has a real menu', () => {
assert.match(source, /import Shell from 'gi:\/\/Shell'/);
assert.match(source, /import Meta from 'gi:\/\/Meta'/);
assert.match(source, /import Pango from 'gi:\/\/Pango'/);
assert.match(source, /new PanelMenu.Button\(0.0, 'Jarvis QVAC', false\)/);
assert.match(source, /PushToTalk/);
assert.match(source, /finalizeReply/);
});