79 lines
5.6 KiB
JavaScript
79 lines
5.6 KiB
JavaScript
// Test an installed Conductor build with its real bridge and a read-only scheduler fixture.
|
|
import {chromium,expect} from '@playwright/test';
|
|
import {spawn,spawnSync} from 'node:child_process';
|
|
import fs from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import http from 'node:http';
|
|
import net from 'node:net';
|
|
import {tmpdir} from 'node:os';
|
|
const build=path.resolve(process.argv[2]||'.local/catalog-build-verification/.local/conductor-build');
|
|
const data=await fs.mkdtemp(path.join(tmpdir(),'scheduler-catalog-'));
|
|
const artifact=path.resolve('test-results/slice10-social-integration');await fs.mkdir(artifact,{recursive:true});
|
|
const fixture=http.createServer((req,res)=>{
|
|
let value;
|
|
if(req.method==='GET'&&req.url==='/social-scheduler/session')value={displayName:'Customer administrator',role:req.headers['x-conductor-role'],workspaces:[{id:'customer',name:'Customer workspace'}],preview:false,publishingEnabled:false};
|
|
else if(req.method==='GET'&&req.url==='/social-scheduler/workspaces/customer')value={accounts:[],posts:[],publishingEnabled:false};
|
|
else {res.writeHead(404);res.end();return;}
|
|
res.writeHead(200,{'content-type':'application/json'});res.end(JSON.stringify(value));
|
|
});
|
|
await new Promise(resolve=>fixture.listen(0,'127.0.0.1',resolve));
|
|
const port=await new Promise(resolve=>{const probe=net.createServer();probe.listen(0,'127.0.0.1',()=>{const port=probe.address().port;probe.close(()=>resolve(port));});});
|
|
const base='http://127.0.0.1:'+port;
|
|
const env={...process.env,PORT:String(port),CONDUCTOR_DATA_DIR:data,CONDUCTOR_STATIC_DIR:path.join(build,'frontend/build'),CONDUCTOR_SECRET_KEY:Buffer.alloc(32,21).toString('base64'),SOCIAL_API_ORIGIN:'http://127.0.0.1:'+fixture.address().port+'/social-scheduler',SOCIAL_INTERNAL_TOKEN:'local-test-fixture',SOCIAL_WORKSPACE_ID:'customer'};
|
|
const server=spawn(process.execPath,['backend/dist/index.js'],{cwd:build,env,stdio:['ignore','pipe','pipe']});
|
|
let output='';server.stdout.on('data',c=>{output+=c;});server.stderr.on('data',c=>{output+=c;});
|
|
let browser,page;
|
|
try {
|
|
for(let n=0;n<100;n++){
|
|
if(server.exitCode!==null)throw new Error(output);
|
|
if(await fetch(base+'/api/health').then(r=>r.ok).catch(()=>false))break;
|
|
if(n===99)throw new Error('Conductor did not start.');
|
|
await new Promise(resolve=>setTimeout(resolve,50));
|
|
}
|
|
browser=await chromium.launch({headless:true});
|
|
const context=await browser.newContext({baseURL:base,viewport:{width:1360,height:980}});
|
|
const setup=await context.request.get('/api/auth/setup');
|
|
const response=await context.request.post('/api/auth/setup',{headers:{'X-Setup-Token':(await setup.json()).setupToken},data:{username:'catalog-test-admin',password:'catalog-test-password',displayName:'Customer Administrator'}});
|
|
expect(response.status()).toBe(201);
|
|
page=await context.newPage();const errors=[];page.on('pageerror',e=>errors.push(e.message));
|
|
await page.goto('/');
|
|
await expect(page).toHaveTitle('Conductor');
|
|
await page.getByRole('navigation').getByRole('button',{name:'Publishing',exact:true}).click();
|
|
await expect(page).toHaveTitle('Publishing · Conductor');
|
|
await expect(page.getByText('No included applications are installed yet.',{exact:false})).toBeVisible();
|
|
await expect(page.getByRole('link',{name:'Open Social Scheduler',exact:true})).toHaveCount(0);
|
|
const manifest=await fs.readFile(path.join(path.resolve(process.argv[3]||'../social-scheduler'),'integration/included-app.json'),'utf8');
|
|
for(let n=0;n<2;n++){
|
|
const result=spawnSync(process.execPath,['backend/dist/scripts/includedApps.js','register'],{cwd:build,env,input:manifest,encoding:'utf8'});
|
|
expect(result.status,result.stderr).toBe(0);
|
|
}
|
|
await page.getByRole('button',{name:'Refresh applications',exact:true}).click();
|
|
await expect(page.getByRole('heading',{name:'Social Scheduler',exact:true})).toHaveCount(1);
|
|
await expect(page.getByRole('link',{name:'Open Social Scheduler',exact:true})).toHaveAttribute('href','/social-scheduler');
|
|
await page.screenshot({path:path.join(artifact,'publishing-social-scheduler.png'),fullPage:true});
|
|
await page.getByRole('link',{name:'Configure',exact:true}).click();
|
|
await expect(page).toHaveURL(base+'/social-scheduler?view=accounts');
|
|
await expect(page.getByRole('heading',{name:'Connect social media'})).toBeVisible();
|
|
await expect(page.getByLabel('Bluesky handle')).toBeVisible();
|
|
await expect(page).toHaveTitle('Social Scheduler · Conductor');
|
|
await expect(page.locator('#conductor-favicon')).toHaveAttribute('href','/favicon.svg');
|
|
await page.screenshot({path:path.join(artifact,'configure-social-scheduler.png'),fullPage:true});
|
|
await page.goto('/');
|
|
await page.getByRole('link',{name:'Open Social Scheduler',exact:true}).click();
|
|
await expect(page.getByRole('heading',{name:'Compose a post'})).toBeVisible();
|
|
await expect(page).toHaveTitle('Social Scheduler · Conductor');
|
|
await expect(page.locator('#conductor-favicon')).toHaveAttribute('href','/favicon.svg');
|
|
await expect(page.getByRole('button',{name:'Queue post now'})).toBeDisabled();
|
|
await page.goto('/');await expect(page).toHaveTitle('Conductor');
|
|
expect(errors).toEqual([]);
|
|
console.log('PASS: installed Conductor build, empty-before-registration, real manifest upsert, Publishing card, Configure/Open through authenticated bridge, inherited app title/default icon, reset on return home.');
|
|
} catch(error){
|
|
if(page)await page.screenshot({path:path.join(artifact,'failure.png'),fullPage:true}).catch(()=>{});
|
|
throw error;
|
|
} finally {
|
|
await browser?.close();
|
|
if(server.exitCode===null)await new Promise(resolve=>{server.once('exit',resolve);server.kill('SIGTERM');});
|
|
fixture.closeAllConnections();await new Promise(resolve=>fixture.close(resolve));
|
|
await fs.rm(data,{recursive:true,force:true});
|
|
}
|