Skip to content

[STU-175] Add responsive layout smoke tests for mobile and tablet viewports#40

Merged
BAWES merged 1 commit into
mainfrom
feature/STU-175-responsive-layout-smoke-tests
May 21, 2026
Merged

[STU-175] Add responsive layout smoke tests for mobile and tablet viewports#40
BAWES merged 1 commit into
mainfrom
feature/STU-175-responsive-layout-smoke-tests

Conversation

@BAWES
Copy link
Copy Markdown
Owner

@BAWES BAWES commented May 21, 2026

Summary

  • Adds 20 responsive layout smoke tests covering landing (/), login (/login), and candidate portal (/candidate) pages
  • Tests run at mobile (390×844) and tablet (768×1024) viewports
  • Adds a tablet project to Playwright config (768×1024, 2x device scale)
  • Assertions: no horizontal overflow, readable text (no zero-height nodes), navigation adaptation (mobile tab bar vs. sidebar rail), and tap target size diagnostics

Test plan

  • All 20 tests pass against chromium project
  • Tests discovered in all 3 Playwright projects (chromium, mobile, tablet)
  • Public page tests (landing, login) — no DB needed
  • Candidate portal tests — authenticated with fixture DB records
  • Type check passes, lint clean
  • npm run test:validate — 76/76 still pass

🤖 Generated with Claude Code

Summary by CodeRabbit

Tests

  • Added automated responsive design testing for mobile and tablet devices, including validation for text readability, touch target accessibility, and layout overflow
  • Expanded Playwright test configuration with tablet viewport support

Review Change Stack

…ts [STU-175]

20 tests covering 3 pages (landing, login, candidate portal) across
mobile (390x844) and tablet (768x1024) viewports with assertions for
horizontal overflow, readable text, tap target sizing, and navigation
adaptation (mobile tab bar vs sidebar).

Also adds a tablet project (768x1024, 2x scale) to Playwright config.

Tests: added responsive smoke test suite (e2e/smoke/responsive.spec.ts)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
studenthub-next Error Error May 21, 2026 9:32pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Walkthrough

This PR adds comprehensive responsive smoke testing to the Playwright suite. It introduces a new tablet device configuration (768×1024), creates reusable validation helpers for detecting overflow and tap-target issues, and implements parallel test suites covering mobile and tablet viewports for unauthenticated and authenticated pages.

Changes

Responsive Smoke Testing

Layer / File(s) Summary
Tablet device configuration
playwright.config.ts
Adds a tablet project with 768×1024 viewport and 2× device scale factor, extending Desktop Chrome settings for tablet emulation.
Responsive validation helpers
e2e/smoke/responsive.spec.ts
Defines helpers to detect horizontal overflow, verify 44×44px minimum tap targets on interactive elements, validate text readability by walking text nodes and checking client rects, and create authenticated browser contexts with candidate session cookies.
Mobile responsive test suite
e2e/smoke/responsive.spec.ts
Tests landing page, login page, and authenticated candidate portal at 390×844 viewport. Verifies page visibility, no horizontal overflow, readable text, minimum tap targets, and responsive navigation UI (tab bar shown at mobile).
Tablet responsive test suite
e2e/smoke/responsive.spec.ts
Tests the same pages at 768×1024 tablet viewport with adjusted navigation expectations for tablet breakpoint. Closes auth context after tests and includes global afterAll hook to disconnect Prisma.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • BAWES/studenthub-codex#23: Changed responsive navigation UI breakpoint to @media (max-width: 768px) to show bottom tab bar on mobile—this PR's tablet tests validate that exact behavior change.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the summary and test plan sections well, matching the template structure. However, it omits the Type checklist, Changes list, and Checklist items (branch naming, commits, build checks), which are significant template sections. Add the missing sections: specify the PR type (Feature), list key changes, and complete the checklist items including branch naming, commit format, build/test verification, and any applicable code standards checks.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding responsive layout smoke tests for mobile and tablet viewports, which is clearly reflected in both added files (e2e/smoke/responsive.spec.ts and playwright.config.ts tablet project).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/STU-175-responsive-layout-smoke-tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@e2e/smoke/responsive.spec.ts`:
- Line 2: Replace the brittle relative import in responsive.spec.ts by using the
project path alias: change the import that currently brings in getFixtures and
disconnectPrisma from "../fixtures/auth" to use the "`@/fixtures/auth`" alias so
internal fixture imports follow the TypeScript import-path convention and remain
stable across file moves; update the import statement that references
getFixtures and disconnectPrisma accordingly.
- Around line 290-307: The test "candidate portal shows floating tab bar, hides
sidebar" currently accepts either nav being visible; change the assertions to
explicitly require the mobile tab bar to be visible and the workspace
rail/sidebar to be hidden: use the existing locators tabBar (".mobileTabBar")
and rail (".workspaceRail") and replace the combined expect with two explicit
checks—assert tabBar is visible (e.g., expect(tabBar).toBeVisible with an
appropriate timeout) and assert rail is not visible/hidden (e.g.,
expect(rail).toBeHidden or verify rail.isVisible() resolves false) so the test
fails if both are visible or the sidebar is shown. Ensure you keep
createAuthedPage and tabletViewport setup and preserve error handling for
locator visibility checks.
- Around line 121-124: The test hard-codes the auth cookie domain to "127.0.0.1"
(the cookie named "studenthub_next_session" using candidate.cookie), which
breaks when PLAYWRIGHT_BASE_URL points to a different host; change the
cookie-setting logic to derive the domain from the test's base URL (e.g. use
PLAYWRIGHT_BASE_URL or the page/context URL) or omit the domain so the cookie
applies to the current host instead of always using "127.0.0.1".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c3934e3-842e-4b8e-a569-f9391dfc2ba2

📥 Commits

Reviewing files that changed from the base of the PR and between ab0895d and c2289ce.

📒 Files selected for processing (2)
  • e2e/smoke/responsive.spec.ts
  • playwright.config.ts

@@ -0,0 +1,340 @@
import { test, expect, type Page, type Browser } from "@playwright/test";
import { getFixtures, disconnectPrisma } from "../fixtures/auth";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Use the @/ alias for internal fixture imports.

This relative import violates the TypeScript import-path convention and is brittle on file moves.

Suggested change
-import { getFixtures, disconnectPrisma } from "../fixtures/auth";
+import { getFixtures, disconnectPrisma } from "`@/e2e/fixtures/auth`";

As per coding guidelines, "Use @/ path alias for all internal imports in TypeScript files".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { getFixtures, disconnectPrisma } from "../fixtures/auth";
import { getFixtures, disconnectPrisma } from "`@/e2e/fixtures/auth`";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/smoke/responsive.spec.ts` at line 2, Replace the brittle relative import
in responsive.spec.ts by using the project path alias: change the import that
currently brings in getFixtures and disconnectPrisma from "../fixtures/auth" to
use the "`@/fixtures/auth`" alias so internal fixture imports follow the
TypeScript import-path convention and remain stable across file moves; update
the import statement that references getFixtures and disconnectPrisma
accordingly.

Comment on lines +121 to +124
name: "studenthub_next_session",
value: candidate.cookie,
domain: "127.0.0.1",
path: "/",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid hard-coding the auth cookie domain.

Line 123 binds auth to 127.0.0.1, so authenticated tests can fail when PLAYWRIGHT_BASE_URL uses another host.

Suggested change
     {
       name: "studenthub_next_session",
       value: candidate.cookie,
-      domain: "127.0.0.1",
-      path: "/",
+      url: process.env.PLAYWRIGHT_BASE_URL ?? "http://127.0.0.1:3000",
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name: "studenthub_next_session",
value: candidate.cookie,
domain: "127.0.0.1",
path: "/",
name: "studenthub_next_session",
value: candidate.cookie,
url: process.env.PLAYWRIGHT_BASE_URL ?? "http://127.0.0.1:3000",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/smoke/responsive.spec.ts` around lines 121 - 124, The test hard-codes the
auth cookie domain to "127.0.0.1" (the cookie named "studenthub_next_session"
using candidate.cookie), which breaks when PLAYWRIGHT_BASE_URL points to a
different host; change the cookie-setting logic to derive the domain from the
test's base URL (e.g. use PLAYWRIGHT_BASE_URL or the page/context URL) or omit
the domain so the cookie applies to the current host instead of always using
"127.0.0.1".

Comment on lines +290 to +307
test("candidate portal shows floating tab bar, hides sidebar", async ({ browser }) => {
const { page, context } = await createAuthedPage(browser, tabletViewport);
await page.goto("/candidate");
await expect(page.locator('text="Readiness"')).toBeVisible({ timeout: 15000 });

// At 768px, the mobileTabBar should be visible (full-width bottom bar)
// and workspaceRail hidden (max-width: 768px rule triggers display:none)
const tabBar = page.locator(".mobileTabBar").first();
const tabBarVisible = await tabBar.isVisible().catch(() => false);

const rail = page.locator(".workspaceRail").first();
const railVisible = await rail.isVisible().catch(() => false);

// At least one nav element must be visible
expect(
tabBarVisible || railVisible,
"expected either mobileTabBar or workspaceRail to be visible",
).toBe(true);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Tablet nav assertion does not validate the stated behavior.

The test says sidebar is hidden, but it currently passes if either nav is visible. A regression where both are visible would not fail.

Suggested change
-    const tabBarVisible = await tabBar.isVisible().catch(() => false);
-
     const rail = page.locator(".workspaceRail").first();
-    const railVisible = await rail.isVisible().catch(() => false);
-
-    // At least one nav element must be visible
-    expect(
-      tabBarVisible || railVisible,
-      "expected either mobileTabBar or workspaceRail to be visible",
-    ).toBe(true);
+    await expect(tabBar).toBeVisible();
+    await expect(rail).not.toBeVisible();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/smoke/responsive.spec.ts` around lines 290 - 307, The test "candidate
portal shows floating tab bar, hides sidebar" currently accepts either nav being
visible; change the assertions to explicitly require the mobile tab bar to be
visible and the workspace rail/sidebar to be hidden: use the existing locators
tabBar (".mobileTabBar") and rail (".workspaceRail") and replace the combined
expect with two explicit checks—assert tabBar is visible (e.g.,
expect(tabBar).toBeVisible with an appropriate timeout) and assert rail is not
visible/hidden (e.g., expect(rail).toBeHidden or verify rail.isVisible()
resolves false) so the test fails if both are visible or the sidebar is shown.
Ensure you keep createAuthedPage and tabletViewport setup and preserve error
handling for locator visibility checks.

Copy link
Copy Markdown
Owner Author

@BAWES BAWES left a comment

Choose a reason for hiding this comment

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

Staff Engineer Review — APPROVE WITH NOTES

Solid smoke test suite. 20 tests covering 3 pages at 2 breakpoints, with meaningful assertions (overflow, readable text, tap targets, navigation adaptation). Good addition to the test pyramid.

What's good

  • Test helpers (assertNoHorizontalOverflow, assertTextReadable, assertTapTargets) are reusable and well-scoped
  • test.describe.configure({ mode: "serial" }) correctly used for candidate portal tests that share the authed fixture
  • disconnectPrisma() in test.afterAll prevents connection leaks
  • Tablet project added cleanly to Playwright config

CodeRabbit findings — disposition

  1. Import path ../fixtures/auth — CodeRabbit suggested @/fixtures/auth. This is incorrect. The @/ path alias maps to src/ (per tsconfig), while the fixtures live at e2e/fixtures/auth.ts. The relative import ../fixtures/auth from e2e/smoke/responsive.spec.ts is the correct path. No change needed.

  2. Hardcoded cookie domain 127.0.0.1 — Valid observation, but the project's baseURL (playwright.config.ts line 11) defaults to http://127.0.0.1:3000, so the two are consistent. If someone overrides PLAYWRIGHT_BASE_URL to a different host, the cookie domain would need to match. Low severity; acceptable for now.

  3. Tablet test relaxed assertion — The test at line 290-307 uses expect(tabBarVisible || railVisible) because 768px is the exact breakpoint boundary where CSS @media (max-width: 768px) behavior can vary by browser engine. The relaxed assertion is a pragmatic choice that avoids flaky tests at the boundary. Acceptable.

Verification

  • CI: all green ✓
  • Branch naming: feature/STU-175-*
  • Commit: conventional ✓

@BAWES BAWES merged commit dba370a into main May 21, 2026
8 of 9 checks passed
@BAWES BAWES deleted the feature/STU-175-responsive-layout-smoke-tests branch May 21, 2026 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant