Skip to content

feat(sc-44545): make registration discoverable on login page#3370

Merged
yodem merged 32 commits into
masterfrom
feature/sc-44545/update-login-page-to-handle-anon-la-candidates
Jun 24, 2026
Merged

feat(sc-44545): make registration discoverable on login page#3370
yodem merged 32 commits into
masterfrom
feature/sc-44545/update-login-page-to-handle-anon-la-candidates

Conversation

@yodem

@yodem yodem commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Story

SC-44545 — Update login page to handle anon LA candidates who don't have an account (epic: Library Assistant System and UX)

As part of simplifying the LA promo (logged-out users get a single "Log in to Try" entry point), the login page must make the path to registration hard to miss.

Changes

  • New partial templates/registration/_cross_flow_nav.html — reusable bilingual "prompt + inline link" line (EN/HE), preserves ?next=.
  • login.html — cross-flow link moved directly under the "Log in to Sefaria" title (was an easy-to-miss link at the bottom); old "Create a new account" link removed. Uses Django 6 {% url … as %} + {% include … with %}.
  • static/css/static.css — scoped .registration-prompt styling from the Figma design (node 35:430): prompt --darker-grey, link --sefaria-blue SemiBold underlined, 4px RTL-safe gap. General CSS vars, no hardcoded hex.
  • auth-flow.spec.ts — HAM-A003 updated to select the new "Sign Up" link (scoped to #login).

Copy (source of truth = story): EN Don't have an account? Sign Up · HE אין לכם חשבון? להרשמה.

Design docs

  • Spec: docs/superpowers/specs/2026-06-02-login-cross-flow-nav-design.md
  • Plan: docs/superpowers/plans/2026-06-02-login-cross-flow-nav.md

Figma (source of truth for layout/CSS): https://www.figma.com/design/2WflG98PDhWQ7OKrDkaCPb/Login---Registration-Wireframes?node-id=35-420

Scope

Login page only. SC-44544 (backend auto-enroll into the experiment on auth) is a separate story; this design leaves the partial as a clean seam for it.

Test plan

Preview cauldron (feature mode): https://sc-44545-login.cauldron.sefaria.org

  • EN: prompt under title, "Sign Up" blue/SemiBold/underlined, old bottom link gone
  • HE (RTL): "אין לכם חשבון? להרשמה" renders correctly
  • /login?next=/texts → link href is /register?next=/texts
  • Mobile width
  • register.html links visually unchanged

🤖 Generated with Claude Code

yodem and others added 8 commits June 2, 2026 11:20
Selecting a path filter on a text search (e.g. filtering "מעל" to Psalms)
returned no results and a 500 from /api/search-wrapper. The search page
sends text filters with agg_type=None, relying on the documented contract
that make_filter() supplies the default field ("path") for that query type.

Commit 1252655 ("keyword-search-in-connected-refs") tightened the text
branch from `if type == "text":` to `if type == "text" and agg_type == "path":`
so a new non-path text agg_type (linked_refs) could use a Term filter. That
routed the agg_type=None default into `Term(**{None: agg_key})`, which raises
TypeError: keywords must be strings -> HTTP 500, breaking all text path
filters (both URL-applied and clicked).

Honor the None default again by matching `agg_type in (None, "path")`, which
fixes URL-load, in-app click, and the empty-filter_fields fallback, while
preserving the Term path for linked_refs and all sheet filters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Spec for making the path to registration discoverable on the login page:
reusable Django include partial, placement under the H1, blue+underlined
link styling, EN/HE copy from the story. Documents why a React migration
was rejected for this story and how the design leaves a seam for SC-44544.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…l vars

- Figma (file 2WflG98PDhWQ7OKrDkaCPb, node 35:425) is source of truth for
  layout/styling/CSS + dev-mode notes; story stays authoritative for strings
  (they agree). Pulled exact specs from 35:430 and all three annotation notes.
- Corrected link color to --sefaria-blue (#18345D, Figma Semantic/Text/Link),
  SemiBold 600, underlined, 4px gap; prompt --darker-grey (#575757). No hex.
- CSS uses general vars from color-palette.css in global static.css (no global
  PostCSS; it is CSS-modules-only for the React bundle).
- Django 6.0.4: resolve register URL via {% url ... as %} + {% include with %}.
- Added mobile + Hebrew/RTL coverage to testing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 bite-sized tasks (TDD-adapted for template/CSS): update HAM-A003 E2E
selector, create reusable partial, wire into login.html + remove old link,
add Figma-sourced scoped CSS via general vars, end-to-end visual/behavior
verification (EN/HE/mobile + register regression + next preservation).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gitvelocity-reviewer

Copy link
Copy Markdown

📊 Code Quality Score: 16/100

Base Score 41 × ESF 0.4 = 16.4 → 16

Category Score Factors
🔭 Scope 9/20 6 files across frontend/backend/E2E/docs; single auth subsystem plus search bug fix; no new APIs or DB migrations; two distinct concerns bundled
🏗️ Architecture 8/20 New reusable Django template partial (DRY); explicit SC-44544 forward-compatibility seam; no new service dependencies; clean separation of concerns
⚙️ Implementation 7/20 Simple template extraction and CSS addition; one-line bug fix with clear logic; good use of Django idioms; no algorithmic complexity
⚠️ Risk 5/20 Auth page changes carry inherent risk but changes are purely presentational; easy to revert; pre-existing next parameter handling; E2E test provides regression guard
✅ Quality 10/15 Regression test for bug fix; E2E test updated; thorough spec and implementation plan docs; visual verification checklist; no template unit tests (acceptable); register page regression not in PR
🔒 Perf / Security 2/5 CSS scoping prevents style leakage; margin-inline-start for RTL safety; acknowledged next URL encoding as follow-up; no new security threats

Was this score accurate? 👍 Yes · 👎 No

Scored by GitVelocity · How are scores calculated?

yodem and others added 4 commits June 2, 2026 16:02
…date-login-page-to-handle-anon-la-candidates
Master's url() (django.conf.urls.url, removed in Django 4.0) breaks URLConf
load on Django 6 -> NameError -> /healthz-rollout 500 -> cauldron web pod
never ready. Folded here so this PR's cauldron image builds clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 aims to make the registration path more discoverable from the server-rendered /login page by introducing a reusable cross-flow navigation partial and updating styling/tests accordingly. It also includes additional unrelated backend/search and URL routing changes that expand scope beyond the stated “login page only”.

Changes:

  • Add a reusable bilingual login/register cross-flow nav partial and include it directly under the login title.
  • Update global CSS to style the new prompt/link (and additionally adjust login form layout).
  • Update Playwright mobile E2E selector for the new “Sign Up” link; add a search regression fix + test; refactor some URL patterns to re_path.

Reviewed changes

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

Show a summary per file
File Description
templates/registration/login.html Includes new cross-flow nav under the H1; removes old bottom registration link; tweaks form markup.
templates/registration/_cross_flow_nav.html New reusable EN/HE prompt + inline link partial that preserves ?next=.
static/css/static.css Adds .registration-prompt styles and additional login layout rules.
e2e-tests/mobile web/auth-flow.spec.ts Updates HAM-A003 to click the new “Sign Up” link scoped to #login.
sefaria/helper/search.py Treats agg_type=None as default text path filter to avoid Term(**{None: ...}) crash.
sefaria/helper/tests/search_test.py Adds regression test for default text filter agg type behavior.
sites/sefaria/urls.py Replaces url(...) usages with re_path(...) for static routes.
docs/superpowers/specs/2026-06-02-login-cross-flow-nav-design.md Adds detailed design spec documenting the approach and styling tokens.
docs/superpowers/plans/2026-06-02-login-cross-flow-nav.md Adds implementation plan/checklist for the change.

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

Comment thread static/css/static.css Outdated
Comment thread templates/registration/_cross_flow_nav.html Outdated
Comment thread sites/sefaria/urls.py
Comment on lines 64 to +78
# Static and Semi Static Content
site_urlpatterns = [
url(r'^metrics/?$', reader_views.metrics),
url(r'^digitized-by-sefaria/?$', reader_views.digitized_by_sefaria),
url(r'^(favicon\.ico|apple-touch-icon\.png|favicon\.svg)/?$', reader_views.module_favicon),
url(r'^(?P<filename>site\.webmanifest|manifest\.json)/?$', reader_views.dynamic_manifest),
url(r'^apple-app-site-association/?$', reader_views.apple_app_site_association),
url(r'^\.well-known/apple-app-site-association/?$', reader_views.apple_app_site_association),
url(r'^\.well-known/assetlinks.json/?$', reader_views.android_asset_links_json),
url(r'^llms\.txt/?$', reader_views.serve_llms_txt),
url(r'^(%s)/?$' % "|".join(static_pages), reader_views.serve_static),
url(r'^(%s)/?$' % "|".join(static_pages_by_lang), reader_views.serve_static_by_lang),
url(r'^healthz/?$', reader_views.application_health_api), # this oddly is returning 'alive' when it's not. is k8s jumping in the way?
url(r'^health-check/?$', reader_views.application_health_api),
url(r'^healthz-rollout/?$', reader_views.rollout_health_api),
re_path(r'^metrics/?$', reader_views.metrics),
re_path(r'^digitized-by-sefaria/?$', reader_views.digitized_by_sefaria),
re_path(r'^(favicon\.ico|apple-touch-icon\.png|favicon\.svg)/?$', reader_views.module_favicon),
re_path(r'^(?P<filename>site\.webmanifest|manifest\.json)/?$', reader_views.dynamic_manifest),
re_path(r'^apple-app-site-association/?$', reader_views.apple_app_site_association),
re_path(r'^\.well-known/apple-app-site-association/?$', reader_views.apple_app_site_association),
re_path(r'^\.well-known/assetlinks.json/?$', reader_views.android_asset_links_json),
re_path(r'^llms\.txt/?$', reader_views.serve_llms_txt),
re_path(r'^(%s)/?$' % "|".join(static_pages), reader_views.serve_static),
re_path(r'^(%s)/?$' % "|".join(static_pages_by_lang), reader_views.serve_static_by_lang),
re_path(r'^healthz/?$', reader_views.application_health_api), # this oddly is returning 'alive' when it's not. is k8s jumping in the way?
re_path(r'^health-check/?$', reader_views.application_health_api),
re_path(r'^healthz-rollout/?$', reader_views.rollout_health_api),
yodem and others added 7 commits June 10, 2026 09:27
- Scope #login layout rules under #login.registrationContent so they
  don't leak into other templates that use id="login" (translate_campaign).
- Remove literal space before the cross-flow link so the 4px CSS
  margin (RTL-safe) controls the gap, matching Figma.

Addresses Copilot review comments on PR #3370.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…date-login-page-to-handle-anon-la-candidates
The sc-44603 search path-filter fix was unrelated scope flagged in
review. It was already hotfixed to prod via #3380; the master
back-merge is tracked separately. Reverting these two files to master
keeps PR #3370 login-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Anon users who click the Library Assistant promo CTA were enrolled in
the experiment only after a second 'Join' click post-login. Route the
promo's login/register ?next= through a new /enable-library-assistant
landing view: once auth completes the user lands there, is enrolled in
the experiments whitelist (reusing _set_user_experiments), and is
redirected back to where they were. The assistant then appears on
reload with no extra click.

- New view enable_library_assistant: same-host-validated redirect,
  forwards the register welcome flag, bounces anon hits to login.
- Banner anon CTAs now target the opt-in landing via nested ?next=.
- Normal (non-promo) logins are unaffected.
- Tests cover enroll+redirect, anon->login, offsite-next fallback,
  welcome forwarding, default, and idempotency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Source files shouldn't carry Shortcut story IDs (branch/commit refs are
enough). Replicated on the login-page branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Source files shouldn't carry Shortcut story IDs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Skip Library Assistant enrollment when the browser reports a cross-site
request (Sec-Fetch-Site), so a cross-site <img> can't silently enroll a
logged-in user; the real same-origin post-login redirect is unaffected.
Add tests for protocol-relative next, welcome appended to an existing
query string, and the cross-site skip; assert no duplicate settings row.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@yodem

yodem commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator Author

/ork multi-agent review — verdict: COMMENT (no blockers)

Reviewed security, correctness, accessibility, i18n/RTL, and test correctness.

All clear:

  • next raw-interpolated into the partial's href is not a new risk — identical to the link it replaced; Django host-validates next before it reaches the template, and auto-escaping covers the attribute context.
  • CSS #login.registrationContent ... rules are correctly scoped — they can't leak to translate_campaign.html (which has id and registrationContent on different elements; the compound selector needs both on the same element).
  • The e2e #login-scoped Sign Up selector is robust — the two id="login" divs are mutually exclusive {% if %}/{% else %} branches, so only one renders.
  • Removing the orphaned <td> tags inside <p> is a validity fix, no regression.

Noted (conscious tradeoff, not changed): the partial has no literal whitespace between prompt and link — the 4px gap is the CSS margin-inline-start (per the earlier Copilot review + Figma). A screen-reader/copy-paste purist would prefer a literal space; keeping the margin-based version to honor the Figma 4px spec and the prior review.

🤖 review run via ork-style parallel agents

@yodem yodem requested a review from stevekaplan123 June 15, 2026 07:10
stevekaplan123
stevekaplan123 previously approved these changes Jun 15, 2026
yodem added 2 commits June 15, 2026 16:11
…ndidates' into feature/sc-44544/la-promo-change-opt-in-behavior-for-non
…e-opt-in-behavior-for-non

feat(sc-44544): auto opt-in to Library Assistant after promo-driven auth
stevekaplan123
stevekaplan123 previously approved these changes Jun 16, 2026
yodem and others added 4 commits June 18, 2026 10:12
…nto feature/sc-44545/update-login-page-to-handle-anon-la-candidates
…ndidates' of https://github.com/Sefaria/Sefaria-Project into feature/sc-44545/update-login-page-to-handle-anon-la-candidates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
yodem and others added 4 commits June 21, 2026 15:14
…nto feature/sc-44545/update-login-page-to-handle-anon-la-candidates
…nto feature/sc-44545/update-login-page-to-handle-anon-la-candidates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nto feature/sc-44545/update-login-page-to-handle-anon-la-candidates
@yodem yodem merged commit 06c6080 into master Jun 24, 2026
12 of 13 checks passed
@gitvelocity-reviewer

Copy link
Copy Markdown

📊 Code Quality Score: 34/100

56 × 0.6 (Medium ESF) = 33.6 → 34

Category Score Factors
🔭 Scope 11/20 8 files across frontend/backend/templates/tests; new public URL endpoint; cross-cutting auth flow, banner, CSS; single feature scope without external service integrations
🏗️ Architecture 9/20 New view with clear single responsibility; introduces URL-based opt-in landing pattern; reusable _cross_flow_nav.html partial (DRY improvement); follows existing Django patterns without major shifts
⚙️ Implementation 11/20 URL safety validation (open-redirect protection); cross-site request detection via Sec-Fetch-Site; urllib.parse query string manipulation preserving existing params; idempotency logic; multiple security considerations handled cleanly
⚠️ Risk 8/20 Auth flow changes carry moderate risk; open-redirect protection present and tested; cross-site enrollment prevention; easily reversible; no database migrations or schema changes
✅ Quality 13/15 9 comprehensive test cases covering happy path, anon redirect, offsite next, welcome param, missing next, idempotency, protocol-relative attack, query string merging, cross-site request; E2E test updated; inline comments explain non-obvious decisions; template documents parameters with Figma reference
🔒 Perf / Security 4/5 Explicit open-redirect protection with url_has_allowed_host_and_scheme; protocol-relative URL attack handled; cross-site enrollment prevention via Sec-Fetch-Site; no rate limiting on endpoint

Was this score accurate? 👍 Yes · 👎 No

Scored by GitVelocity · How are scores calculated?

HershelT added a commit that referenced this pull request Jun 30, 2026
HAM-A003 (mobile auth-flow): the login page's registration call-to-action
changed in #3370 — the link is now "Sign Up" ("Don't have an account? Sign
Up", rendered by registration/_cross_flow_nav.html), not "Create a new
account". Update the locator and test title to match.

VBM-002 (Voices /history bookmarking): the Voices /history page crashes on
render (TypeError reading 'trim' in ProfilePic) when the account's history
contains a row for a since-deleted sheet with no ownerName, blanking the
whole page so .savedHistoryList never appears. Add clearReadingHistory() to
VoicesBookmarksPage and call it before seeding: it toggles the account's
reading_history setting off->on via /api/profile/sync, which makes the
server run delete_user_history(exclude_saved=True) — wiping reading history
while preserving /saved. The settings time_stamp must strictly exceed the
stored one (persists across runs on the shared account), so the helper reads
the current stamp and sends strictly-greater values.
NoahBeitler pushed a commit to NoahBeitler/Sefaria-Project that referenced this pull request Jul 5, 2026
…new tagging system (Sefaria#3456)

* Overwrite e2e-tests with origin/playwright-mdl content

* chore: update Playwright configuration

* feat: starting to refractor the self contained MDL tests to align with the rests of the POM tests.

* feat: Enhance module header interactions and language handling

- Improved dropdown interaction by ensuring overlays are dismissed before clicking.
- Added special handling for language selection in dropdowns, utilizing URL-based strategies.
- Updated utility functions to simplify modal and popup dismissals using a click-if-visible approach.
- Refactored language change logic to prioritize cookie setting and URL navigation.
- Updated Playwright configuration to allow dynamic base URLs via environment variables for better test flexibility.

* refactor: streamline modal handling by consolidating hide functions

* refactor: simplify language verification logic in toggleLanguage function

* feat: add tests for trending topics functionality in Voices module

* feat: add VALID_TOPICS constant and improve module button navigation handling in Voices tests

* feat: add cross-module redirect tests for Library to Voices and no-redirect loops in Voices module; update selectors and dependencies

* feat: enhance cross-module redirect tests and improve navigation handling; add gotoOrThrow utility for better error handling

* feat: implement cross-subdomain cookie handling for improved storage state management

* feat: rename browser test configurations for clarity and consistency; update test directories for modularization tests

* feat: refactor module URLs to use environment variable for sandbox domain; update tests for consistency across modules

* feat: add cross-module login scenarios and improve user menu handling; refactor module URLs to use environment variable

* feat: enhance cross-module login tests and update user menu selectors; add new test scripts for browser compatibility

* feat: enhance cross-module redirect tests; add response status checks and update module selectors

* feat: Fix auth context sharing in goToPageWithUser

Add auth cookies to provided context instead of creating separate browser
context. This ensures fixture pages inherit authentication state, fixing
test isolation issues where tests would fail with language/domain mismatches
when run as part of full suite.

Backward compatible with existing test patterns.

* feat: enhance cross-module login scenarios; add explicit wait for profile picture visibility after login

* feat: update profile redirect test to use 'qa-automation' user

* feat: update module URLs to support English and Hebrew versions; refactor tests and constants accordingly

* feat: implement Help Sheet to Zendesk redirects tests for English and Hebrew versions; add redirect mappings

* feat: Create the first of many sanity tests. Added two new pages to thew page objecvt modal for profile page and edit profilepage.

* feat: removing console logs on first sanity tests

* feat(Sheets Sanity): Created a Sanity test that checks the lifecycle of sheet creation. Fixed old Selectors and moved log out to end.

* docs: Add comprehensive documentation for the e2e Sanity Test Suite, detailing core, user flow, sheet workflow, and cross-module redirect tests.

* feat: removing old files

* feat: add Sanity test 3; Search bar verification and results

* feat: path sheet workflow tests to add images and youtube videos and verify it works

* refractor: fix wait times and configuration to handle bigger load on tests

* refactor: sanity tests - update load state waits to 'domcontentloaded' for improved performance and reliability

* feat: remove the temporary go live folder for testing in sanity

* feat: moved cross module redirect tests out of misc/ directory back into the main sanity tests as a non temp test

* feat: enhance user login checks with debug logging and improved wait states

* feat: add TIMEOUT_MULTIPLIER env variable with t() helper for e2e tests

* feat: apply TIMEOUT_MULTIPLIER t() wrapper to all page objects

* feat: Implement mobile E2E testing infrastructure including constants, documentation, and a hamburger menu sanity test.

* feat(e2e): apply TIMEOUT_MULTIPLIER t() wrapper to all spec files

Wrap all timeout values and waitForTimeout() calls in spec files
with the t() helper function so they respect TIMEOUT_MULTIPLIER.

Files updated:
- Sanity: cross-module-login, user-flow-sanity, sheet-workflow-sanity,
  search-sanity, cross-module-redirects
- Tests: regression-tests, sheet-editor-resource-panel, sheetEditor,
  autosave
- Voices: trending-topics, header
- Library: header
- Misc: help-sheet-redirects
- Mobile: mobile-hamburger-sanity

* docs(e2e): add TIMEOUT_MULTIPLIER documentation to README

- Added 'Timeout Multiplier' subsection under Environment Setup
- Updated Configuration Reference with accurate timeout values
- Included usage examples showing correct t() wrapper pattern

* feat: remove old mobile web testing plan and implementation (working on a better ne, needed a fresh start)

* feat: rename voices and library folder. Harden goToPageWithLang function. and remove old outdated tests to pave way for new

* feat(e2e): add Library Texts Tree Traversal tests for Tanach

* docs(e2e): rewrite READM and fix SANITY.md

* feat(e2e): implement Library Assistant end-to-end tests and related configurations like POM and new account that has LA enabled

* feat(e2e): add negative coverage tests for Library Assistant visibility and enhance page object with expectNotPresent method

* feat(e2e): add tests for Library Assistant header menu and responsive behavior

* feat(e2e): update Library Assistant tests for running on firefox and safari

Co-authored-by: Copilot <copilot@github.com>

* refactor(e2e): remove unused browser configurations from Playwright setup ('all')

* chore(env): update example.env with comments for clarity on adding a user for Library Assistant

* refactor(e2e): streamline search and user flow tests by removing redundant code and enhancing modal handling. Sanity Tests are now reliable and consitent

* fix(e2e): harden header new-tab test and modal-dismiss loop against parallel-run flakes, reduces time for dismissing popups

* fix(e2e): update footer link URLs for About and Help sections to ensure correct navigation behavior

* fix(e2e): replace 'networkidle' with 'domcontentloaded' for improved page load handling in header and navigation tests

* fix(e2e): replace 'networkidle' with 'domcontentloaded' for improved page load handling across various tests

* fix(e2e): make the header spec tests have more conistency in what they are clicking. Archive trendin topics test

* fix(e2e): fix timing issue with modal popup

* feat(e2e): add tests for Terms and Privacy Policy links in sidebar footer

* fix(e2e): update regex patterns for footer link hrefs to improve matching

* feat(e2e-mobile): Add mobile hamburger menu tests and page object implementation part 1.

* feat(e2e-mobile): Implement mobile authentication flow tests and enhance mobile hamburger functionality

* feat(e2e): Add end-to-end tests for Resource Panel features
- Introduce a new page object for the Resource Panel to encapsulate interactions and assertions related to the panel's UI components and behaviors.

* feat(e2e): Update header menu test to include 'Settings' item for staff users

* rename(e2e): rename mobile folder to mobiel web for clarity

* feat(e2e-mobile): Add mobile hamburger menu and authentication flow tests back in under new folder name

* feat(e2e): Resource Panel Part 2 — 13 new spec files covering RP-060 → RP-212 (42 tests across Connections List, Text List, Topics, Web Pages, Sheets, Manuscripts, Notes, Add to Sheet, Share, Search in Text, Feedback, Guide, Hebrew UI)

Extends the POM with helpers for every new mode and brings the Resource Panel suite from 37 to 79 tests, all passing against real production data with zero skips.

* refactor(e2e): move auth to globalSetup; eliminate per-worker login races

Replace the per-test "log in lazily and write auth_*.json on first call" pattern with a one-time globalSetup that logs every account in once before any worker starts and writes a read-only storage-state file per profile. Workers only ever read those files thereafter, so the file-write race that caused auth-gated tests to flake on every full-parallel run is now structurally impossible. Also fixes missing awaits in loginAs, a single-cookie clear in expireLogoutCookie, a redundant double-navigation in goToPageWithUser, and removes the BROWSER_SETTINGS.english/.hebrew footguns plus their now-unused helpers.

* test(e2e): introduce two-layer overlay suppression and fix sanity-suite flakes

Strapi-driven banners (Sustainer modal et al.) are now suppressed at context level via `installOverlaySuppression` — `addInitScript` short-circuits `modal_*`/`banner_*` localStorage reads, `context.route` empties the `/api/strapi/graphql-cache` payload, and a `cookiesNotificationAccepted=1` cookie is pre-seeded; `hideAllModalsAndPopups` is now a slim parallel click-fallback for the remaining non-Strapi overlays.

Also: Sanity 7's logout now uses `enAdmin` so it stops invalidating the `enUser` session that sheet-workflow-sanity depends on, Sanity 9e waits for the SPA URL update instead of `domcontentloaded`, RP-190's `hasGuideButton` timeout was bumped from 3s to 15s, 102 redundant `hideAllModalsAndPopups` calls were dropped across 28 spec files, and 6 stale Zendesk slugs in `helpDeskLinksConstants.ts` were refreshed — all six chrome projects now pass 261/261 at full parallelism.

* fix(e2e): change playwright mobile config setup to use mobile web folder instead of mobile

* refractor(e2e): change playwright.mobile.config to playwright.mobileweb.config for consistency

* docs(e2e): enhance README and comments with details on destructive auth tests and session management

* feat(e2e): Add end-to-end tests for Voices Topics landing and topic pages

- Implement `voices-topics-landing.spec.ts` to test the Trending Topics sidebar on the landing page, ensuring it lists 5–15 topics and each is navigable.
- Create `voices-topics.spec.ts` for comprehensive end-to-end tests on individual topic pages, covering content display, language support, sheet interactions, and navigation.
- Introduce `voicesTopicPage.ts` as a Page Object Model (POM) for the Voices topic page, encapsulating methods for interacting with the page elements and assertions.

* feat(e2e): Enhance user menu interaction for logged-in state and improve locator precision

* feat(e2e): Improve Guided Learning button visibility checks and increase timeout for full-parallel load

* feat(e2e): Update share URL checks to be case-insensitive for sandbox compatibility

* feat(e2e): Remove unused cookieObject and enhance cookie domain handling for cross-subdomain tests

* docs(e2e): production rewrite of README handbook + fix stale sub-doc references

README.md: rewrite as a humans-first handbook with a front-loaded quick start,
a README-vs-CLAUDE doc map, and a utils.ts catalogue. Correct page-object count
(20), complete the PageManager accessor list, fix mobile/ -> mobile web/ paths,
remove the obsolete chrome-all/firefox-all/safari-all troubleshooting entry,
re-point canonical exemplars to resourcePanelPage/voicesTopicPage + the Resource
Panel/Voices Topics specs, document fixtures/ and archived-tests/, and switch the
TOC to lint-clean auto-generated heading anchors.

assistant/README.md: drop the nonexistent ai-chatbot.csv references; correct the
coverage from 10 to the real 16 behavioral + 4 LA-NEG tests; refresh the TODO.

mobile web/README.md: fix the self-referencing mobile/ paths -> mobile web/.

Voices Topics + Resource Panel READMEs: fix the broken mobile/ link and remove
references to the .claude/ scratch directory and its test-plan CSVs.

* fix(e2e): repair broken package.json playwright test scripts

The test:* aliases referenced an old project-naming scheme (chromium / library /
voices / misc / webkit-library) that no longer exists in playwright.config.ts.
Point them at the real project names (chrome-* / firefox-* / safari-*), drop the
unmappable generic browser aliases, and add aliases for the newer suites
(sanity, assistant, resource-panel, voices-topics) plus the mobile config.

* docs(e2e): rename Sanity/SANITY.md to README.md for folder consistency

* docs(e2e): add per-folder READMEs and modernize the Sanity guide

Every meaningful subfolder now has an onboarding doc:
- pages/README.md — page-object index: canonical models (resourcePanelPage,
  voicesTopicPage), every pm.onX() accessor, and legacy/orphan markers
- library/, voices/, Misc/ — per-folder guides (scope, ID schemes, baseURL)
- Full testing by Feature/README.md — index of the deep feature suites
- archived-tests/README.md — why specs are archived and how to run them locally

Sanity/README.md (renamed from SANITY.md in the prior commit): rewritten into
the house style (ID tables, per-spec detail, architecture, gotchas incl. the
Sanity 7 -> enAdmin destructive-auth constraint).

Root README directory map + Related-docs updated to surface the new docs and
the Sanity README rename.

* docs(e2e): commit CLAUDE.md handbook for the test suite

Un-ignore e2e-tests/CLAUDE.md and add it to the repo so the prescriptive
rules for writing tests, extending page objects, and auditing existing
code are version-controlled alongside the suite. The global CLAUDE.md
ignore rule is preserved with a negation so only the e2e-tests handbook
is opted in.

* fix(e2e): harden RP-056 lexicon invalid-entry assertion for local runs

Scope the assertion to the autocomplete widget's own
`.dictionary-toc-autocomplete` class instead of `.connectionsPanel` (so
it holds when jQuery UI mounts the dropdown on <body>), and skip the
Enter press that could close the dropdown before assertion.

* test(e2e): cut artifact overhead and add local retry to stabilize flaky runs

Resource Panel tests (RP-053, RP-122, RP-123) intermittently hit the 50s
timeout under full parallelism (8 workers): a starved worker stalled on
context.newPage()/panel rendering, and the forced teardown left half-written
trace files (the ENOENT artifact errors). All three pass in isolation in
5-7s, confirming flake-not-bug.

- video: retain-on-failure -> off. retain-on-failure records video for every
  test then discards on pass; pure overhead and redundant with the trace.
- trace: retain-on-failure -> on-first-retry. No artifacts on the happy path;
  a full trace is still captured when a failed test is retried.
- retries (desktop): 0 -> 1 locally so a starvation blip self-heals. CI keeps 2.
  Mobile config already retried locally; only its video/trace were updated.

Full chrome-resource-panel suite: 79 passed, 0 retries, 3.4m -> 1.1m.

* feat(e2e): add Hebrew Library Assistant suite + fix MDL auth for logged-in .il

The Library Assistant chatbot is now bilingual, so mirror the English LA
coverage in Hebrew. Verified every label against the LIVE deployed
component (the source i18n catalog differed from prod), and reworked the
auth layer to support a logged-in session on the Hebrew (.org.il) domain.

* fix(e2e): update user menu interaction to handle React re-renders and ensure dropdown visibility

* feat(e2e): add Library Topics test suite (LIB-001 → LIB-029)

* feat(e2e): enhance header tests for logged-in users and update user menu items

* fix(e2e): improve error handling and messaging for login failures in global setup

* test(e2e): add Voices Bookmarks & History suite (VBM-001 → VBM-010)

Sheet-page Save menu, /saved list, and /history bookmarking on the
Voices module. Adds VoicesBookmarksPage POM, three specs, disjoint
test sheets, README, and chrome/firefox/safari-voices-bookmarks
projects. 10 tests, stable at full parallelism.

* fix(e2e): Update folder name of Voices Bookmarks History to Voices Bookmarks (Saved) and History for clarity

* feat(e2e): make Sanity suite tag-scoped via @sanity instead of folder-scoped

Sanity membership was tied to the Sanity/ folder, so adding a release-gate
test that lives elsewhere meant copy/pasting it. Make the chrome/firefox/
safari-sanity projects tag-scoped instead: testDir './e2e-tests' + grep
/@sanity/, so they run every test tagged { tag: '@sanity' } anywhere in the
tree. This is safe with a single baseURL because the suite navigates to
absolute MODULE_URLS, never relative paths.

* feat(e2e): add @sanity tag to Resource Panel tests and update README for clarity on test execution

* feat(e2e): change user-flow to user-menu for clairty

* refactor(e2e): remove @sanity tag from describe blocks for cross-module tests and tag some individually

* refactor(e2e): make Sanity tag-scoped and relocate specs to feature folders

Sanity membership was tied to the Sanity/ folder. Make it a property of the
test instead: the *-sanity projects now scan the whole tree and run every test
tagged { tag: '@sanity' } (testDir './e2e-tests' + grep /@sanity/), so a
release-gate test lives in its feature folder and joins the suite via the tag.

Relocate the five former Sanity specs to where they belong and renumber their
IDs; @sanity tags travel with them, so the tagged run is unchanged (32 tests):
- search-sanity          -> Full testing by Feature/Search/search.spec.ts        (SRCH-001..006)
- user-menu-sanity       -> Full testing by Feature/User Menu/user-menu.spec.ts  (UMN-001..007)
- cross-module-login     -> Full testing by Feature/Cross-Module/login.spec.ts   (XMOD-L01..L09)
- cross-module-redirects -> Full testing by Feature/Cross-Module/redirects.spec.ts (XMOD-R01..R17)
- sheet-workflow         -> voices/sheet-lifecycle.spec.ts                        (SHT-001..010)

* fix(e2e): stabilize SRCH-002/005 search-submit with web-first toHaveURL

Both 'submit search and get results' tests flaked on navigation timing:
- SRCH-002 asserted page.url() right after a fixed waitForTimeout, racing the
  search navigation (received /texts instead of /search?q=...).
- SRCH-005 used page.waitForURL, which waits for the 'load' event by default;
  the Voices search SPA's load can lag past 15s even though the URL is correct.

Replace both with a web-first expect(page).toHaveURL(...) assertion, which
polls the URL and does not depend on the load event firing. Verified stable:
each test passed 3x with retries off.

* fix(e2e): harden UMN-002/SHT-001 navigation waits with toHaveURL

Both used page.waitForURL after a client-side (SPA) route change, which waits
for the 'load' event by default — the same root cause as the SRCH-005 flake
(URL is correct well before 'load' fires on Sefaria's SPAs).

- UMN-002: profile nav via the user menu
- SHT-001: sheet creation via the Create button

Swap both to a web-first expect(page).toHaveURL(...) that polls the URL without
depending on 'load'. Verified: each passed 3x with retries off.

* fix(e2e): harden remaining click-nav waits with web-first toHaveURL

Suite-wide sweep for the same root cause as the SRCH-005 flake (asserting on a
URL that's reached by a client-side SPA route change before waiting on it
web-first). Three more genuine matches:
- SRCH-001 / SRCH-004: suggestion click then waitForLoadState + sync url.toMatch
- MOD-H014: Create-button click then waitForURL (waits for the 'load' event)

Replace all with expect(page).toHaveURL(...). Verified: SRCH-001/004 6x, MOD-H014
3x, all passing with retries off.

Swept the rest of the suite and cleared the others: the voices/sidebar Create &
Learn-More asserts go through handleClickNavigation (awaits navigation/popup), the
Library Topics LIB-024 ref-link nav is a real document load via the canonical
Promise.all pattern, and the cross-module XMOD sync-url asserts follow awaited
page.goto()s.

* test(e2e): expand @sanity coverage (+75 tests, mobile-sanity project)

* ocs(e2e): clarify run-via-project + document mobile sanity

* feat(e2e): rename voices in archived tests to do not run on prod for clarity

* fix(e2e): repair HAM-A003 and VBM-002 failures

HAM-A003 (mobile auth-flow): the login page's registration call-to-action
changed in Sefaria#3370 — the link is now "Sign Up" ("Don't have an account? Sign
Up", rendered by registration/_cross_flow_nav.html), not "Create a new
account". Update the locator and test title to match.

VBM-002 (Voices /history bookmarking): the Voices /history page crashes on
render (TypeError reading 'trim' in ProfilePic) when the account's history
contains a row for a since-deleted sheet with no ownerName, blanking the
whole page so .savedHistoryList never appears. Add clearReadingHistory() to
VoicesBookmarksPage and call it before seeding: it toggles the account's
reading_history setting off->on via /api/profile/sync, which makes the
server run delete_user_history(exclude_saved=True) — wiping reading history
while preserving /saved. The settings time_stamp must strictly exceed the
stored one (persists across runs on the shared account), so the helper reads
the current stamp and sends strictly-greater values.

* fix(e2e): make Voices Bookmarks tests module-pinned and history-render robust

VBM-001/005/006 failed under chrome-sanity (baseURL=LIBRARY) while passing
under chrome-bookmarks (baseURL=VOICES): the POM navigated with relative
page.goto('/saved' | '/history' | '/sheets/<id>'), which resolve against the
running project's baseURL, so under sanity they landed on the Library module
and the Voices /saved entries were absent. Pin navigation to absolute Voices
URLs (voicesBase) so the suite is correct regardless of project baseURL.

VBM-002: replace the unreliable clearReadingHistory() helper. The
reading_history toggle does not durably clear history on the shared QA
account — the server-side delete is inconsistent and the client re-syncs its
cached history back — so /history kept crashing on accumulated deleted-sheet
rows (ProfilePic throws on a missing ownerName, blanking the page). Instead
sanitize the user_history responses at the network layer (page.route): fetch
the real response and drop only the un-renderable rows (a sheet item with no
ownerName). Real data, including the seeded target row, passes through and the
bookmark toggle + /saved checks still hit the real backend.

* fix(e2e): protect all Voices Bookmarks list navigations from the ProfilePic crash

The /saved page is the same UserHistoryPanel/SheetBlock/ProfilePic render as
/history, on the same shared QA account — so the undefined-ownerName crash that
blanked /history can blank /saved too the moment a saved sheet's author is
deleted. Centralize the user_history response sanitizer: gotoSaved and
gotoHistory both call it (idempotent, registered once per page), so every VBM
list navigation is covered, not just VBM-002. Drop the now-redundant explicit
call in history-page.spec.ts.

---------

Co-authored-by: Copilot <copilot@github.com>
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.

3 participants