92 lines
5.1 KiB
JavaScript
92 lines
5.1 KiB
JavaScript
// Every custom dropdown (rj-select) of the dashboard: opened at several window sizes, no option may be squeezed or clipped,
|
|
// long lists scroll, and the list stays inside the window. Guards against the flex-shrink bug that cut the text of long lists.
|
|
const { chromium } = require('playwright');
|
|
|
|
const base = process.env.REVIEW_URL;
|
|
const password = process.env.REVIEW_PASSWORD;
|
|
const errors = [];
|
|
let opened = 0;
|
|
const seen = new Set();
|
|
|
|
function assert(condition, message) {
|
|
if (!condition) throw new Error(message);
|
|
}
|
|
|
|
async function inspectOpenList(page, label) {
|
|
return page.evaluate(() => {
|
|
const list = Array.from(document.querySelectorAll('.rj-select__list')).find(el => !el.hidden);
|
|
if (!list) return { error: 'the list did not open' };
|
|
const rect = list.getBoundingClientRect();
|
|
const options = Array.from(list.querySelectorAll('.rj-select__option, .rj-select__group'));
|
|
const squeezed = options.filter(el => {
|
|
const style = getComputedStyle(el);
|
|
const needed = parseFloat(style.lineHeight === 'normal' ? parseFloat(style.fontSize) * 1.2 : style.lineHeight) + parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);
|
|
return el.getBoundingClientRect().height + 0.5 < needed || el.scrollHeight > el.clientHeight + 1;
|
|
}).map(el => el.textContent.trim() + ' [h=' + el.getBoundingClientRect().height + ' ch=' + el.clientHeight + ' sh=' + el.scrollHeight + ' lh=' + getComputedStyle(el).lineHeight + ']');
|
|
return {
|
|
count: options.length, squeezed,
|
|
scrolls: list.scrollHeight > list.clientHeight + 1,
|
|
fits: list.scrollHeight <= list.clientHeight + 1,
|
|
inside: rect.top >= -1 && rect.bottom <= window.innerHeight + 1 && rect.left >= -1 && rect.right <= window.innerWidth + 1,
|
|
height: Math.round(rect.height)
|
|
};
|
|
});
|
|
}
|
|
|
|
async function checkVisibleDropdowns(page, where) {
|
|
const buttons = await page.locator('.rj-select__button').all();
|
|
for (const button of buttons) {
|
|
if (!(await button.isVisible())) continue;
|
|
const id = (await button.getAttribute('data-rj-select-for')) || '(no id)';
|
|
const key = where + ' / ' + id;
|
|
if (seen.has(key)) continue;
|
|
seen.add(key);
|
|
await button.scrollIntoViewIfNeeded();
|
|
await button.click();
|
|
const result = await inspectOpenList(page, key);
|
|
assert(!result.error, key + ': ' + result.error);
|
|
assert(result.squeezed.length === 0, key + ': options are squeezed or clipped: ' + result.squeezed.join(', '));
|
|
assert(result.fits || result.scrolls, key + ': the list neither fits nor scrolls');
|
|
assert(result.inside, key + ': the list leaves the window (' + result.height + 'px high)');
|
|
opened++;
|
|
await page.keyboard.press('Escape');
|
|
await page.waitForTimeout(60);
|
|
if (await page.locator('.rj-select__list:not([hidden])').count()) await page.mouse.click(2, 2);
|
|
}
|
|
}
|
|
|
|
async function walk(page, sizeName) {
|
|
const topTabs = await page.evaluate(() => Array.from(document.querySelectorAll('.sidebar-nav-item[data-tab]')).map(el => el.getAttribute('data-tab')));
|
|
for (const tab of topTabs) {
|
|
await page.evaluate(name => switchTab(name), tab);
|
|
await page.waitForTimeout(150);
|
|
if (tab !== 'settings') { await checkVisibleDropdowns(page, sizeName + ' ' + tab); continue; }
|
|
const settingsTabs = await page.evaluate(() => Array.from(document.querySelectorAll('.settings-tab-btn[data-settings-tab]')).map(el => el.getAttribute('data-settings-tab')));
|
|
for (const settingsTab of Array.from(new Set(settingsTabs))) {
|
|
await page.evaluate(name => { const button = document.querySelector('.settings-tab-btn[data-settings-tab="' + name + '"]'); if (button) button.click(); }, settingsTab);
|
|
await page.waitForTimeout(200);
|
|
await checkVisibleDropdowns(page, sizeName + ' settings/' + settingsTab);
|
|
}
|
|
}
|
|
}
|
|
|
|
(async () => {
|
|
const browser = await chromium.launch();
|
|
for (const [name, viewport] of [['desktop', { width: 1280, height: 900 }], ['short', { width: 1280, height: 520 }], ['tablet', { width: 820, height: 700 }], ['phone', { width: 390, height: 640 }]]) {
|
|
const context = await browser.newContext({ viewport });
|
|
const page = await context.newPage();
|
|
page.on('pageerror', error => errors.push(name + ': ' + error.message));
|
|
await page.goto(base + '/admin/index.php');
|
|
await page.locator('#username').fill('admin');
|
|
await page.locator('#password').fill(password);
|
|
await Promise.all([page.waitForURL('**/admin/dashboard.php'), page.locator('form').first().locator('button[type=submit]').click()]);
|
|
await page.waitForTimeout(400);
|
|
await walk(page, name);
|
|
await context.close();
|
|
}
|
|
await browser.close();
|
|
assert(opened > 20, 'only ' + opened + ' dropdowns were checked');
|
|
if (errors.length) throw new Error(errors.join('\n'));
|
|
console.log('dropdown-check ok: ' + opened + ' dropdowns opened and inspected');
|
|
})().catch(error => { console.error(error.message); process.exit(1); });
|