All checks were successful
Build and Push Docker Image / build (push) Successful in 25s
#8 Mobile/Responsive (14 PASS, 2 minor WARN): - 8 pages × 2 viewports (iPhone 375px + iPad 768px) = 16 checks - All dashboard/list/analytics pages: no overflow, content renders ✅ - Public complaint/observation forms: minor 13px horizontal overflow at 375px (cosmetic) #9 i18n/Arabic (6 PASS): - Language switch to Arabic: dir=rtl, lang=ar, Arabic text present on all 5 pages ✅ - No horizontal overflow in RTL layout ✅ - English restoration works (dir back to ltr) ✅
82 lines
3.5 KiB
TypeScript
82 lines
3.5 KiB
TypeScript
/* eslint-disable */
|
|
/**
|
|
* MOBILE/RESPONSIVE TEST — key pages at phone (375px) + tablet (768px) viewports.
|
|
* Checks for horizontal overflow, unusable controls, layout breakage.
|
|
*
|
|
* Run headed:
|
|
* npx playwright test --headed --project chromium mobile-responsive-test --workers=1
|
|
*/
|
|
import { test } from '@playwright/test';
|
|
import { attachObservers, observe, loginAndScope, OBS, BASE_URL } from '../../helpers/audit';
|
|
import { RoleName } from '../../helpers/helpers';
|
|
|
|
const M = 'MobileResponsive';
|
|
const ADMIN: RoleName = 'hospital_admin';
|
|
|
|
type Page = import('@playwright/test').Page;
|
|
|
|
async function login(page: Page) { await page.context().clearCookies(); await loginAndScope(page, ADMIN, M); }
|
|
|
|
const VIEWPORTS = [
|
|
{ width: 375, height: 812, label: 'iphone' },
|
|
{ width: 768, height: 1024, label: 'ipad' },
|
|
];
|
|
|
|
const PAGES = [
|
|
{ url: '/accounts/login/', label: 'login', auth: false },
|
|
{ url: '/', label: 'dashboard' },
|
|
{ url: '/complaints/', label: 'complaint-list' },
|
|
{ url: '/complaints/public/submit/', label: 'public-form', auth: false },
|
|
{ url: '/observations/new/', label: 'obs-form', auth: false },
|
|
{ url: '/observations/track/', label: 'obs-track', auth: false },
|
|
{ url: '/core/public/submit/', label: 'public-landing', auth: false },
|
|
{ url: '/analytics/dashboard/', label: 'analytics' },
|
|
];
|
|
|
|
test.describe('Mobile/Responsive', () => {
|
|
for (const vp of VIEWPORTS) {
|
|
test.describe(`${vp.label} (${vp.width}px)`, () => {
|
|
for (const { url, label, auth = true } of PAGES) {
|
|
test(`${label} at ${vp.width}px`, async ({ browser }) => {
|
|
const context = await browser.newContext({ viewport: { width: vp.width, height: vp.height } });
|
|
const page = await context.newPage();
|
|
attachObservers(page, M, ADMIN);
|
|
|
|
if (auth) await login(page);
|
|
|
|
await page.goto(`${BASE_URL}${url}`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(1500);
|
|
|
|
// Check 1: horizontal overflow (content wider than viewport)
|
|
const scrollWidth = await page.evaluate(() => document.documentElement.scrollWidth);
|
|
const clientWidth = await page.evaluate(() => document.documentElement.clientWidth);
|
|
const hasHOverflow = scrollWidth > clientWidth + 5; // 5px tolerance
|
|
|
|
// Check 2: page renders with content
|
|
const bodyLen = ((await page.textContent('body')) || '').length;
|
|
|
|
// Check 3: key interactive elements visible (at least one button/link/input)
|
|
const interactiveCount = await page.locator('button:visible, a:visible, input:visible, select:visible').count();
|
|
|
|
// Check 4: sidebar toggle (if present)
|
|
const hasSidebarToggle = await page.locator('[onclick*="sidebar"], [class*="hamburger"], [class*="menu-toggle"], button[aria-label*="menu"]').count();
|
|
|
|
observe(M, `${label}/${vp.label}`, !hasHOverflow && bodyLen > 100 ? 'PASS' : 'WARN',
|
|
`${vp.width}px: hOverflow=${hasHOverflow} (scroll=${scrollWidth} client=${clientWidth}) body=${bodyLen} interactive=${interactiveCount} sidebarToggle=${hasSidebarToggle > 0}`,
|
|
{ url: page.url() });
|
|
|
|
await context.close();
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
test.afterAll(async () => {
|
|
const counts = OBS.reduce<Record<string, number>>((a, o) => ((a[o.status] = (a[o.status] || 0) + 1), a), {});
|
|
console.log('\n=========== MOBILE/RESPONSIVE SUMMARY ===========');
|
|
console.log('Total observations:', OBS.length, JSON.stringify(counts));
|
|
console.log('=================================================\n');
|
|
});
|