test/home-mate-settings.test.js
Rama main · réplica de lectura del repositorio original.
var test = require('node:test');
var assert = require('node:assert/strict');
var child = require('node:child_process');
var path = require('node:path');
var pw = require('playwright');
test('production Mate settings preserve drafts and acknowledge prompt/model saves', async function (t) {
var port = 35000 + process.pid % 10000;
var server = child.spawn(process.execPath, [path.join(__dirname,'fixtures/session-folders-dom/serve.js'), String(port)], {stdio:['ignore','pipe','pipe']});
var browser; t.after(async function () { if (browser) await browser.close(); server.kill(); });
await new Promise(function (resolve, reject) { server.stdout.once('data',resolve); server.once('error',reject); });
browser = await pw.chromium.launch();
var page = await browser.newPage({viewport:{width:1280,height:900}, reducedMotion:'reduce'});
var errors = []; page.on('pageerror',function (e) { errors.push(e.stack || e.message); });
await page.goto('http://127.0.0.1:' + port + '/mate-settings.html');
await page.waitForFunction(function () { return window.__ready; }).catch(function (error) {
throw new Error(error.message + "\nPage errors: " + errors.join("\n"));
});
var dialog = page.getByRole('dialog', {name:'Mate settings'});
var prompt = page.getByRole('textbox', {name:'Mate prompt'});
assert.equal(await prompt.count(), 0);
await page.waitForFunction(function () { return Array.from(document.querySelectorAll('.mate-settings-profile-avatar img')).every(function (img) { return img.complete && img.naturalWidth > 0; }); });
assert.equal(await page.getByRole('button',{name:'Model: Luna',exact:true}).count(), 0);
assert.equal(await page.getByRole('button',{name:'Upload image',exact:true}).count(), 0);
await t.test('identity is readable until Edit, with cancel, retained drafts and acknowledged save', async function () {
assert.equal(await page.getByRole('textbox',{name:'Name',exact:true}).count(),0);
assert.equal(await page.getByText('A thoughtful partner for product design.',{exact:true}).isVisible(),true);
await page.getByRole('button',{name:'Edit Mate identity'}).click();
await page.getByRole('textbox',{name:'Name',exact:true}).fill('Ari revised');
await page.getByRole('textbox',{name:'Introduction',exact:true}).fill('A concise design partner.');
await dialog.getByRole('button',{name:'Prompt',exact:true}).click();
await dialog.getByRole('button',{name:'General',exact:true}).click();
assert.equal(await page.getByRole('textbox',{name:'Name',exact:true}).inputValue(),'Ari revised');
await dialog.getByRole('button',{name:'Close Mate settings'}).click();
await page.locator('#confirm-cancel').click();
await page.evaluate(function () { window.__fail = 'Identity save failed.'; });
await page.getByRole('button',{name:'Save identity'}).click();
await page.getByRole('alert').filter({hasText:'Identity save failed.'}).waitFor();
assert.equal(await page.getByRole('textbox',{name:'Name',exact:true}).inputValue(),'Ari revised');
await page.evaluate(function () { window.__fail = ''; });
await page.getByRole('button',{name:'Cancel',exact:true}).click();
assert.equal(await page.getByRole('textbox',{name:'Name',exact:true}).count(),0);
await page.getByRole('button',{name:'Edit Mate identity'}).click();
await page.getByRole('textbox',{name:'Introduction',exact:true}).fill('A concise design partner.');
await page.getByRole('button',{name:'Save identity'}).click();
await page.getByText('A concise design partner.',{exact:true}).waitFor();
assert.equal(await page.getByRole('textbox',{name:'Name',exact:true}).count(),0);
var saved = await page.evaluate(function () { return window.__sent.filter(function (m) { return m.type === 'mate_update'; }).pop(); });
assert.equal(saved.updates.profile.avatarStyle,'bottts');
assert.equal(saved.updates.profile.displayName,'Ari');
assert.equal(saved.updates.bio,'A concise design partner.');
});
await dialog.getByRole('button',{name:'Prompt',exact:true}).click();
await prompt.waitFor();
var original = await prompt.inputValue();
await t.test('Prompt shows actual instructions and retains a draft across pages', async function () {
assert.match(original, /thoughtful design partner/);
await prompt.fill(original + '\nAsk before changing direction.');
await dialog.getByRole('button',{name:'Model',exact:true}).click();
await page.getByRole('button',{name:'Model: Luna',exact:true}).waitFor();
await dialog.getByRole('button',{name:'Prompt',exact:true}).click();
assert.match(await prompt.inputValue(), /Ask before changing direction/);
await dialog.getByRole('button',{name:'Close Mate settings'}).click();
assert.equal(await page.locator('#confirm-modal').isVisible(), true);
await page.locator('#confirm-cancel').click();
assert.equal(await dialog.isVisible(), true);
await page.getByRole('button',{name:'Save prompt',exact:true}).click();
await page.getByText('Prompt saved. New conversations use these instructions.').waitFor();
assert.equal(await page.getByRole('button',{name:'Save prompt',exact:true}).count(), 0);
});
await t.test('prompt save failure, conflict, offline and stale response retain intent', async function () {
await prompt.fill(original + '\nA revised draft remains visible until acknowledged.');
await page.evaluate(function () { window.__fail = 'Disk is read-only.'; });
await page.getByRole('button',{name:'Save prompt',exact:true}).click();
await page.getByRole('alert').filter({hasText:'Disk is read-only.'}).waitFor();
assert.match(await prompt.inputValue(), /revised draft/);
await page.evaluate(function () { window.__fail = 'Changed elsewhere.'; });
await page.getByRole('button',{name:'Save prompt',exact:true}).click();
await page.getByRole('button',{name:'Reload saved prompt'}).waitFor();
assert.match(await prompt.inputValue(), /revised draft/);
await page.evaluate(function () { window.__fail = ''; });
await page.getByRole('button',{name:'Reload saved prompt'}).click();
await page.waitForFunction(function () { return !document.querySelector('.mate-instructions-input').disabled; });
assert.match(await prompt.inputValue(), /Ask before changing direction/);
await page.evaluate(function () { window.__instructions({mateId:'mate_preview',requestId:'stale',ok:true,content:'wrong'}); window.__offline(true); });
await prompt.fill(original + '\nOffline changes remain in the editor.');
await page.getByRole('button',{name:'Save prompt',exact:true}).click();
await page.getByText('Clay is offline. Reconnect and try again.',{exact:true}).waitFor();
assert.match(await prompt.inputValue(), /Offline changes/);
await page.evaluate(function () { window.__offline(false); });
await page.getByRole('button',{name:'Cancel',exact:true}).click();
});
await t.test('compact model draft, keyboard picker, failure retry and acknowledged save', async function () {
await dialog.getByRole('button',{name:'Model',exact:true}).click();
await page.getByRole('button',{name:'Provider: Codex',exact:true}).click();
await page.keyboard.press('Escape');
assert.equal(await dialog.isVisible(), true);
await page.getByRole('button',{name:'Provider: Codex',exact:true}).click();
await page.getByRole('option',{name:'Claude Code',exact:true}).click();
await page.getByRole('button',{name:'Model: Choose a model',exact:true}).waitFor();
await page.getByRole('button',{name:'Model: Choose a model',exact:true}).click();
await page.getByRole('option',{name:'Sol',exact:true}).click();
assert.equal(await page.evaluate(function () { return window.__sent.filter(function (m) { return m.type === 'home_mate_model_set'; }).length; }), 0);
await dialog.getByRole('button',{name:'Prompt',exact:true}).click();
await dialog.getByRole('button',{name:'Model',exact:true}).click();
assert.equal(await page.locator('#mate-settings-model').inputValue(), 'sol');
await page.evaluate(function () { window.__fail = 'Model save failed.'; });
await page.getByRole('button',{name:'Save model',exact:true}).click();
await page.getByText('Model save failed.',{exact:true}).waitFor();
assert.equal(await page.locator('#mate-settings-model').inputValue(), 'sol');
await page.evaluate(function () { window.__fail = ''; });
await page.getByRole('button',{name:'Save model',exact:true}).click();
await page.getByText('Model saved.',{exact:true}).waitFor();
var saved = await page.evaluate(function () { return window.__sent.filter(function (m) { return m.type === 'home_mate_model_set'; }).pop(); });
assert.equal(saved.vendor,'claude'); assert.equal(saved.model,'sol'); assert.equal(saved.sessionId,1);
});
await t.test('appearance controls and focus return remain available', async function () {
await dialog.getByRole('button',{name:'General',exact:true}).click();
await page.getByRole('button',{name:'Change Mate avatar'}).click();
assert.equal(await page.getByRole('button',{name:'Upload image',exact:true}).isVisible(), true);
assert.equal(await page.locator('.home-mate-avatar-choice').count(),96);
await page.getByRole('button',{name:'Teal Diamond avatar',exact:true}).click();
await page.getByRole('button',{name:'Save avatar',exact:true}).click();
await page.getByText('Avatar saved.',{exact:true}).waitFor();
await page.getByRole('button',{name:'Cancel',exact:true}).click();
assert.equal(await page.getByRole('button',{name:'Upload image',exact:true}).count(),0);
await dialog.getByRole('button',{name:'Close Mate settings'}).click();
assert.equal(await page.getByRole('button',{name:'Actions for Ari'}).evaluate(function (el) { return el === document.activeElement; }), true);
await page.getByRole('button',{name:'Actions for Ari'}).click();
await page.getByRole('menuitem',{name:'Mate settings'}).click();
await dialog.getByRole('button',{name:'Model',exact:true}).click();
await page.getByRole('button',{name:'Model: Sol',exact:true}).waitFor();
});
await t.test('light/dark and narrow settings fit, with reachable model controls', async function () {
for (var theme of ['light','dark']) {
for (var width of [1280,390]) {
await page.setViewportSize({width:width,height:width === 1280 ? 720 : 900});
await page.evaluate(function (theme) { document.documentElement.classList.toggle('light-theme',theme === 'light'); }, theme);
for (var section of ['General','Model','Prompt']) {
await dialog.getByRole('button',{name:section,exact:true}).click();
var fits = await dialog.evaluate(function (el) { var r=el.getBoundingClientRect(); return r.left >= 0 && r.right <= innerWidth + 1 && r.bottom <= innerHeight + 1 && el.scrollWidth <= el.clientWidth + 1; });
assert.equal(fits,true);
if (section === 'Prompt') {
var geometry = await prompt.evaluate(function (el) {
var r = el.getBoundingClientRect(); var body = el.closest('.home-mate-settings-body').getBoundingClientRect();
return {height:r.height, available:body.height, bottomGap:body.bottom-r.bottom};
});
assert.ok(geometry.height > geometry.available * .7);
assert.ok(geometry.bottomGap <= 28);
}
if (section === 'Model') {
if (width === 1280) assert.equal(await dialog.locator('.home-mate-settings-content').evaluate(function (el) { return el.scrollHeight <= el.clientHeight + 1; }),true);
await page.getByRole('button',{name:/^Model: /}).scrollIntoViewIfNeeded();
assert.equal(await page.getByRole('button',{name:/^Model: /}).isVisible(), true);
await dialog.locator('.home-mate-settings-content').evaluate(function (el) { el.scrollTop = 0; });
}
await page.screenshot({path:'/tmp/mate-settings-' + section.toLowerCase() + '-' + theme + '-' + width + '.png'});
}
}
}
});
await t.test('permanent deletion requires exact typed acknowledgement and cancels safely', async function () {
await dialog.getByRole('button',{name:'General',exact:true}).click();
var remove = page.getByRole('button',{name:'Delete Mate',exact:true});
await remove.click();
var confirmation = page.getByRole('alertdialog');
var input = page.getByRole('textbox',{name:'Type understand to confirm permanent deletion'});
var confirm = page.getByRole('button',{name:'Delete permanently',exact:true});
assert.equal(await confirm.isDisabled(),true);
assert.match(await confirmation.innerText(),/cannot be undone/);
await input.fill('UNDERSTAND'); assert.equal(await confirm.isDisabled(),true);
await input.fill('understand '); assert.equal(await confirm.isDisabled(),true);
await page.keyboard.press('Escape');
assert.equal(await confirmation.count(),0);
assert.equal(await remove.evaluate(function (el) { return el === document.activeElement; }),true);
assert.equal(await page.evaluate(function () { return window.__sent.filter(function (m) { return m.type === 'mate_delete'; }).length; }),0);
await remove.click(); await input.fill('understand');
assert.equal(await confirm.isEnabled(),true);
await page.locator('#confirm-cancel').click();
await remove.click(); assert.equal(await input.inputValue(),''); assert.equal(await confirm.isDisabled(),true);
await input.fill('understand');
await page.screenshot({path:'/tmp/mate-delete-confirm-narrow-dark.png'});
await confirm.click();
assert.equal(await page.evaluate(function () { return window.__sent.filter(function (m) { return m.type === 'mate_delete'; }).length; }),1);
assert.equal(await dialog.count(),0);
assert.equal(await page.locator('.mate-delete-challenge').count(),0);
assert.equal(await page.locator('#confirm-ok').isDisabled(),false);
});
assert.deepEqual(errors,[]);
});