175 lines
6.4 KiB
JavaScript
175 lines
6.4 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import process from 'node:process';
|
|
const { chromium } = await import(
|
|
process.env.PLAYWRIGHT_MODULE ?? 'playwright'
|
|
);
|
|
const browser = await chromium.launch({ channel: 'chrome', headless: true });
|
|
const base = 'http://127.0.0.1:4176';
|
|
const context = await browser.newContext({
|
|
viewport: { width: 1280, height: 900 },
|
|
hasTouch: true,
|
|
});
|
|
const page = await context.newPage();
|
|
page.setDefaultTimeout(10000);
|
|
page.on('dialog', (dialog) => dialog.accept());
|
|
const errors = [];
|
|
page.on('pageerror', (error) => errors.push(error.message));
|
|
const checklist = () =>
|
|
page.getByRole('region', { name: 'Acceptance criteria' });
|
|
const field = (number) =>
|
|
page.getByRole('textbox', { name: `Criterion ${number}`, exact: true });
|
|
const button = (name) => page.getByRole('button', { name, exact: true });
|
|
const setResult = (value) =>
|
|
context.addCookies([{ name: 'feedback_test_result', value, url: base }]);
|
|
const waitState = (value) =>
|
|
page.locator(`[data-form-state="${value}"]`).first().waitFor();
|
|
const save = async () => {
|
|
await button('Save changes').click();
|
|
await waitState('success');
|
|
};
|
|
try {
|
|
await page.goto(`${base}/projects/7/tickets/31`);
|
|
await checklist().waitFor();
|
|
const boxes = checklist().getByRole('checkbox');
|
|
await setResult('server');
|
|
await boxes.first().focus();
|
|
await page.keyboard.press('Space');
|
|
await page
|
|
.getByText('Checklist status could not be saved. Please try again.')
|
|
.waitFor();
|
|
assert.equal(
|
|
await boxes.first().isChecked(),
|
|
false,
|
|
'Failure must restore saved checkbox',
|
|
);
|
|
await setResult('success');
|
|
let payload;
|
|
page.on('request', (request) => {
|
|
if (request.method() === 'PATCH') {
|
|
payload = request.postDataJSON();
|
|
}
|
|
});
|
|
await boxes.first().tap();
|
|
await page.waitForFunction(
|
|
() =>
|
|
document.querySelectorAll('section[aria-busy="true"]').length === 0,
|
|
);
|
|
await page.getByText('3 of 4 complete').waitFor();
|
|
assert.deepEqual(
|
|
payload.acceptance_criteria.map((item) => item.checked),
|
|
[true, true, true, false],
|
|
);
|
|
assert.deepEqual(
|
|
payload.acceptance_criteria.map((item) => item.id),
|
|
[11, 12, 13, 14],
|
|
);
|
|
await button('Edit checklist').click();
|
|
await field(2).fill('Edited child');
|
|
await field(2).press('Alt+ArrowLeft');
|
|
assert.deepEqual(
|
|
await checklist()
|
|
.locator('li')
|
|
.evaluateAll((items) =>
|
|
items.map((item) => Number(item.dataset.indent)),
|
|
),
|
|
[0, 0, 1, 0],
|
|
);
|
|
await button('Indent criterion 2').tap();
|
|
await button('Remove criterion 1').tap();
|
|
assert.equal(await field(1).inputValue(), 'Edited child');
|
|
assert.equal(
|
|
await field(1).evaluate((el) => el === document.activeElement),
|
|
true,
|
|
);
|
|
await button('Add criterion').focus();
|
|
await page.keyboard.press('Enter');
|
|
assert.equal(
|
|
await field(4).evaluate((el) => el === document.activeElement),
|
|
true,
|
|
);
|
|
await field(4).fill('New criterion');
|
|
await button('Indent criterion 4').click();
|
|
await setResult('validation');
|
|
await button('Save changes').click();
|
|
await waitState('validation');
|
|
assert.equal(await field(1).inputValue(), 'Edited child');
|
|
await setResult('success');
|
|
await save();
|
|
assert.deepEqual(
|
|
payload.acceptance_criteria.map((item) => item.id),
|
|
[12, 13, 14, undefined],
|
|
);
|
|
assert.deepEqual(
|
|
payload.acceptance_criteria.map((item) => item.indent),
|
|
[0, 1, 0, 1],
|
|
);
|
|
await page.reload();
|
|
await button('Edit checklist').click();
|
|
assert.equal(await field(1).inputValue(), 'Edited child');
|
|
assert.equal(await field(4).inputValue(), 'New criterion');
|
|
// Blank/whitespace text cannot silently delete a persisted criterion.
|
|
await field(1).fill(' ');
|
|
assert.equal(await field(1).evaluate((el) => el.checkValidity()), false);
|
|
assert.equal(await button('Preview checklist').isDisabled(), true);
|
|
await field(1).fill('Edited child');
|
|
for (const width of [1280, 390, 320]) {
|
|
await page.setViewportSize({ width, height: 900 });
|
|
for (const theme of ['light', 'dark']) {
|
|
await page.emulateMedia({ colorScheme: theme });
|
|
await page.evaluate(
|
|
(theme) =>
|
|
document.documentElement.classList.toggle(
|
|
'dark',
|
|
theme === 'dark',
|
|
),
|
|
theme,
|
|
);
|
|
assert.equal(
|
|
await checklist().evaluate(
|
|
(el) => el.scrollWidth <= el.clientWidth,
|
|
),
|
|
true,
|
|
);
|
|
await checklist().screenshot({
|
|
path: `/private/tmp/kan32-checklist-${width}-${theme}.png`,
|
|
});
|
|
await button('Outdent criterion 4').tap();
|
|
await button('Indent criterion 4').tap();
|
|
}
|
|
}
|
|
await page.goto(`${base}/projects/7/tickets/31?readonly=1`);
|
|
await checklist().waitFor();
|
|
assert.equal(await checklist().getByRole('textbox').count(), 0);
|
|
assert.equal(await checklist().getByRole('button').count(), 0);
|
|
assert.equal(
|
|
await checklist().getByRole('checkbox').first().isDisabled(),
|
|
true,
|
|
);
|
|
await page.goto(`${base}/projects/7/tickets/create`);
|
|
await page.locator('#title').fill('Created with criteria');
|
|
for (const text of ['First', 'Second']) {
|
|
await button('Add criterion').click();
|
|
await page.locator('textarea').last().fill(text);
|
|
}
|
|
await button('Indent criterion 2').tap();
|
|
await checklist().getByRole('checkbox').first().focus();
|
|
await page.keyboard.press('Space');
|
|
const request = page.waitForRequest(
|
|
(request) =>
|
|
request.method() === 'POST' && request.url().endsWith('/tickets'),
|
|
);
|
|
await button('Create ticket').click();
|
|
const created = (await request).postDataJSON();
|
|
assert.deepEqual(created.acceptance_criteria, [
|
|
{ text: 'First', checked: true, indent: 0 },
|
|
{ text: 'Second', checked: true, indent: 1 },
|
|
]);
|
|
await page.getByRole('heading', { name: 'Destination' }).waitFor();
|
|
assert.deepEqual(errors, []);
|
|
console.log(
|
|
'Acceptance criteria browser checks passed: persistence, errors, keyboard, touch, readonly, creation, mobile and themes.',
|
|
);
|
|
} finally {
|
|
await browser.close();
|
|
}
|