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'); var fs=require('node:fs'); test('shared catalog appearance names cover all stable IDs without numbered public labels',async function(){ var source=fs.readFileSync(path.join(__dirname,'../lib/public/modules/mate-avatar-catalog.js'),'utf8'); var module=await import('data:text/javascript;base64,'+Buffer.from(source).toString('base64')); assert.deepEqual(module.MATE_AVATAR_COLLECTIONS.map(function(c){return c.name;}),['Oddballs','Kindred Bots']); var entries=module.MATE_AVATAR_COLLECTIONS.flatMap(function(c){return c.avatars;}); assert.equal(entries.length,96);assert.equal(new Set(entries.map(function(e){return e.name;})).size,96); var packaged=require('../lib/public/mate-avatars/catalog.json'); entries.forEach(function(e){assert.doesNotMatch(e.name,/\d|\b[BC]\b/);assert.equal(packaged.find(function(p){return p.id===e.id;}).name,e.name);}); }); test('real project-strip Mate menu opens the exact avatar picker without navigating conversations',async function(t){ var server=child.spawn(process.execPath,[path.join(__dirname,'fixtures/mate-avatar-catalog-server.cjs'),'0'],{stdio:['ignore','pipe','pipe']}); var browser;t.after(async function(){if(browser)await browser.close();server.kill('SIGTERM');}); var url=await new Promise(function(resolve,reject){server.stdout.once('data',function(data){resolve(String(data).match(/http:\/\/127\.0\.0\.1:\d+/)[0]);});server.once('error',reject);}); browser=await pw.chromium.launch();var page=await browser.newPage({viewport:{width:1280,height:900}}); var errors=[];page.on('pageerror',function(e){errors.push(e.stack||e.message);}); await page.goto(url+'/mate-settings.html?strip=1');await page.waitForFunction(function(){return window.__stripReady;}).catch(function(e){throw new Error(e.message+' '+errors.join('\n'));}); var records=await (await page.request.get(url+'/fixture/mates')).json(); var target=records.find(function(m){return m.name==='Bea';});var ari=records.find(function(m){return m.name==='Ari';});var locked=records.find(function(m){return m.name==='Locked';}); var targetButton=page.locator('#icon-strip-projects [data-mate-id="'+target.id+'"]'); await targetButton.click({button:'right'});await page.getByRole('menuitem',{name:'Change avatar',exact:true}).click(); var dialog=page.getByRole('dialog',{name:'Mate settings'});await dialog.waitFor(); assert.equal(await dialog.locator('.home-mate-settings-header span').first().textContent(),'Bea'); assert.equal(await dialog.locator('.home-mate-avatar-choice').count(),96); assert.equal(await dialog.getByRole('heading',{name:'Oddballs',exact:true}).count(),1); assert.equal(await dialog.getByRole('heading',{name:'Kindred Bots',exact:true}).count(),1); var text=await dialog.locator('.home-mate-avatar-editor').innerText();assert.doesNotMatch(text,/\b[BC]\d{2}\b|Adventurous robots|Expressive robots|Preview:/); var labels=await dialog.locator('.home-mate-avatar-choice').evaluateAll(function(items){return items.map(function(item){return item.getAttribute('aria-label');});}); assert.equal(new Set(labels).size,96);labels.forEach(function(label){assert.doesNotMatch(label,/\b[BC]\d{2}\b/);}); assert.equal(await dialog.locator('.home-mate-avatar-choice span').count(),0); assert.equal(await dialog.locator('.home-mate-avatar-selection img').getAttribute('src'),'/fixture/custom.png'); await page.locator('#mate-avatar-cancel').click();assert.equal(await dialog.count(),0); assert.equal(await targetButton.evaluate(function(e){return e===document.activeElement;}),true); var afterCancel=await (await page.request.get(url+'/fixture/mates')).json();assert.equal(afterCancel.find(function(m){return m.id===target.id;}).profile.avatarCustom,'/fixture/custom.png'); await targetButton.focus();await page.keyboard.press('Shift+F10');assert.equal(await page.getByRole('menuitem',{name:'Change avatar'}).isVisible(),true); await page.keyboard.press('Enter');await page.locator('#mate-avatar-C48').click(); assert.equal(await dialog.locator('.home-mate-avatar-selection p').textContent(),'Preview'); assert.doesNotMatch(await dialog.locator('.home-mate-avatar-editor').innerText(),/\b[BC]\d{2}\b/); await page.locator('#mate-avatar-save').click();await page.getByRole('status').filter({hasText:'Avatar saved.'}).waitFor(); await page.getByRole('button',{name:'Close Mate settings',exact:true}).click();assert.equal(await targetButton.evaluate(function(e){return e===document.activeElement;}),true); var saved=await (await page.request.get(url+'/fixture/mates')).json();assert.equal(saved.find(function(m){return m.id===target.id;}).profile.avatarCatalogId,'C48');assert.deepEqual(saved.find(function(m){return m.id===ari.id;}).profile,ari.profile); assert.deepEqual(await page.evaluate(function(){return {slug:window.store.get('currentSlug'),opened:window.__opened};}),{slug:'fixture-project',opened:[]}); await targetButton.dispatchEvent('keydown',{key:'ContextMenu',bubbles:true});await page.keyboard.press('Escape');assert.equal(await targetButton.evaluate(function(e){return e===document.activeElement;}),true); await page.locator('#icon-strip-projects [data-mate-id="'+locked.id+'"]').click({button:'right'});assert.equal(await page.getByRole('menuitem',{name:'Change avatar'}).count(),0);await page.keyboard.press('Escape'); await page.locator('[data-slug="fixture-project"]').click({button:'right'});assert.equal(await page.getByRole('menuitem',{name:'Set Icon',exact:true}).count(),1);assert.equal(await page.getByRole('menuitem',{name:'Change avatar'}).count(),0);await page.mouse.click(500,700); await page.locator('.icon-strip-brand').click({button:'right'});assert.equal(await page.getByRole('menuitem',{name:'Change avatar'}).count(),0); var denied=await page.evaluate(async function(){var m=await import('/modules/mate-management.js');return m.changeMateAvatar(document.querySelector('.icon-strip-brand'),{id:'foreign-mate'});});assert.equal(denied,false); await page.reload();await page.waitForFunction(function(){return window.__stripReady;}); await page.locator('#icon-strip-projects [data-mate-id="'+target.id+'"]').click({button:'right'});await page.getByRole('menuitem',{name:'Change avatar'}).click();assert.equal(await page.locator('#mate-avatar-C48').getAttribute('aria-pressed'),'true');await page.locator('#mate-avatar-cancel').click(); await page.setViewportSize({width:390,height:844});await page.getByRole('button',{name:'Actions for Ari',exact:true}).click();await page.getByRole('menuitem',{name:'Mate settings',exact:true}).click();await page.locator('#mate-profile-avatar').click();assert.equal(await page.getByRole('heading',{name:'Oddballs',exact:true}).count(),1);await page.locator('#mate-avatar-cancel').click(); assert.deepEqual(errors,[]); });