From bedf2547f0f9720a88f2b5b56fcd8970117ca9a6 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Thu, 11 Jun 2026 12:16:42 -0700 Subject: [PATCH 1/4] feat(landing): full-page scroll-reactive 3D background + mobile fixes + snapshot tests Landing page overhaul: - Replace the two isolated hero/CTA canvases with ONE persistent full-page 3D scene (position: sticky) whose camera dollies down through a vertical column of frosted-glass panels as the page scrolls, framing a glowing core at both the hero (top) and CTA (bottom). Driven by a scroll-progress ref so scrolling never re-renders React. - Cohesive dark theme: content surfaces are now SOLID dark panels (opaque, not see-through) so cards/accordions stay crisp, while the 3D shows through the open areas (hero, CTA, section gaps). - Mobile: remove the floating scroll-cue arrow that collided with the stats card; the stats band no longer overlaps the hero on small screens. - Hero CTA row: "Take the tour" gains a PlayCircleOutline icon and a new "Scroll to explore" text button (bouncing ArrowDownward) replaces the arrow. - Honors prefers-reduced-motion and keeps the WebGL/low-power fallbacks. Testing: - Add comprehensive Jest snapshot coverage (11 snapshots) for the chrome, static pages, auth pages, and the redesigned LandingPage (WebGL hero and WebAuthn helpers mocked for determinism). - Add jsdom polyfills (matchMedia, Intersection/Resize observers, scroll helpers) in setupTests and a renderWithRouter test helper. All 46 tests / 11 snapshots pass. Co-Authored-By: Claude Opus 4.8 (1M context) --- frontend/src/__tests__/07_snapshots.test.js | 116 + .../__snapshots__/07_snapshots.test.js.snap | 5655 +++++++++++++++++ .../src/components/three/HeroExperience.js | 151 +- frontend/src/pages/LandingPage.js | 311 +- frontend/src/setupTests.js | 54 + frontend/src/test-utils.js | 24 + 6 files changed, 6062 insertions(+), 249 deletions(-) create mode 100644 frontend/src/__tests__/07_snapshots.test.js create mode 100644 frontend/src/__tests__/__snapshots__/07_snapshots.test.js.snap create mode 100644 frontend/src/test-utils.js diff --git a/frontend/src/__tests__/07_snapshots.test.js b/frontend/src/__tests__/07_snapshots.test.js new file mode 100644 index 0000000..a8f6164 --- /dev/null +++ b/frontend/src/__tests__/07_snapshots.test.js @@ -0,0 +1,116 @@ +/** + * Snapshot coverage for the presentational surface of the app. + * + * These lock in the rendered DOM for the static pages, the auth pages, the + * chrome (Navbar/Footer/Spinner), and the redesigned 3D LandingPage. Heavy + * runtime-only widgets (upload/chat modals that pull in axios + firebase + + * file APIs) are intentionally excluded — they have no deterministic initial + * render to snapshot. + * + * To intentionally update a snapshot after a UI change: `npx jest -u`. + */ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { MemoryRouter } from "react-router-dom"; +import { renderWithRouter } from "../test-utils"; + +// The hero's WebGL scene can't run in jsdom; swap in a deterministic stub so +// LandingPage snapshots are stable and don't depend on three.js / a GPU. +jest.mock("../components/three/HeroExperience", () => ({ + __esModule: true, + default: () =>
, +})); + +// WebAuthn/passkey helpers touch browser-only APIs missing in jsdom; stub them +// so the auth pages render their default (no-passkey) state. +jest.mock("../utils/passkeys", () => ({ + __esModule: true, + isPasskeySupported: () => false, + isPlatformAuthenticatorAvailable: async () => false, + authenticateWithPasskey: jest.fn(), + registerPasskey: jest.fn(), +})); + +import Spinner from "../components/Spinner"; +import Footer from "../components/Footer"; +import Navbar from "../components/Navbar"; +import NotFoundPage from "../pages/NotFoundPage"; +import PrivacyPolicy from "../pages/PrivacyPolicy"; +import TermsOfService from "../pages/TermsOfService"; +import HowToUse from "../pages/HowToUse"; +import Login from "../pages/Login"; +import Register from "../pages/Register"; +import ForgotPassword from "../pages/ForgotPassword"; +import LandingPage from "../pages/LandingPage"; + +describe("Component snapshots", () => { + it("Spinner renders consistently", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("Footer renders consistently", () => { + const { container } = renderWithRouter(