Update homepage routes to include language and mode
Build & Deploy / Build & Push Docker Image (push) Successful in 2m51s
Test / Type Check (all packages) (push) Successful in 52s
Build & Deploy / Deploy to VPS (push) Successful in 4s
Test / API Unit Tests (push) Successful in 49s
Test / Homepage Unit Tests (push) Successful in 44s
Test / Storefront Unit Tests (push) Successful in 39s
Test / Admin Unit Tests (push) Successful in 40s
Test / Dashboard Unit Tests (push) Successful in 40s
Test / API Integration Tests (push) Successful in 1m0s
Build & Deploy / Build & Push Docker Image (push) Successful in 2m51s
Test / Type Check (all packages) (push) Successful in 52s
Build & Deploy / Deploy to VPS (push) Successful in 4s
Test / API Unit Tests (push) Successful in 49s
Test / Homepage Unit Tests (push) Successful in 44s
Test / Storefront Unit Tests (push) Successful in 39s
Test / Admin Unit Tests (push) Successful in 40s
Test / Dashboard Unit Tests (push) Successful in 40s
Test / API Integration Tests (push) Successful in 1m0s
This commit is contained in:
@@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test';
|
||||
|
||||
for (const locale of ['en', 'fr', 'ar'] as const) {
|
||||
test(`${locale} component fixture renders without horizontal overflow`, async ({ page }) => {
|
||||
await page.goto(`/${locale}/component-lab`);
|
||||
await page.goto(`/${locale}/system/component-lab`);
|
||||
await expect(page.locator('h1')).toBeVisible();
|
||||
const overflow = await page.evaluate(
|
||||
() => document.documentElement.scrollWidth > document.documentElement.clientWidth,
|
||||
@@ -15,7 +15,7 @@ for (const locale of ['en', 'fr', 'ar'] as const) {
|
||||
test('component controls support keyboard interaction and dialog focus return', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/en/component-lab');
|
||||
await page.goto('/en/system/component-lab');
|
||||
const firstTab = page.getByRole('tab', { name: 'Reservations' });
|
||||
await firstTab.focus();
|
||||
await page.keyboard.press('ArrowRight');
|
||||
@@ -36,7 +36,7 @@ test('component fixture reflows at narrow mobile and 200 percent zoom equivalent
|
||||
page,
|
||||
}) => {
|
||||
await page.setViewportSize({ width: 320, height: 800 });
|
||||
await page.goto('/fr/component-lab');
|
||||
await page.goto('/fr/system/component-lab');
|
||||
await page.evaluate(() => (document.body.style.zoom = '2'));
|
||||
const overflow = await page.evaluate(
|
||||
() => document.documentElement.scrollWidth > document.documentElement.clientWidth + 1,
|
||||
|
||||
@@ -6,7 +6,7 @@ for (const scenario of [
|
||||
{ locale: 'ar', dir: 'rtl' },
|
||||
] as const) {
|
||||
test(`${scenario.locale} homepage assembles the approved section sequence`, async ({ page }) => {
|
||||
await page.goto(`/${scenario.locale}`);
|
||||
await page.goto(`/${scenario.locale}/system`);
|
||||
|
||||
await expect(page.locator('html')).toHaveAttribute('dir', scenario.dir);
|
||||
await expect(page.locator('main h1')).toHaveCount(1);
|
||||
@@ -19,7 +19,7 @@ for (const scenario of [
|
||||
});
|
||||
|
||||
test(`${scenario.locale} unresolved actions remain non-navigable`, async ({ page }) => {
|
||||
await page.goto(`/${scenario.locale}`);
|
||||
await page.goto(`/${scenario.locale}/system`);
|
||||
|
||||
const disabledActions = page.locator('main [aria-disabled="true"]');
|
||||
await expect(disabledActions).toHaveCount(2);
|
||||
@@ -28,22 +28,22 @@ for (const scenario of [
|
||||
}
|
||||
|
||||
test('Arabic preview preserves left-to-right vehicle identifiers', async ({ page }) => {
|
||||
await page.goto('/ar');
|
||||
await page.goto('/ar/system');
|
||||
const identifiers = page.locator('[role="table"] bdi');
|
||||
await expect(identifiers).toHaveCount(2);
|
||||
await expect(identifiers.first()).toHaveAttribute('dir', 'ltr');
|
||||
});
|
||||
|
||||
test('homepage navigation targets resolve to real sections', async ({ page }) => {
|
||||
await page.goto('/en');
|
||||
await page.goto('/en/system');
|
||||
for (const hash of ['product', 'workflow', 'modules', 'pricing', 'faq']) {
|
||||
await expect(page.locator(`#${hash}`)).toHaveCount(1);
|
||||
await expect(page.locator(`header a[href="/en#${hash}"]`)).toHaveCount(2);
|
||||
await expect(page.locator(`header a[href="/en/system#${hash}"]`)).toHaveCount(2);
|
||||
}
|
||||
});
|
||||
|
||||
test('FAQ uses native disclosure behavior', async ({ page }) => {
|
||||
await page.goto('/en');
|
||||
await page.goto('/en/system');
|
||||
const firstItem = page.locator('#faq details').first();
|
||||
await expect(firstItem).toHaveAttribute('open', '');
|
||||
await firstItem.locator('summary').click();
|
||||
@@ -51,7 +51,7 @@ test('FAQ uses native disclosure behavior', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('pricing recommends a plan from the selected fleet band', async ({ page }) => {
|
||||
await page.goto('/en');
|
||||
await page.goto('/en/system');
|
||||
|
||||
await expect(page.locator('#pricing [data-plan="launch"]')).toHaveAttribute(
|
||||
'data-recommended',
|
||||
|
||||
@@ -21,7 +21,7 @@ const localeData = {
|
||||
for (const locale of ['en', 'fr', 'ar'] as const) {
|
||||
test(`${locale} demo flow validates and succeeds through the local adapter`, async ({ page }) => {
|
||||
const labels = localeData[locale];
|
||||
await page.goto(`/${locale}`);
|
||||
await page.goto(`/${locale}/system`);
|
||||
await page.locator('main button[data-demo-source="hero"]').click();
|
||||
|
||||
const dialog = page.getByRole('dialog', { name: labels.title });
|
||||
@@ -41,12 +41,12 @@ for (const locale of ['en', 'fr', 'ar'] as const) {
|
||||
.click();
|
||||
|
||||
await expect(dialog.getByRole('heading', { name: labels.success })).toBeVisible();
|
||||
await expect(page).toHaveURL(new RegExp(`/${locale}$`));
|
||||
await expect(page).toHaveURL(new RegExp(`/${locale}/system$`));
|
||||
});
|
||||
}
|
||||
|
||||
test('duplicate clicks create only one request while submission is pending', async ({ page }) => {
|
||||
await page.goto('/en');
|
||||
await page.goto('/en/system');
|
||||
await page.locator('main button[data-demo-source="hero"]').click();
|
||||
const dialog = page.getByRole('dialog', { name: 'Book a product demo' });
|
||||
await dialog.locator('#fullName').fill('Avery Example');
|
||||
@@ -59,7 +59,7 @@ test('duplicate clicks create only one request while submission is pending', asy
|
||||
});
|
||||
|
||||
test('demo form does not place personal data in the URL or analytics sink', async ({ page }) => {
|
||||
await page.goto('/en');
|
||||
await page.goto('/en/system');
|
||||
await page.locator('main button[data-demo-source="hero"]').click();
|
||||
const dialog = page.getByRole('dialog', { name: 'Book a product demo' });
|
||||
await dialog.locator('#fullName').fill('Private Example');
|
||||
|
||||
@@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test';
|
||||
test.use({ viewport: { width: 390, height: 844 } });
|
||||
|
||||
test('mobile navigation opens modally, closes with Escape, and returns focus', async ({ page }) => {
|
||||
await page.goto('/en');
|
||||
await page.goto('/en/system');
|
||||
const trigger = page.getByRole('button', { name: 'Open navigation menu' });
|
||||
await trigger.focus();
|
||||
await trigger.click();
|
||||
@@ -19,7 +19,7 @@ test('mobile navigation opens modally, closes with Escape, and returns focus', a
|
||||
});
|
||||
|
||||
test('skip navigation moves focus to the main landmark', async ({ page }) => {
|
||||
await page.goto('/en');
|
||||
await page.goto('/en/system');
|
||||
await page.keyboard.press('Tab');
|
||||
const skip = page.getByRole('link', { name: 'Skip to main content' });
|
||||
await expect(skip).toBeFocused();
|
||||
@@ -32,7 +32,7 @@ test.describe('primary navigation visibility', () => {
|
||||
test.use({ viewport: { width: 1024, height: 768 } });
|
||||
|
||||
test('keeps the complete navbar visible at common laptop widths', async ({ page }) => {
|
||||
await page.goto('/en');
|
||||
await page.goto('/en/system');
|
||||
|
||||
const navigation = page.getByRole('navigation', { name: 'Primary navigation' }).first();
|
||||
await expect(navigation).toBeVisible();
|
||||
@@ -47,7 +47,7 @@ test.describe('mobile primary navigation destinations', () => {
|
||||
test.use({ viewport: { width: 390, height: 844 } });
|
||||
|
||||
test('keeps every navbar destination in the mobile drawer', async ({ page }) => {
|
||||
await page.goto('/en');
|
||||
await page.goto('/en/system');
|
||||
await page.getByRole('button', { name: 'Open navigation menu' }).click();
|
||||
|
||||
const dialog = page.getByRole('dialog', { name: 'Primary navigation' });
|
||||
|
||||
@@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test';
|
||||
test('server shell remains readable with JavaScript disabled', async ({ browser }) => {
|
||||
const context = await browser.newContext({ javaScriptEnabled: false, colorScheme: 'dark' });
|
||||
const page = await context.newPage();
|
||||
await page.goto('/ar');
|
||||
await page.goto('/ar/dark');
|
||||
await expect(page.locator('html')).toHaveAttribute('lang', 'ar');
|
||||
await expect(page.locator('html')).toHaveAttribute('dir', 'rtl');
|
||||
await expect(page.locator('h1')).toBeVisible();
|
||||
|
||||
@@ -6,7 +6,7 @@ for (const locale of ['en', 'fr', 'ar'] as const) {
|
||||
for (const width of widths) {
|
||||
test(`${locale} has no horizontal overflow at ${width}px`, async ({ page }) => {
|
||||
await page.setViewportSize({ width, height: 900 });
|
||||
await page.goto(`/${locale}`);
|
||||
await page.goto(`/${locale}/system`);
|
||||
const overflow = await page.evaluate(
|
||||
() => document.documentElement.scrollWidth - document.documentElement.clientWidth,
|
||||
);
|
||||
@@ -19,6 +19,6 @@ test('shell remains operable at a 320 CSS pixel reflow equivalent to 200 percent
|
||||
page,
|
||||
}) => {
|
||||
await page.setViewportSize({ width: 640, height: 800 });
|
||||
await page.goto('/fr');
|
||||
await page.goto('/fr/system');
|
||||
await expect(page.getByRole('button', { name: 'Ouvrir le menu de navigation' })).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -6,14 +6,14 @@ for (const scenario of [
|
||||
{ locale: 'ar', dir: 'rtl', title: /RentalDriveGo/ },
|
||||
] as const) {
|
||||
test(`${scenario.locale} shell is server rendered`, async ({ page, request }) => {
|
||||
const raw = await request.get(`/${scenario.locale}`);
|
||||
const raw = await request.get(`/${scenario.locale}/system`);
|
||||
const html = await raw.text();
|
||||
expect(raw.status()).toBe(200);
|
||||
expect(html).toMatch(
|
||||
new RegExp(`<html[^>]*lang=\"${scenario.locale}\"[^>]*dir=\"${scenario.dir}\"`),
|
||||
);
|
||||
|
||||
const response = await page.goto(`/${scenario.locale}`);
|
||||
const response = await page.goto(`/${scenario.locale}/system`);
|
||||
expect(response?.status()).toBe(200);
|
||||
await expect(page.locator('html')).toHaveAttribute('lang', scenario.locale);
|
||||
await expect(page.locator('html')).toHaveAttribute('dir', scenario.dir);
|
||||
@@ -23,7 +23,7 @@ for (const scenario of [
|
||||
await expect(page.locator('footer')).toBeVisible();
|
||||
await expect(page.locator('link[rel="canonical"]')).toHaveAttribute(
|
||||
'href',
|
||||
new RegExp(`/${scenario.locale}$`),
|
||||
new RegExp(`/${scenario.locale}/system$`),
|
||||
);
|
||||
for (const language of ['en', 'fr', 'ar', 'x-default']) {
|
||||
await expect(page.locator(`link[rel="alternate"][hreflang="${language}"]`)).toHaveCount(1);
|
||||
@@ -41,18 +41,18 @@ test('root locale detection honors Accept-Language', async ({ browser }) => {
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto('/');
|
||||
await expect(page).toHaveURL(/\/ar$/);
|
||||
await expect(page).toHaveURL(/\/ar\/system$/);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
test('locale switching keeps the semantic route and approved campaign state', async ({ page }) => {
|
||||
await page.goto('/en/privacy?utm_source=qa&email=drop-me#ignored');
|
||||
await page.goto('/en/system/privacy?utm_source=qa&email=drop-me#ignored');
|
||||
await page.getByLabel('Language').first().selectOption('fr');
|
||||
await expect(page).toHaveURL('/fr/confidentialite?utm_source=qa');
|
||||
await expect(page).toHaveURL('/fr/system/confidentialite?utm_source=qa');
|
||||
});
|
||||
|
||||
test('pending route shells are localized and noindex', async ({ page }) => {
|
||||
await page.goto('/fr/confidentialite');
|
||||
await page.goto('/fr/system/confidentialite');
|
||||
await expect(page.locator('h1')).toContainText('Confidentialité');
|
||||
await expect(page.locator('meta[name="robots"]')).toHaveAttribute('content', /noindex/);
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test';
|
||||
for (const locale of ['fr', 'ar'] as const) {
|
||||
test(`${locale} shell tolerates long content expansion`, async ({ page }) => {
|
||||
await page.setViewportSize({ width: 390, height: 900 });
|
||||
await page.goto(`/${locale}`);
|
||||
await page.goto(`/${locale}/system`);
|
||||
await page
|
||||
.locator('main article h3')
|
||||
.first()
|
||||
|
||||
@@ -14,7 +14,7 @@ test('explicit dark theme is present on the server-rendered root', async ({ brow
|
||||
},
|
||||
]);
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto('/en');
|
||||
const response = await page.goto('/en/dark');
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme-preference', 'dark');
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
|
||||
const csp = response?.headers()['content-security-policy'] ?? '';
|
||||
@@ -28,7 +28,7 @@ test('explicit dark theme is present on the server-rendered root', async ({ brow
|
||||
test('system preference resolves before interaction', async ({ browser }) => {
|
||||
const context = await browser.newContext({ colorScheme: 'dark' });
|
||||
const page = await context.newPage();
|
||||
await page.goto('/en');
|
||||
await page.goto('/en/system');
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme-preference', 'system');
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
|
||||
await context.close();
|
||||
@@ -37,7 +37,7 @@ test('system preference resolves before interaction', async ({ browser }) => {
|
||||
test('system follows operating-system changes while explicit preferences ignore them', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/en');
|
||||
await page.goto('/en/system');
|
||||
const selector = page.getByLabel('Theme').first();
|
||||
|
||||
await selector.selectOption('system');
|
||||
@@ -47,6 +47,7 @@ test('system follows operating-system changes while explicit preferences ignore
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme', 'light');
|
||||
|
||||
await selector.selectOption('dark');
|
||||
await expect(page).toHaveURL('/en/dark');
|
||||
await page.emulateMedia({ colorScheme: 'light' });
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme-preference', 'dark');
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
|
||||
|
||||
Reference in New Issue
Block a user