Files
meguri/tests/browser-calendars.cjs
Ultradesu 5260c2828b
Checks / test (push) Failing after 2m25s
NEXT
2026-10-07 15:30:31 +01:00

102 lines
9.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const {openSetup,isolatedUser,signIn}=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 {
// Sandbox 2 belongs to this suite: an administrator plus Bob Chen as an employee.
const tester=await isolatedUser({sandbox:2,prefix:'calendars',display:'Calendar tester',employees:['Bob Chen']});
await signIn(page,tester.username,tester.password);
await page.getByLabel('Workspace',{exact:true}).waitFor();
await openSetup(page,'Workspace settings');
await page.locator('#workspace-members').getByText('Bob Chen',{exact:true}).waitFor();
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().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;});