Skip to content

Conversation

@danielmarv
Copy link
Member

This pull request introduces a comprehensive suite of end-to-end (E2E) Playwright tests to verify internationalization (i18n), navigation, accessibility, SEO, and performance across all major pages in both English and German locales. Additionally, it makes minor improvements to the layout files to better support hydration and clarify the i18n layout delegation. These changes ensure robust testing coverage for user experience, accessibility, and technical best practices.

E2E Testing Enhancements:

  • Added Playwright E2E tests for all major pages (home, about, contact, posts, support-care) in both English and German, verifying navigation, content visibility, and correct locale handling. [1] src/app/[locale]/layout.tsxL77-R81, [2] [3] [4]
  • Implemented locale switching tests to ensure language changes persist across navigation and maintain page context.
  • Introduced accessibility tests to check for correct heading hierarchy, ARIA landmarks, alt text on images, keyboard accessibility, and correct lang attributes for both locales.
  • Added SEO tests to check for presence and validity of title tags, meta descriptions, Open Graph tags, canonical URLs, and the existence of robots.txt and sitemap.xml.
  • Added performance tests to ensure fast load times, absence of console errors, and no broken resources for both locales.

Layout Improvements:

  • Updated src/app/[locale]/layout.tsx to add suppressHydrationWarning to <html> and <body> for improved hydration handling in Next.js. (src/app/[locale]/layout.tsxL77-R81)
  • Cleaned up src/app/layout.tsx by removing unused font and component imports, and clarified comments to indicate that root layout delegates to locale-specific layout for i18n. [1] [2]

@danielmarv danielmarv self-assigned this Feb 1, 2026
Copilot AI review requested due to automatic review settings February 1, 2026 17:06
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a broad Playwright E2E test suite covering core pages in English/German locales (navigation, a11y, SEO, performance) and tweaks Next.js layouts to better support i18n/hydration, plus a small TS typing and gitignore update.

Changes:

  • Added Playwright E2E specs for page coverage (home/about/contact/posts/support-care), locale switching, accessibility checks, SEO checks, and basic performance checks.
  • Updated Next.js app layouts to delegate i18n layout responsibilities and added suppressHydrationWarning on locale layout <html>/<body>.
  • Added a TS declaration for importing .css and ignored Playwright test-results/ output.

Reviewed changes

Copilot reviewed 11 out of 13 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
tests/e2e/about.spec.ts Adds E2E coverage for About page across locales.
tests/e2e/accessibility.spec.ts Adds basic accessibility assertions (headings/landmarks/alts/keyboard/lang).
tests/e2e/contact.spec.ts Adds E2E coverage for Contact page and nav pathing.
tests/e2e/home.spec.ts Adds E2E coverage for home page rendering/meta/responsiveness.
tests/e2e/locale-switching.spec.ts Adds locale switching and locale persistence checks.
tests/e2e/performance.spec.ts Adds basic perf/console/broken-resource checks.
tests/e2e/posts.spec.ts Adds E2E coverage for posts listing and navigating to a post.
tests/e2e/seo.spec.ts Adds SEO assertions for title/meta/OG/canonical + robots/sitemap availability.
tests/e2e/support-care.spec.ts Adds E2E coverage for Support Care routes across locales.
src/types/css.d.ts Adds TS module declaration for importing .css.
src/app/layout.tsx Refactors root layout to “delegate” to locale layout and removes unused imports.
src/app/[locale]/layout.tsx Adds suppressHydrationWarning on <html>/<body>.
.gitignore Ignores Playwright test-results/.
Comments suppressed due to low confidence (1)

src/app/layout.tsx:34

  • src/app/layout.tsx is the Next.js root layout but it no longer renders <html>/<body> (it returns children directly). Next’s app router expects the root layout to include these tags; otherwise next build typically fails with a missing <html>/<body> error and metadata injection can break. Restore <html><body> here and move locale-specific lang handling into a supported pattern (e.g., make [locale]/layout a non-root wrapper, or restructure so the true root layout owns <html>/<body>).
// Root layout delegates to [locale]/layout.tsx for i18n support
// The locale layout provides html/body with proper lang attributes
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return children;
}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +27 to +30
if (metaDescription) {
expect(metaDescription.length).toBeGreaterThan(50);
expect(metaDescription.length).toBeLessThan(200); // Allow slightly longer descriptions
}
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test name says the page “has meta description”, but the assertions are skipped when content is missing. That makes the test a false positive for missing meta descriptions. Either (a) require the meta description to exist (and validate length), or (b) rename the test to indicate it’s optional.

Suggested change
if (metaDescription) {
expect(metaDescription.length).toBeGreaterThan(50);
expect(metaDescription.length).toBeLessThan(200); // Allow slightly longer descriptions
}
expect(metaDescription).toBeTruthy();
const descriptionLength = (metaDescription ?? '').length;
expect(descriptionLength).toBeGreaterThan(50);
expect(descriptionLength).toBeLessThan(200); // Allow slightly longer descriptions

Copilot uses AI. Check for mistakes.
Comment on lines +52 to +55
if (await canonical.count() > 0) {
const href = await canonical.getAttribute('href');
expect(href).toBeTruthy();
}
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, this test is named “has canonical URL” but it passes when the canonical link tag is absent due to the if (await canonical.count() > 0) guard. Decide whether canonical is required; if yes, assert existence + non-empty href, otherwise rename the test.

Suggested change
if (await canonical.count() > 0) {
const href = await canonical.getAttribute('href');
expect(href).toBeTruthy();
}
await expect(canonical).toHaveCount(1);
const href = await canonical.getAttribute('href');
expect(href).toBeTruthy();

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +19
const startTime = Date.now();

await page.goto(localePath(locale), { waitUntil: 'domcontentloaded' });

const loadTime = Date.now() - startTime;

// Page should load within 5 seconds
expect(loadTime).toBeLessThan(5000);
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This “loads within reasonable time” check is running against pnpm dev (see playwright.config.ts), so timings will vary widely by machine/CI load and aren’t representative of production performance. Consider running performance assertions against a production build (next build + next start) or relaxing/removing this strict wall-clock timing check to avoid flaky CI failures.

Suggested change
const startTime = Date.now();
await page.goto(localePath(locale), { waitUntil: 'domcontentloaded' });
const loadTime = Date.now() - startTime;
// Page should load within 5 seconds
expect(loadTime).toBeLessThan(5000);
await page.goto(localePath(locale), { waitUntil: 'domcontentloaded' });
// Basic sanity check that the page has rendered something; avoid strict wall-clock timing
await expect(page).toHaveTitle(/./);

Copilot uses AI. Check for mistakes.
Comment on lines +37 to +48
test(`page has no broken resources for ${locale}`, async ({ page }) => {
const failedRequests: string[] = [];

page.on('requestfailed', (request) => {
failedRequests.push(request.url());
});

await page.goto(localePath(locale), { waitUntil: 'networkidle' });

// Should not have failed resource loads
expect(failedRequests.length).toBe(0);
});
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requestfailed will include failures for third-party resources (e.g., the Iconify CDN script included in src/app/[locale]/layout.tsx), ad blockers, transient network issues, etc. As written, this will be flaky in CI. Filter failures to same-origin requests (or explicitly allowlist known external dependencies) before asserting failedRequests.length === 0.

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +32
const languageSwitcher = page.locator('[data-locale-switcher], a[href*="/de/about"], button[aria-label*="Deutsch"]').first();

if (await languageSwitcher.count() > 0) {
await languageSwitcher.click();

// Should be on about page in German
await expect(page).toHaveURL(/\/de\/about/);
}
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as above: this test silently succeeds when the locale switcher isn’t found due to the if (await languageSwitcher.count() > 0) guard. If switching locale is expected functionality, assert the switcher exists and fail the test when it doesn’t.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +55
if (await aboutLink.count() > 0) {
await aboutLink.click();

// Should still be in German
await expect(page).toHaveURL(/\/de\/about/);
}

// Navigate to contact page
const contactLink = page.locator('nav a[href*="contact"]').first();
if (await contactLink.count() > 0) {
await contactLink.click();

// Should still be in German
await expect(page).toHaveURL(/\/de\/contact/);
}
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This navigation assertion is conditional on aboutLink.count() > 0, so a missing/incorrect About link won’t fail the test (false positive). If locale persistence across navigation is required, assert the link exists/visible before clicking.

Suggested change
if (await aboutLink.count() > 0) {
await aboutLink.click();
// Should still be in German
await expect(page).toHaveURL(/\/de\/about/);
}
// Navigate to contact page
const contactLink = page.locator('nav a[href*="contact"]').first();
if (await contactLink.count() > 0) {
await contactLink.click();
// Should still be in German
await expect(page).toHaveURL(/\/de\/contact/);
}
await expect(aboutLink).toBeVisible();
await aboutLink.click();
// Should still be in German
await expect(page).toHaveURL(/\/de\/about/);
// Navigate to contact page
const contactLink = page.locator('nav a[href*="contact"]').first();
await expect(contactLink).toBeVisible();
await contactLink.click();
// Should still be in German
await expect(page).toHaveURL(/\/de\/contact/);

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +55
const contactLink = page.locator('nav a[href*="contact"]').first();
if (await contactLink.count() > 0) {
await contactLink.click();

// Should still be in German
await expect(page).toHaveURL(/\/de\/contact/);
}
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue: guarding the click with if (await contactLink.count() > 0) allows the test to pass even if the Contact link is missing/broken. Make the link required (expect it visible) so the test actually validates locale persistence.

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +17
const languageSwitcher = page.locator('[data-locale-switcher], a[href*="/de/"], button[aria-label*="Deutsch"], button[aria-label*="German"]').first();

if (await languageSwitcher.count() > 0) {
await languageSwitcher.click();

// Should switch to German
await expect(page).toHaveURL(/\/de\//);
}
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test can pass without actually verifying locale switching because it only clicks/asserts if a candidate switcher is found (if (await languageSwitcher.count() > 0)). If the switcher disappears/regresses, the test becomes a false positive. Make the switcher required (assert it exists/visible) and then perform the click + URL assertion.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +43
if (await contactLink.count() > 0) {
await contactLink.click();

// Should navigate to contact page
await expect(page).toHaveURL(/\/contact/);

// Should maintain locale
if (locale === 'de') {
await expect(page).toHaveURL(/\/de\/contact/);
}
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This navigation test can pass without validating anything because it only clicks/asserts when contactLink.count() > 0. If the nav link is expected to exist, remove the guard and assert the link is present/visible before clicking so regressions are caught.

Suggested change
if (await contactLink.count() > 0) {
await contactLink.click();
// Should navigate to contact page
await expect(page).toHaveURL(/\/contact/);
// Should maintain locale
if (locale === 'de') {
await expect(page).toHaveURL(/\/de\/contact/);
}
// Contact link is expected to exist and be visible
await expect(contactLink).toBeVisible();
await contactLink.click();
// Should navigate to contact page
await expect(page).toHaveURL(/\/contact/);
// Should maintain locale
if (locale === 'de') {
await expect(page).toHaveURL(/\/de\/contact/);

Copilot uses AI. Check for mistakes.
Signed-off-by: Daniel Ntege <danientege785@gmail.com>
Signed-off-by: Daniel Ntege <danientege785@gmail.com>
@danielmarv
Copy link
Member Author

@copilot open a new pull request to apply changes based on the comments in this thread

Copy link

Copilot AI commented Feb 1, 2026

@danielmarv I've opened a new pull request, #266, to work on those changes. Once the pull request is ready, I'll request review from you.

@danielmarv danielmarv merged commit 5d3d3ea into next-js-migration Feb 1, 2026
2 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Migration to NextJS Feb 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants