Updates
Rolling release / release (push) Successful in 9m26s

This commit is contained in:
2026-09-11 21:21:26 -04:00
parent b39a60132a
commit 9e76928045
10 changed files with 315 additions and 63 deletions
+70 -2
View File
@@ -24,9 +24,12 @@ function harness() {
show() { this.visible = true; }
destroy() { this.destroyed = true; }
set_position() {}
set_width() {}
set_height() {}
grab_key_focus() {}
set_style() {}
add_style_class_name() {}
get_first_child() { return this.children[0] || null; }
}
const context = vm.createContext({
Extension: class {},
@@ -34,9 +37,10 @@ function harness() {
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() {} } },
Main: { layoutManager: { addChrome() {}, primaryMonitor: { x: 0, y: 0, width: 1920, height: 1080 } } },
GLib: {
PRIORITY_DEFAULT: 0, SOURCE_REMOVE: false,
PRIORITY_DEFAULT: 0, PRIORITY_DEFAULT_IDLE: 200, SOURCE_REMOVE: false,
idle_add(_priority, callback) { callback(); return 1; },
timeout_add(_priority, _delay, callback) { const id = timers.size + 1; timers.set(id, callback); return id; },
Source: { remove(id) { timers.delete(id); } },
},
@@ -52,6 +56,7 @@ test('overlay constructs and destroys with pending halo animation', () => {
const overlay = new ArcOverlay();
assert.equal(overlay.header.children[1].x_expand, true);
overlay.attach();
overlay.show(true);
overlay.showHalo();
overlay.showHalo();
assert.equal(timers.size, 1);
@@ -89,6 +94,18 @@ test('final reply is shown after tool result rows', () => {
assert.match(overlay.transcript.children[2].text, /Your computer is ready/);
});
test('tokens after a tool result start a new spoken Jarvis row', () => {
const { ArcOverlay } = harness();
const overlay = new ArcOverlay();
overlay.addToolCall('{"name":"capability_status"}');
overlay.addToolResult('{"name":"capability_status"}');
overlay.token('Your computer is ready.');
overlay.finalizeReply('Your computer is ready.');
assert.equal(overlay.transcript.children.length, 3);
assert.match(overlay.transcript.children[2].text, /Your computer is ready/);
assert.doesNotMatch(overlay.transcript.children[2].style_class, /jarvis-row-tool/);
});
test('addRow and token coerce objects to readable text', () => {
const { ArcOverlay } = harness();
const overlay = new ArcOverlay();
@@ -130,4 +147,55 @@ test('D-Bus confirmation signal does not call missing Interface.emit', () => {
const dbusSource = readFileSync(new URL('../daemon/dbus-service.js', import.meta.url), 'utf8');
assert.match(dbusSource, /ConfirmationRequired\(tool, args, pattern\) \{ return \[String\(tool\)/);
assert.doesNotMatch(dbusSource, /ConfirmationRequired\(tool, args, pattern\) \{ this\.emit/);
assert.match(dbusSource, /ContextReset\(\) \{ return; \}/);
assert.match(dbusSource, /Confirm: \{ inSignature: 'sss'/);
});
test('minimize stays closed while Jarvis speaks, then restore shows the transcript', () => {
const { ArcOverlay } = harness();
const overlay = new ArcOverlay();
overlay.attach();
overlay.show(true);
overlay.minimize();
overlay.setState('SPEAKING');
overlay.finalizeReply('I am still talking in the background.');
assert.equal(overlay.root.visible, false);
overlay.show(true);
assert.equal(overlay.root.visible, true);
assert.match(overlay.transcript.children[0].text, /still talking/);
});
test('ConfirmationRequired chips answer Confirm with job and tool ids', () => {
const { ArcOverlay } = harness();
const overlay = new ArcOverlay();
const answers = [];
overlay.onConfirm = (...args) => answers.push(args);
overlay.offerConfirm('write_file', JSON.stringify({ jobId: 'job-1', toolCallId: 'call-9', args: { path: '~/notes' } }), 'destructive');
assert.equal(overlay.confirm.visible, true);
overlay._answerConfirm('allow');
assert.equal(overlay.confirm.visible, false);
assert.deepEqual(answers, [['job-1', 'call-9', 'allow']]);
});
test('errors and reset failures are one-line notices, not chat rows', () => {
const { ArcOverlay } = harness();
const overlay = new ArcOverlay();
overlay.setNotice('ResetContext failed:\nTypeError: Cannot read properties of undefined (reading \'apply\')');
assert.equal(overlay.transcript.children.length, 0);
assert.equal(overlay.notice.visible, true);
assert.doesNotMatch(overlay.notice.text, /\n/);
overlay.clear();
overlay.setNotice('New conversation');
assert.equal(overlay.notice.text, 'New conversation');
});
test('prefs bind every GSettings schema key', () => {
const prefs = readFileSync(new URL('../apps/gnome-extension/[email protected]/prefs.js', import.meta.url), 'utf8');
const schema = readFileSync(new URL('../apps/gnome-extension/[email protected]/schemas/org.gnome.shell.extensions.jarvis.gschema.xml', import.meta.url), 'utf8');
const keys = [...schema.matchAll(/<key name="([^"]+)"/g)].map((match) => match[1]);
assert.ok(keys.length >= 14);
for (const key of keys) assert.match(prefs, new RegExp(`['"]${key}['"]`));
assert.match(prefs, /wakePhrase/);
assert.match(prefs, /ttsEnabled/);
assert.match(prefs, /modelProfile/);
});