109 lines
10 KiB
JavaScript
109 lines
10 KiB
JavaScript
const {openSetup}=require('./support/browser.cjs');
|
||
// End-to-end coverage for scheduling contacts, country imports, working hours, and display timezones.
|
||
|
||
const assert=require('node:assert/strict');
|
||
const fs=require('node:fs');
|
||
const base=process.env.MEGURI_BROWSER_URL || 'http://127.0.0.1:3000';
|
||
assert(['localhost','127.0.0.1'].includes(new URL(base).hostname));
|
||
fs.mkdirSync('test-results',{recursive:true});
|
||
(async()=>{
|
||
const browser=await require('./support/browser.cjs').launchBrowser();
|
||
const page=await browser.newPage({viewport:{width:1440,height:1100},timezoneId:'America/Los_Angeles'});
|
||
page.setDefaultTimeout(10000);const errors=[];page.on('pageerror',e=>errors.push(e.message));
|
||
const dialog=()=>page.getByRole('dialog');
|
||
const save=async()=>{await dialog().getByRole('button',{name:'Save changes',exact:true}).click();await dialog().waitFor({state:'hidden'});};
|
||
const state=()=>page.evaluate(()=>{const app=Alpine.$data(document.body);return JSON.parse(JSON.stringify({workspace:app.workspace,user:app.user,endpoint:app.endpoint(),displayZone:app.displayZone}));});
|
||
try {
|
||
const label='Calendars '+Date.now();
|
||
await page.goto(base);
|
||
await page.getByRole('button',{name:'New here? Create an account'}).click();
|
||
await page.getByLabel('Username',{exact:true}).fill('calendars-'+Date.now());
|
||
await page.getByLabel('Display name',{exact:true}).fill('Calendar tester');
|
||
await page.getByLabel('Password',{exact:true}).fill('local-browser-test-password');
|
||
await page.getByLabel('Email address',{exact:true}).fill('browser-'+Date.now()+'-'+Math.random().toString(16).slice(2)+'@example.com');await page.getByRole('button',{name:'Create account',exact:true}).click();
|
||
await page.getByRole('button',{name:'New workspace',exact:true}).waitFor();
|
||
await page.getByRole('button',{name:'New workspace',exact:true}).click();
|
||
await dialog().getByLabel('Name',{exact:true}).fill(label);await save();
|
||
await page.getByRole('button',{name:'New workspace',exact:true}).click();
|
||
await dialog().getByLabel('Name',{exact:true}).fill(label);await save();
|
||
await openSetup(page,'Workspace settings');
|
||
await page.getByRole('button',{name:'+ Add teammate'}).click();await dialog().getByLabel('Registered username').fill('bob');await save();
|
||
await openSetup(page,'Schedules');
|
||
await page.locator('.schedule-create > summary').click();
|
||
await page.getByRole('button',{name:'+ Rotation',exact:true}).click();
|
||
await dialog().getByLabel('Schedule name').fill('Distributed rotation');
|
||
await dialog().getByLabel('Handover time').fill('09:00');
|
||
await dialog().getByLabel('Remove participant').first().click();
|
||
await dialog().getByRole('button',{name:'+ Add participant · name or email'}).click();
|
||
const picker=()=>page.getByRole('region',{name:'Choose a person'});
|
||
await picker().getByLabel('Search workspace users or enter email').fill('Bob');
|
||
await picker().locator('.picker-option').filter({hasText:'Bob Chen'}).click();
|
||
await dialog().getByRole('button',{name:'+ Add participant · name or email'}).click();
|
||
await picker().getByLabel('Search workspace users or enter email').fill('never-logged-in@example.com');
|
||
assert.equal(await picker().getByLabel('Country',{exact:true}).inputValue(),'CY');
|
||
await picker().getByLabel('Name (optional)',{exact:true}).fill('Ani in Armenia');
|
||
await picker().getByLabel('Country',{exact:true}).selectOption('AM');
|
||
await picker().getByRole('button',{name:'Add never-logged-in@example.com',exact:true}).click();
|
||
await page.screenshot({path:'test-results/rotation-people.png',fullPage:true});await save();
|
||
let current=await state();assert.equal(await page.getByLabel('Workspace',{exact:true}).inputValue(),current.workspace.id);const contact=current.workspace.document.people.find(p=>p.email==='never-logged-in@example.com');
|
||
assert.equal(contact.user_id,null);assert.equal(contact.country,'AM');assert.equal(current.workspace.document.schedules[0].handover,'09:00:00');
|
||
await page.getByRole('button',{name:'+ Absence',exact:true}).click();
|
||
await dialog().locator('.person-select').click();
|
||
await picker().getByLabel('Search workspace users or enter email').fill('absence-only@example.com');
|
||
await picker().getByRole('button',{name:'Add absence-only@example.com',exact:true}).click();await save();
|
||
current=await state();assert(current.workspace.document.absences.some(a=>a.user_id===current.workspace.document.people.find(p=>p.email==='absence-only@example.com').id));
|
||
await page.locator('#public-holidays').evaluate(el=>el.open=true);await page.getByRole('button',{name:'Import CSV',exact:true}).click();
|
||
const csv='country,date,name\nCY,2027-01-01,New Year\nAM,2027-01-01,"New Year, holiday"';
|
||
const before=current.workspace.revision;
|
||
await dialog().getByLabel('Holiday CSV').fill(csv);
|
||
await dialog().getByText('New holidays: 2',{exact:true}).waitFor();
|
||
let remote=await page.evaluate(async endpoint=>fetch(endpoint).then(r=>r.json()),current.endpoint);
|
||
assert.equal(remote.workspace.revision,before,'Preview must not write');assert.equal(remote.workspace.document.holidays.length,0);
|
||
await dialog().getByLabel('Holiday CSV').fill(csv+'\nXX,2027-01-01,Invalid country');
|
||
await dialog().locator('.validation-error').waitFor();assert(await dialog().getByRole('button',{name:'Import new holidays'}).isDisabled());
|
||
await dialog().getByLabel('Holiday CSV').fill(csv);await dialog().getByText('New holidays: 2',{exact:true}).waitFor();
|
||
await page.screenshot({path:'test-results/holiday-import.png',fullPage:true});
|
||
await dialog().getByRole('button',{name:'Import new holidays'}).click();await dialog().waitFor({state:'hidden'});
|
||
await page.locator('.holiday-group summary').filter({hasText:'Cyprus'}).waitFor();await page.locator('.holiday-group summary').filter({hasText:'Armenia'}).waitFor();
|
||
await page.locator('#public-holidays').evaluate(el=>el.open=true);await page.getByRole('button',{name:'Import CSV',exact:true}).click();
|
||
await dialog().getByLabel('Holiday CSV').fill(csv);await dialog().getByText('2 already saved',{exact:true}).waitFor();assert(await dialog().getByRole('button',{name:'Import new holidays'}).isDisabled());
|
||
await dialog().getByRole('button',{name:'Cancel',exact:true}).click();
|
||
await openSetup(page,'Workspace settings');
|
||
await page.getByRole('button',{name:'Edit working interval'}).click();
|
||
await dialog().getByLabel('Workspace IANA timezone').fill('Asia/Yerevan');
|
||
await dialog().getByLabel('Working day starts').fill('10:00');await dialog().getByLabel('Working day ends').fill('18:00');await save();
|
||
current=await state();assert.equal(current.workspace.document.timezone,'Asia/Yerevan');assert.equal(current.workspace.document.working_interval.start,'10:00:00');
|
||
await openSetup(page,'Escalations & routing');await page.getByRole('button',{name:'+ Escalation policy',exact:true}).click();
|
||
await dialog().getByLabel('Policy name').fill('Primary then backup');
|
||
await dialog().getByRole('button',{name:'+ Escalation step'}).click();await save();
|
||
await page.getByRole('button',{name:'+ Routing rule',exact:true}).click();
|
||
await dialog().getByLabel('Rule name').fill('Production work hours');
|
||
await dialog().getByLabel('Escalation policy').selectOption({label:'Primary then backup'});
|
||
await dialog().getByRole('button',{name:'+ AND condition'}).click();await dialog().getByLabel('Value',{exact:true}).fill('env:prod');
|
||
await dialog().getByLabel('Only during working hours').check();
|
||
await dialog().getByText(/Mon, Tue, Wed, Thu, Fri · 10:00–18:00 · Asia\/Yerevan/).waitFor();
|
||
assert.equal(await dialog().locator('input[type=time]').count(),0,'Working interval is edited at workspace level');
|
||
await page.screenshot({path:'test-results/routing-editor.png',fullPage:true});await save();
|
||
await page.screenshot({path:'test-results/policy-flows.png',fullPage:true});
|
||
current=await state();const schedule=JSON.stringify(current.workspace.document.schedules),revision=current.workspace.revision;
|
||
assert.equal(current.displayZone,'America/Los_Angeles','Follow browser timezone by default');
|
||
await page.locator('.account-button').click();
|
||
assert.equal(await dialog().getByLabel('Country of work').inputValue(),'CY');
|
||
assert((await dialog().getByLabel('Country of work').locator('option').count())>=249);
|
||
await dialog().getByLabel('Display timezone',{exact:true}).fill('Asia/Tokyo');await save();
|
||
current=await state();assert.equal(current.displayZone,'Asia/Tokyo');assert.equal(current.workspace.revision,revision);assert.equal(JSON.stringify(current.workspace.document.schedules),schedule);
|
||
await page.getByRole('link', { name: 'Calendar',exact:true}).click();await page.locator('.day-clock').first().filter({hasText:'14:00 GMT+9'}).waitFor();
|
||
await page.locator('.account-button').click();await dialog().getByRole('button',{name:'Use browser timezone'}).click();await save();
|
||
current=await state();assert.equal(current.displayZone,'America/Los_Angeles');assert.equal(current.workspace.revision,revision);
|
||
await openSetup(page,'Schedules');await page.locator('#public-holidays').evaluate(el=>el.open=true);await page.getByRole('button',{name:'Import CSV',exact:true}).click();
|
||
await dialog().getByLabel('Holiday CSV').fill(csv+'\nCY,2027-12-25,Christmas');await dialog().getByText('New holidays: 1',{exact:true}).waitFor();
|
||
await page.setViewportSize({width:390,height:844});await page.screenshot({path:'test-results/holiday-import-mobile.png',fullPage:true});
|
||
assert(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth),'Mobile import should not overflow');
|
||
assert.deepEqual(errors,[]);
|
||
console.log('PASS: scoped user picker, email rotation/absence, country CSV preview/import/validation/dedup, workspace interval, policy flows, browser/account display timezone, mobile import');
|
||
} catch(error) {
|
||
console.error('Browser errors:',errors);console.error('Visible alert:',await page.locator('[role=alert]').textContent());await page.screenshot({path:'test-results/calendars-failure.png',fullPage:true});throw error;
|
||
} finally {await browser.close();}
|
||
})().catch(error=>{console.error(error);process.exitCode=1;});
|
||
|