conductor/scripts/test-update-conductor.mjs

68 lines
4.7 KiB
JavaScript

// Browser checks run only against the isolated SLICE13 test host.
import {chromium,expect} from '@playwright/test';
import fs from 'node:fs/promises';
const [host,port,phase]=process.argv.slice(2);
if(!/^conductor-slice13-[a-f0-9]{10}$/.test(host)||!/^\d+$/.test(port)||!['seed','legacy','upgraded'].includes(phase))throw Error('Disposable upgrade fixture required.');
const dir='test-results/slice13',base='https://conductor.customer.test:'+port;
const browser=await chromium.launch({headless:true,args:['--host-resolver-rules=MAP conductor.customer.test 127.0.0.1','--no-proxy-server']});
let page;
try{
const context=await browser.newContext({baseURL:base,ignoreHTTPSErrors:true,viewport:{width:1400,height:1000}});
page=await context.newPage();
await page.goto('/');
await expect(page.getByLabel('Username',{exact:true})).toBeVisible();
const request=async(method,url,options={})=>{
const value=await page.evaluate(async({method,url,data,headers})=>{
const response=await fetch(url,{method,headers:{...(data?{'Content-Type':'application/json'}:{}),...headers},body:data?JSON.stringify(data):undefined});
return {status:response.status,body:await response.json()};
},{method,url,...options});
return {status:()=>value.status,json:async()=>value.body};
};
let auth;
if(phase==='seed'){
const setup=await request('GET','/api/auth/setup');
auth=await request('POST','/api/auth/setup',{headers:{'X-Setup-Token':(await setup.json()).setupToken},data:{username:'customer-admin',password:'disposable-upgrade-password',displayName:'Customer Administrator'}});
expect(auth.status()).toBe(201);
}else{
auth=await request('POST','/api/auth/login',{data:{username:'customer-admin',password:'disposable-upgrade-password'}});
expect(auth.status()).toBe(200);
}
const csrf=(await auth.json()).csrfToken;
if(phase==='seed'){
const project=await request('POST','/api/projects',{headers:{'X-CSRF-Token':csrf},data:{name:'Customer project',description:'Preserve my saved work.'}});
expect(project.status()).toBe(201);
const publication=await request('POST','/api/admin/published-apps',{headers:{'X-CSRF-Token':csrf},data:{sourceProjectId:(await project.json()).id,slug:'customer-app',displayName:'Customer publication',visibility:'authenticated'}});
expect(publication.status()).toBe(201);
}
const projects=await (await request('GET','/api/projects')).json();
expect(projects).toHaveLength(1);expect(projects[0].name).toBe('Customer project');
const published=await (await request('GET','/api/admin/published-apps')).json();
expect(published).toHaveLength(1);expect(published[0].slug).toBe('customer-app');
expect(published[0].version).toBe(1);
const session=await request('GET','/api/social-scheduler/session');
expect(session.status()).toBe(200);expect((await session.json()).role).toBe('admin');
const snapshot=await (await request('GET','/api/social-scheduler/workspaces/customer')).json();
expect(snapshot.accounts[0].handle).toBe('customer.bsky.social');
expect(snapshot.posts[0].text).toBe('Scheduled before the Conductor upgrade.');
const errors=[];page.on('pageerror',e=>errors.push(e.message));
await page.goto('/');await page.getByRole('navigation').getByRole('button',{name:'Publishing',exact:true}).click();
await expect(page.getByRole('heading',{name:'Social Scheduler',exact:true})).toHaveCount(1);
await page.screenshot({path:dir+'/'+phase+'-publishing.png',fullPage:true});
await page.getByRole('link',{name:'Open Social Scheduler',exact:true}).click();
await expect(page.getByRole('heading',{name:'Compose a post',exact:true})).toBeVisible();
await expect(page.getByText('Scheduled before the Conductor upgrade.',{exact:true}).first()).toBeVisible();
if(phase==='upgraded'){
await expect(page).toHaveTitle('Social Scheduler · Conductor');
await expect(page.locator('#conductor-favicon')).toHaveAttribute('href','/favicon.svg');
}
await page.screenshot({path:dir+'/'+phase+'-social.png',fullPage:true});
await page.goto('/apps/customer-app');
await expect(page.getByText('Customer publication',{exact:true}).first()).toBeVisible();
expect(errors).toEqual([]);
await fs.writeFile(dir+'/'+phase+'-browser.json',JSON.stringify({passed:true,phase,checks:['same administrator password','saved project','unchanged published snapshot/version','existing social connection','existing queued post','authenticated API bridge','single included-app card','real Social Scheduler screen',...(phase==='upgraded'?['app browser title','default favicon']:[])]},null,2));
console.log('PASS: '+phase+' browser login, project/publication, app screen and existing connection/queue.');
}catch(error){
if(page)await page.screenshot({path:dir+'/'+phase+'-failure.png',fullPage:true}).catch(()=>{});
throw error;
}finally{await browser.close();}