Plan reviewed by automated QA expert agent. Key changes incorporated:
Must fix (done):
- Split
GeoErrorPageintoEnableLocationPageandRestrictedLocationPagepage objects - Fixed
GeoPromptOverlay.dismissselector (was the checkbox, not a button) - Added
location_lostreason to Slice 3 failure redirect tests - Enumerated all restricted-location variants explicitly in Slice 1
Should fix (done):
- Split ticket #4 into 4a (pre-login) and 4b (post-login)
- Added
data-testidprerequisite note for post-login debug panel - Added "read config from debug panel" helper for environment-agnostic threshold tests
- Added JWT fallback test (viewer_country_region override) to Slice 3
- Added cookie-overrides-config priority test to Slice 2
Accepted as-is:
- Dropped
runAcrossViewportsframework (YAGNI, use inline viewport assertions) - Firefox project scoped to Slice 1 only (static pages)
- Skipped deterministic canary hash test as E2E (keep as unit test)
- Added loading state test between prompt dismiss and lobby render
End-to-end test coverage for the GeoComply feature: static error pages, location prompt, login verification flow (success + failure paths), post-login monitoring (via debug panel simulation), and the debug panels themselves.
All tests run on both configured Playwright projects (Desktop Chrome, Mobile Safari/iPhone 12). The static page viewport tests additionally cover the landscape breakpoint on smaller devices.
Supported browsers (for reference - Playwright covers the major rendering engines):
- Chromium: Chrome, Edge, Brave, Samsung Internet
- WebKit: Safari (macOS, iOS, iPad)
- Firefox: Firefox (desktop)
Current Playwright projects: chromium (Desktop Chrome 1280x720) and Mobile Safari (iPhone 12 390x844).
Build on the existing e2e infrastructure. New code should follow established patterns: page objects like LoginAndRegistrationPage and PokerLobbyPage, services in src/services/, fixtures in src/fixtures.ts.
The existing helper sets 4 cookies (subdivision, status, canary percent, email pattern). Extend the GeoSpoofOptions interface to support the full cookie API:
| New option | Cookie | Purpose |
|---|---|---|
reason |
GEOCOMPLY_SPOOF_REASON |
Failure reason (permission_denied, timeout, etc.) |
promptEnabled |
GEO_LOCATION_PROMPT_ENABLED |
Control location prompt visibility |
simulationEnabled |
GEO_SIMULATION_ENABLED |
Enable simulation mode for post-login tests |
Backward compatible - existing callers (including LoginAndRegistrationPage.setGeoSpoofCookies()) unaffected. New cookies only added to the array when the option is explicitly provided.
Currently setGeoSpoofCookies() calls geoSpoofCookies(domain) with defaults. Change to accept optional GeoSpoofOptions so tests can customize:
async navigate(geoOptions?: GeoSpoofOptions): Promise<void> {
await this.setGeoSpoofCookies(geoOptions)
// ... existing goto logic
}This lets geo tests configure failure modes before login without needing separate cookie setup. The loginPage fixture continues to work unchanged (no options = defaults = success spoof).
Wraps the location prompt DOM elements:
selectors:
overlay: '#geo-location-prompt'
title: '.geo-prompt-title'
body: '.geo-prompt-body'
continueBtn: '#geo-location-prompt-continue'
dontShowCheckbox: '#geo-location-prompt-dismiss' (checkbox input, NOT a button)
privacyLink: '.geo-prompt-link'
methods:
waitForVisible()
clickContinue()
checkDontShowAgain() // checks the checkbox, does NOT dismiss
isVisible(): boolean
Wraps the post-login simulation panel:
selectors (data-testid):
simToggle: 'sim-toggle'
simResultSelect: 'sim-result-select'
simDispatch: 'sim-dispatch'
locationTrigger: 'location-dropdown-trigger'
locationOption: 'location-option-{value}'
canaryBadge: 'canary-status-badge'
currentGeo: 'current-geo'
restrictionPill: 'current-restriction-pill'
eventLog: 'event-log'
logEntry: 'log-entry'
methods:
activateSimulation()
deactivateSimulation()
simulateSuccess(subdivision: string)
simulateFailure(reason: GeoFailureReason)
getCanaryStatus(): string
getCurrentGeo(): string
getRestrictionStatus(): string
waitForLogEntry(text: string)
Wraps the pre-login debug panel (?geo_debug mode):
selectors (DOM IDs):
root: '#geo-debug'
canaryPercent: '#geo-debug-canary-percent'
promptToggle: '#geo-debug-prompt-toggle'
simulationToggle: '#geo-debug-simulation-toggle'
spoofStatus: '#geo-debug-spoof-status'
spoofLocation: '#geo-debug-location-trigger'
spoofReason: '#geo-debug-spoof-reason'
launchButton: '#geo-debug-launch'
clearResult: '#geo-debug-clear-result'
authPill: '.geo-debug-auth-pill'
stateInspector: '#geo-debug-dev-panel'
methods:
setCanaryPercent(n: number)
setSpoofSuccess(subdivision: string)
setSpoofFailure(reason: string)
enablePrompt()
enableSimulation()
launch()
isAuthenticated(): boolean
Wraps the /user/enable-location server-rendered page:
selectors:
logo: '.geo-logo'
title: 'h2'
body: '.geo-container p'
faqLink: 'a[href*="help.globalpoker"]'
retryBtn: '.geo-retry-button'
methods:
waitForLoaded()
getTitle(): string
getBody(): string
clickRetry()
Wraps the /user/restricted-location server-rendered page (two variants):
selectors:
logo: '.geo-logo' (signed-out variant)
blockIcon: '.geo-block-icon' (blocked-at-login variant)
title: 'h2'
body: '.geo-container p'
termsLink: 'a[href*="terms-conditions"]' (blocked-at-login only)
actionBtn: '.geo-retry-button'
methods:
waitForLoaded()
getTitle(): string
getBody(): string
isBlockedAtLoginVariant(): boolean (checks for .geo-block-icon presence)
clickAction()
Navigate to /?geo_debug, read threshold values from the pre-login panel input placeholders. Returns config values the environment uses so tests are environment-agnostic:
returns: {
failedThreshold: number // from #geo-debug-failed-threshold placeholder
restrictedThreshold: number
allowedThreshold: number
}
Used by Slice 5 monitoring tests to dispatch the exact number of events needed for sign-out.
The post-login debug panel components (SimulationPanel.tsx, LocationDropdown.tsx, EventLog.tsx, ConfigOverridePanel.tsx, GeoLocationDebugTabView.tsx) use data-testid attributes. Verify these exist in production code. If any are missing, add them as part of ticket #0.
No authentication needed. Fast, no flakiness. Tests the server-rendered HTML pages.
| Test | Page | What it validates |
|---|---|---|
| enable-location renders default copy | /user/enable-location |
Title "Location access is required", default body text, FAQ link, Retry button |
| enable-location renders per-reason body | /user/enable-location?reason={each} |
Each of 6 reasons produces correct body text |
| enable-location Retry links to / | /user/enable-location |
Button href is "/" |
| enable-location FAQ links to troubleshooting | /user/enable-location |
FAQ href is help.globalpoker.com troubleshooting article |
| restricted-location blocked-at-login (default) | /user/restricted-location |
geo-block-icon image, "App not available in your location", Terms link, "Return home" button |
| restricted-location: entered_restricted_market | /user/restricted-location?reason=entered_restricted_market |
GP logo, "You were signed out", reason body, "Log in" button |
| restricted-location: market_restriction_changed | /user/restricted-location?reason=market_restriction_changed |
GP logo, "You were signed out", reason body, "Log in" button |
| restricted-location: restriction_cleared | /user/restricted-location?reason=restriction_cleared |
GP logo, "You were signed out", reason body, "Log in" button |
| All pages render action button in viewport (landscape) | All URLs | assertElementInViewport('.geo-retry-button') across device viewports |
Tags: @pool:none (no auth needed, can run in any environment including prod)
Authenticated flow with spoofed success. Validates the golden path: canary group -> prompt -> dismiss -> verify -> lobby.
| Test | Cookies | What it validates |
|---|---|---|
| Control group bypasses geo, reaches lobby | canaryPercent: 0 |
No prompt, no verification, lobby loads |
| Canary group sees location prompt | canaryPercent: 100, promptEnabled: true |
Prompt overlay appears after login |
| Dismissing prompt proceeds to verification | Same | Click Continue, prompt disappears, lobby loads (spoof success) |
| "Don't show again" checkbox prevents re-show | Same + check dismiss checkbox | Second login: no prompt, straight to lobby |
| Successful verification stores result in sessionStorage | canaryPercent: 100 |
sessionStorage['geo-comply-result'] contains Success result |
| Lobby loaded event fires for both groups | Both canaryPercent: 0 and 100 |
geo.lobby.loaded element or lobby content visible |
| Cookie overrides config canary percent | canaryPercent: 0 cookie when env default is non-zero |
No geo-gate runs, straight to lobby |
| Loading state visible between prompt dismiss and lobby | canaryPercent: 100, promptEnabled: true |
After Continue click, #loadingView visible before lobby renders |
Tags: @pool:bot @no-prod (uses spoof infrastructure)
Authenticated flow with spoofed failures. Validates that each failure mode redirects to the correct error page.
| Test | Spoof config | Expected redirect |
|---|---|---|
| permission_denied -> enable-location | status: Failed, reason: permission_denied |
/user/enable-location?reason=permission_denied |
| timeout -> enable-location | status: Failed, reason: timeout |
/user/enable-location?reason=timeout |
| sdk_load_failed -> enable-location | status: Failed, reason: sdk_load_failed |
/user/enable-location?reason=sdk_load_failed |
| token_failed -> enable-location | status: Failed, reason: token_failed |
/user/enable-location?reason=token_failed |
| verification_failed -> enable-location | status: Failed, reason: verification_failed |
/user/enable-location?reason=verification_failed |
| location_lost -> enable-location | status: Failed, reason: location_lost |
/user/enable-location?reason=location_lost |
| Restricted market -> restricted-location | status: Success, subdivision: <blocked market> |
/user/restricted-location (via Auth0 logout) |
| JWT fallback: restricted residential region | canaryPercent: 0 + OVERRIDE_VIEWER_COUNTRY_REGION=<restricted> |
Residential market restriction behavior without geo-gate |
Note: The restricted market test depends on which markets are in BLOCKED_MARKETS config for the target environment. Use the readConfigDefaults() helper or the pre-login debug panel to determine which subdivision triggers Phase 3 in CI. Don't hardcode.
Tags: @pool:bot @no-prod
| Test | URL | What it validates |
|---|---|---|
| Panel renders when geo_debug param present | /?geo_debug |
#geo-debug root visible, all sections rendered |
| Auth status shows correctly (logged out) | /?geo_debug (fresh context) |
Auth pill shows "Logged out" |
| Auth status shows correctly (logged in) | /?geo_debug (after login) |
Auth pill shows "Logged in" |
| Canary percent input updates cookie | /?geo_debug |
Set value, check cookie via page.context().cookies() |
| Spoof status dropdown works | /?geo_debug |
Select Success/Failed, verify cookie |
| Location dropdown shows subdivisions | /?geo_debug (status=Success) |
Click trigger, dropdown list appears with options |
| Failure reason dropdown shows options | /?geo_debug (status=Failed) |
All 5 failure reasons listed |
| Launch button navigates to app | /?geo_debug |
Clicking Launch removes geo_debug param, app loads |
| State inspector shows cookie values | /?geo_debug |
Toggle dev panel, verify table rows match set cookies |
Tags: @pool:bot @no-prod (needs login for auth status test; @pool:none for logged-out tests)
| Test | What it validates |
|---|---|
| Debug tab accessible from profile menu | Navigate to profile -> debug section visible |
| Canary status badge shows correct state | Badge shows "In canary group (100%)" |
| Login result display shows success | After spoofed success login, shows green "Success" badge |
| Current geo shows spoofed subdivision | Shows the subdivision from GEOCOMPLY_SPOOF cookie |
| Simulation toggle activates/deactivates | Click sim-toggle, verify state change |
| Result type dropdown has all options | Success + 5 failure modes listed |
| Dispatch button sends event | Select Success + US-CA, click dispatch, event log entry appears |
| Event log records dispatched events | Multiple dispatches produce chronological log entries |
| Config override inputs update cookies | Change failed threshold, verify cookie value |
Tags: @pool:bot @no-prod
Uses the post-login debug panel simulation controls to trigger monitoring callbacks through the real callback handler. Login with spoof success first, then use simulation panel.
| Test | Simulation action | Expected outcome |
|---|---|---|
| Successful location check keeps user logged in | Dispatch Success (US-CA) | User stays in lobby, event log shows "allowed" |
| Permission denied -> immediate sign-out | Dispatch Failed:permission_denied | Redirected to /user/restricted-location?reason=... or enable-location (via Auth0 logout) |
| Consecutive failures -> sign-out after threshold | Dispatch Failed:timeout x3 (default threshold) | After 3rd dispatch, user is signed out |
| Restriction detected -> sign-out | Dispatch Success with restricted subdivision | Restriction pill updates, user signed out after threshold |
| Restriction cleared -> sign-out for re-login | Login from restricted, then dispatch Success with allowed subdivision | User signed out with restriction_cleared reason |
Note: These tests need GEO_SIMULATION_ENABLED=true cookie and the simulation panel activated. The test flow:
- Login with spoof success (reach lobby)
- Navigate to profile -> debug panel
- Activate simulation mode (sim-toggle)
- Select result type + location
- Click dispatch
- Assert outcome (stay in lobby or redirect to error page)
Tags: @pool:bot @no-prod
All under epic POK-26453 (GeoComply Integration), linked to POK-26687 (E2E test story).
| # | Ticket | Type | Description | Depends on |
|---|---|---|---|---|
| 0 | Shared geo e2e infrastructure | Task | Extend geoSpoofCookies, page objects (GeoPromptOverlay, GeoDebugPanel, GeoPreLoginPanel, EnableLocationPage, RestrictedLocationPage), readConfigDefaults helper, verify data-testid attributes in production code | - |
| 1 | Static error pages e2e tests | Task | Slice 1: all enable-location + restricted-location variants, per-reason copy, viewport assertions. Add Firefox project scoped to this slice. | #0 |
| 2 | Geo-comply happy path e2e tests | Task | Slice 2: control group bypass, canary prompt, dismiss, success -> lobby, cookie-overrides-config, loading state | #0 |
| 3 | Geo-comply failure redirect e2e tests | Task | Slice 3: all 6 failure reasons + restricted market + JWT fallback | #0 |
| 4a | Pre-login debug panel e2e tests | Task | Slice 4a: canary input, spoof controls, prompt toggle, simulation toggle, state inspector, launch | #0 |
| 4b | Post-login debug panel e2e tests | Task | Slice 4b: canary badge, login result, geo state, restriction pill, simulation dispatch, event log, config overrides | #0 |
| 5 | Post-login monitoring e2e tests | Task | Slice 5: simulation-driven sign-out flows (uses readConfigDefaults for environment-agnostic thresholds) | #0, #4b |
#0 Shared infrastructure (page objects, helpers, data-testid audit)
#1 Static error pages (no auth, fastest feedback, add Firefox project)
#2 Happy path (core flow, highest value)
#3 Failure redirects (all error paths + JWT fallback)
#4a Pre-login debug panel (QA tooling, no dependency on 4b)
#4b Post-login debug panel (simulation controls)
#5 Post-login monitoring (most complex, depends on 4b for simulation)