Skip to content

test(e2e): expand e2e coverage to account-settings, consent and auto-account-creation#43

Closed
Kzoeps wants to merge 9 commits intohypercerts-org:mainfrom
Kzoeps:e2e-test-additional
Closed

test(e2e): expand e2e coverage to account-settings, consent and auto-account-creation#43
Kzoeps wants to merge 9 commits intohypercerts-org:mainfrom
Kzoeps:e2e-test-additional

Conversation

@Kzoeps
Copy link
Copy Markdown
Contributor

@Kzoeps Kzoeps commented Mar 30, 2026

Builds on #42

  • Adds e2e coverage for account-settings, consent-screen, internal-api, and automatic-account-creation features
  • Refactors e2e support layer: createAccountViaOAuth and resetBrowserContext extracted to shared helpers, Mailpit inbox clearing switched to per-recipient targeting to prevent cross-scenario OTP bleed
  • Replaces hardcoded "[email protected]" preconditions with dynamic per-run emails; adds @email, @docker-only, and @pending tags where appropriate
  • Removes redundant getSession deletion assertion (superseded by describeRepo check) and removes current handle display from account settings UI (handle update form retained; full handle-change flow tracked as pending)

Summary by CodeRabbit

  • Tests

    • Expanded end-to-end coverage: automatic account creation, consent-screen flows, internal API checks, passwordless email OTPs, account settings (handle updates, multi-session revocation, deletion), and account-recovery scenarios.
  • Chores

    • Increased test timeouts, improved mail-trap cleanup and OTP polling, unified E2E helpers, and more robust session/assertion handling for reliable runs.
  • Documentation

    • Updated E2E guides, feature tag strategy (added email tag) and env example to document internal-secret usage and mail-trap/OTP behavior.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 30, 2026

@Kzoeps is attempting to deploy a commit to the Hypercerts Foundation Team on Vercel.

A member of the Team first needs to authorize it.

@Kzoeps Kzoeps marked this pull request as draft March 30, 2026 16:56
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

Warning

Rate limit exceeded

@Kzoeps has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 22 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 4 minutes and 22 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ce07d0f6-ddd9-42fc-a13a-7389ec68a4aa

📥 Commits

Reviewing files that changed from the base of the PR and between bfa44e1 and 8accba8.

📒 Files selected for processing (2)
  • packages/auth-service/src/index.ts
  • packages/pds-core/src/index.ts
📝 Walkthrough

Walkthrough

Expanded the E2E test suite: added/updated feature files and step definitions, introduced new support helpers and world fields, reworked Mailpit/OTP handling and timeouts, added internal-api tests guarded by an internal secret, and implemented multi-session and account-settings flows.

Changes

Cohort / File(s) Summary
Cucumber config & tags
e2e/cucumber.mjs, features/IMPLEMENTATION-PLAN.md
Added multiple feature paths to Cucumber config; tightened tag filtering to exclude @docker-only and @pending; documented and added @email tag and updated tag matrix.
Feature specs (new/updated)
features/automatic-account-creation.feature, features/consent-screen.feature, features/internal-api.feature, features/account-settings.feature, features/passwordless-authentication.feature, features/*.feature
New and revised Gherkin scenarios driving OAuth sign-up, consent, internal API checks, account settings, and passwordless flows; tags adjusted (@email, @manual, @docker-only, @pending).
Step definitions (new/updated)
e2e/step-definitions/.../*.steps.ts
e2e/step-definitions/auth.steps.ts, e2e/step-definitions/consent.steps.ts, e2e/step-definitions/pds.steps.ts, e2e/step-definitions/internal-api.steps.ts, e2e/step-definitions/account-settings.steps.ts, e2e/step-definitions/email.steps.ts, e2e/step-definitions/common.steps.ts
Added multiple new step-definition modules; consolidated email steps; removed in-file OAuth helper from common.steps; added returning-user flows, consent interactions, internal-api HTTP-driven steps, and comprehensive account-settings flows.
Support helpers & flows
e2e/support/flows.ts, e2e/support/utils.ts, e2e/support/mailpit.ts, e2e/support/hooks.ts, e2e/support/env.ts, e2e/.env.example
Introduced createAccountViaOAuth, getPage, resetBrowserContext, callInternalApi; increased Mailpit polling/timeouts and added searchMessages; global + per-scenario Mailpit cleanup; added internalSecret wiring and updated .env.example.
World shape & timeouts
e2e/support/world.ts, e2e/support/hooks.ts
Extended EpdsWorld with userHandle, secondaryContext/page, API result fields and skip helper; increased default Cucumber timeout and improved BeforeAll/After hooks for Mailpit and context cleanup.
Large account-settings test module
e2e/step-definitions/account-settings.steps.ts
New comprehensive account-settings E2E flows: OTP login, handle update, multi-session create/revoke, account deletion and backend repo verification.
Docs & README
e2e/README.md, docs/design/testing-gaps.md
Updated Mailpit usage/docs and noted heuristic OTP parsing; updated implementation-plan/tag guidance.
Misc (tiny docs/entrypoint)
docker-entrypoint.sh, e2e/.env.example
Non-functional comment added to entrypoint; added E2E_EPDS_INTERNAL_SECRET to example env.

Sequence Diagram(s)

sequenceDiagram
    participant User as Playwright User
    participant Demo as Demo Client
    participant EPDS as ePDS (Auth)
    participant Mailpit as Mailpit (Email Trap)
    participant PDS as PDS (Identity)

    User->>Demo: Start OAuth login
    Demo->>EPDS: Redirect to /oauth/authorize
    EPDS->>User: Render login form
    User->>EPDS: Submit email
    EPDS->>Mailpit: Send OTP email
    Mailpit-->>User: OTP email arrives
    User->>Mailpit: Poll /api/v1/search & fetch message
    User->>EPDS: Submit OTP
    EPDS->>PDS: Create account/session
    EPDS->>User: Redirect to consent screen
    User->>EPDS: Click Approve
    EPDS->>Demo: Redirect with auth code
    Demo->>EPDS: Exchange code for token
    EPDS-->>Demo: Return token
    Demo->>User: Redirect to /welcome

    rect rgba(100,150,200,0.5)
    Note over PDS,User: Post-signup validations
    User->>PDS: com.atproto.identity.resolveHandle
    PDS-->>User: Return DID
    User->>PDS: GET /.well-known/atproto-did
    PDS-->>User: Return DID text
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Possibly related PRs

Suggested reviewers

  • aspiers

Poem

🐰 Hopped through tests with OTP in paw,

Mailpit hummed and Demo saw it all.
Approve and resolve, sessions multiply,
Handles checked, deleted repos say bye.
A rabbit cheers — E2E leaps high! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: expanding e2e test coverage to three new feature areas (account-settings, consent-screen, and automatic-account-creation).

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@Kzoeps
Copy link
Copy Markdown
Contributor Author

Kzoeps commented Mar 30, 2026

@coderabbitai please review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

@Kzoeps: Sure! I'll start reviewing the changes in this PR right away.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

1 similar comment
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

@Kzoeps: Sure! I'll start reviewing the changes in this PR right away.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

🧹 Nitpick comments (4)
e2e/step-definitions/pds.steps.ts (2)

147-150: Document why this step is a no-op.

The empty function body is intentional per the comment, but a brief inline comment inside the function would make this clearer for future maintainers.

Proposed fix
 Then(
   'the only way to authenticate is through the ePDS OAuth flow',
-  function (this: EpdsWorld) {},
+  function (this: EpdsWorld) {
+    // No-op: assertion is implicit from preceding createSession rejection
+  },
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/pds.steps.ts` around lines 147 - 150, Add a brief inline
comment inside the empty step-definition function for the step "the only way to
authenticate is through the ePDS OAuth flow" (the Then(...) handler using
EpdsWorld) to document that the no-op is intentional and why it exists; update
the anonymous function body to contain a one-line comment explaining it's
intentionally empty (e.g., authentication is validated elsewhere or handled by
shared fixtures) so future maintainers understand the purpose.

53-56: Use replaceAll for cleaner regex escaping.

SonarCloud suggests using replaceAll here. While the current code works correctly, replaceAll is more idiomatic in modern JS.

Proposed fix
     const pdsDomain = new URL(testEnv.pdsUrl).hostname
     // Handle must be <short-alphanumeric>.<pds-domain>
     const pattern = new RegExp(
-      `^[a-z0-9]{4,20}\\.${pdsDomain.replace(/\./g, '\\.')}$`,
+      `^[a-z0-9]{4,20}\\.${pdsDomain.replaceAll('.', '\\.')}$`,
     )

Note: The ast-grep ReDoS warning is a false positive here—pdsDomain comes from controlled test infrastructure (testEnv.pdsUrl), not user input.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/pds.steps.ts` around lines 53 - 56, The regex
construction uses pdsDomain.replace(/\./g, '\\.') — replaceAll is preferred for
readability; change the call to pdsDomain.replaceAll('.', '\\.') when building
the pattern variable so the RegExp remains `new
RegExp(\`^[a-z0-9]{4,20}\\.${escaped}\\$\`)` (use the escaped value of
pdsDomain), keeping the same validation logic in the code that defines pattern
and references pdsDomain.
e2e/support/flows.ts (1)

32-33: Consider reusing getPage from utils.ts.

The page null-check here duplicates the logic in getPage(world). Using the shared helper improves consistency.

Proposed fix
+import { getPage } from './utils.js'
 import { waitForEmail, extractOtp, clearMailpit } from './mailpit.js'
 
 ...
 
 export async function createAccountViaOAuth(
   world: EpdsWorld,
   email: string,
 ): Promise<{ did: string; handle: string | undefined }> {
-  const page = world.page
-  if (!page) throw new Error('page is not initialised')
+  const page = getPage(world)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/support/flows.ts` around lines 32 - 33, Replace the direct access and
null-check of page in e2e/support/flows.ts with the shared helper getPage(world)
from utils.ts: find where you do "const page = world.page" and "if (!page) throw
..." and instead call getPage(world) to obtain the Page instance (importing
getPage if not already), removing the duplicated null-check and ensuring you use
the returned Page object for subsequent operations.
e2e/support/hooks.ts (1)

27-32: Consider adding error handling for the BeforeAll Mailpit cleanup.

The fetch to wipe leftover emails doesn't check res.ok. If Mailpit is unreachable or returns an error, the suite proceeds silently. This could lead to flaky tests from stale emails, with no indication of the root cause.

Proposed fix
   // Wipe any emails left over from previous runs
   if (testEnv.mailpitPass) {
-    await fetch(`${testEnv.mailpitUrl}/api/v1/messages`, {
+    const res = await fetch(`${testEnv.mailpitUrl}/api/v1/messages`, {
       method: 'DELETE',
       headers: { Authorization: mailpitAuthHeader() },
     })
+    if (!res.ok) {
+      console.warn(`Mailpit cleanup failed: ${res.status} — stale emails may cause flaky tests`)
+    }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/support/hooks.ts` around lines 27 - 32, The BeforeAll Mailpit cleanup
fetch call (inside the block checking testEnv.mailpitPass) does not verify the
response; update the code around the fetch to check the returned Response (res)
from the DELETE to `${testEnv.mailpitUrl}/api/v1/messages` and handle non-OK
responses by throwing or logging an error with the status and body so the test
run fails or reports the failure; ensure you still include the Authorization
header from mailpitAuthHeader() and propagate or surface the error (or retry if
desired) so stale emails won't be ignored silently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/step-definitions/auth.steps.ts`:
- Around line 257-266: Replace the hardcoded '00000000' in the When step handler
with a dynamically generated wrong OTP using the active OTP settings: if
this.otpCode is available, mutate or derive a wrong value from it; otherwise
build a bogus code using OTP_LENGTH and OTP_CHARSET (ensure length matches
OTP_LENGTH and characters are drawn from OTP_CHARSET but differ from a valid
code) and use that for page.fill('#code', ...); apply the same fix to the
single-attempt helper referenced above so the test exercises the retry logic
regardless of numeric/alphanumeric or length configuration.

In `@e2e/step-definitions/consent.steps.ts`:
- Around line 24-34: The test currently asserts any non-empty text in the
.subtitle but should verify the actual client name; update the Then("it shows
the demo client's name", async function (this: EpdsWorld) { ... }) step to
target the client name element (e.g. use the locator '.subtitle strong' instead
of '.subtitle') and assert its trimmed innerText equals the expected client name
(load/compare against the value in client-metadata.json) using getPage(...) and
the subtitle locator so the test fails if the <strong> client name is missing or
incorrect.

In `@features/consent-screen.feature`:
- Around line 12-20: The step "Then the browser is redirected back to the demo
client with a valid session" only checks the URL (via the shared auth step that
waits for **/welcome) and thus duplicates "Then no consent screen is shown"
without asserting session validity; update the step (or the shared step
implementation used by it) to explicitly verify authentication state after
redirect by asserting a real session artifact — e.g., check for the demo
client's session cookie (name used by the app), an access/id token in
localStorage, or call the demo client's session endpoint (e.g., /api/session) to
confirm the user is authenticated — and apply the same stronger assertion to the
other consent-success scenarios and the shared step referenced by "Then no
consent screen is shown".

---

Nitpick comments:
In `@e2e/step-definitions/pds.steps.ts`:
- Around line 147-150: Add a brief inline comment inside the empty
step-definition function for the step "the only way to authenticate is through
the ePDS OAuth flow" (the Then(...) handler using EpdsWorld) to document that
the no-op is intentional and why it exists; update the anonymous function body
to contain a one-line comment explaining it's intentionally empty (e.g.,
authentication is validated elsewhere or handled by shared fixtures) so future
maintainers understand the purpose.
- Around line 53-56: The regex construction uses pdsDomain.replace(/\./g, '\\.')
— replaceAll is preferred for readability; change the call to
pdsDomain.replaceAll('.', '\\.') when building the pattern variable so the
RegExp remains `new RegExp(\`^[a-z0-9]{4,20}\\.${escaped}\\$\`)` (use the
escaped value of pdsDomain), keeping the same validation logic in the code that
defines pattern and references pdsDomain.

In `@e2e/support/flows.ts`:
- Around line 32-33: Replace the direct access and null-check of page in
e2e/support/flows.ts with the shared helper getPage(world) from utils.ts: find
where you do "const page = world.page" and "if (!page) throw ..." and instead
call getPage(world) to obtain the Page instance (importing getPage if not
already), removing the duplicated null-check and ensuring you use the returned
Page object for subsequent operations.

In `@e2e/support/hooks.ts`:
- Around line 27-32: The BeforeAll Mailpit cleanup fetch call (inside the block
checking testEnv.mailpitPass) does not verify the response; update the code
around the fetch to check the returned Response (res) from the DELETE to
`${testEnv.mailpitUrl}/api/v1/messages` and handle non-OK responses by throwing
or logging an error with the status and body so the test run fails or reports
the failure; ensure you still include the Authorization header from
mailpitAuthHeader() and propagate or surface the error (or retry if desired) so
stale emails won't be ignored silently.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0181e006-a423-405f-a75a-daa1f669c937

📥 Commits

Reviewing files that changed from the base of the PR and between b73cb1f and 3aa4220.

📒 Files selected for processing (14)
  • e2e/cucumber.mjs
  • e2e/step-definitions/auth.steps.ts
  • e2e/step-definitions/common.steps.ts
  • e2e/step-definitions/consent.steps.ts
  • e2e/step-definitions/email.steps.ts
  • e2e/step-definitions/pds.steps.ts
  • e2e/support/flows.ts
  • e2e/support/hooks.ts
  • e2e/support/mailpit.ts
  • e2e/support/utils.ts
  • e2e/support/world.ts
  • features/automatic-account-creation.feature
  • features/consent-screen.feature
  • features/passwordless-authentication.feature

Comment on lines +12 to 20
Given a returning user has a PDS account
When the demo client initiates an OAuth login
And the user enters the test email on the login page
Then an OTP email arrives in the mail trap
When the user enters the OTP code
Then a consent screen is displayed
And it shows the demo client's name and requested permissions
And it shows the demo client's name
When the user clicks "Approve"
Then the browser is redirected back to the demo client with a valid session
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Mar 30, 2026

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

The “valid session” assertion is weaker than the scenario text.

Then the browser is redirected back to the demo client with a valid session currently just waits for **/welcome in the shared auth step. On Lines 33-44, that makes it effectively the same check as Then no consent screen is shown, so the session/token part is never asserted explicitly.

🔍 Make the session check explicit
-    Then the browser is redirected back to the demo client with a valid session
+    Then the browser is redirected back to the demo client
+    And the demo client has a valid OAuth access token

Apply the same change to the other consent-success scenarios here, or strengthen the shared step implementation instead.

Also applies to: 33-39, 42-44

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@features/consent-screen.feature` around lines 12 - 20, The step "Then the
browser is redirected back to the demo client with a valid session" only checks
the URL (via the shared auth step that waits for **/welcome) and thus duplicates
"Then no consent screen is shown" without asserting session validity; update the
step (or the shared step implementation used by it) to explicitly verify
authentication state after redirect by asserting a real session artifact — e.g.,
check for the demo client's session cookie (name used by the app), an access/id
token in localStorage, or call the demo client's session endpoint (e.g.,
/api/session) to confirm the user is authenticated — and apply the same stronger
assertion to the other consent-success scenarios and the shared step referenced
by "Then no consent screen is shown".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

What the finding claims:
'the browser is redirected back to the demo client with a valid session' only checks the URL (waits for **/welcome) and duplicates 'no consent screen is shown' without asserting session validity.
What the code actually does:
'the browser is redirected back to the demo client with a valid session' calls assertDemoClientSession() (auth.steps.ts:65-80), which does four distinct checks:

  1. waitForURL('**/welcome') — URL redirect
  2. Finds the session_id cookie and asserts it has a non-empty value — real session artifact
  3. toContainText('You are signed in.') — UI confirmation
  4. toContainText('Sign out') — button presence
  5. toContainText(/@[\w.-]+/) — handle is rendered (proves the session was decoded server-side)
    And 'no consent screen is shown' (consent.steps.ts:55-60) only does the URL check — waitForURL('**/welcome'). These two steps are deliberately different: no consent screen is shown is a routing assertion, with a valid session is a session validity assertion. They're not duplicates.
    The finding describes a problem that was already fixed. The current code is correct, the session check is substantive, and there's nothing to change.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@Kzoeps, you're absolutely right — my finding was incorrect. The current code is correct and the two steps are meaningfully distinct:

  • 'the browser is redirected back to the demo client with a valid session' delegates to assertDemoClientSession() which performs substantive session-validity checks (cookie presence, UI confirmation, handle rendering).
  • 'no consent screen is shown' is intentionally a lighter routing-only assertion (waitForURL('**/welcome')).

There is nothing to change here. I apologize for the noise.


✏️ Learnings added
Learnt from: Kzoeps
Repo: hypercerts-org/ePDS PR: 43
File: features/consent-screen.feature:12-20
Timestamp: 2026-04-06T07:00:00.197Z
Learning: In `e2e/step-definitions/auth.steps.ts` (hypercerts-org/ePDS), the `assertDemoClientSession()` helper (around lines 65–80) performs multiple distinct session-validity checks: `waitForURL('**/welcome')`, asserting the `session_id` cookie has a non-empty value, `toContainText('You are signed in.')`, `toContainText('Sign out')`, and `toContainText(/@[\w.-]+/)` to verify the handle is rendered server-side. The Gherkin step `'the browser is redirected back to the demo client with a valid session'` uses this helper and is substantively different from `'no consent screen is shown'` in `e2e/step-definitions/consent.steps.ts`, which only does a `waitForURL('**/welcome')` routing check. Do not flag these two steps as duplicates.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: Kzoeps
Repo: hypercerts-org/ePDS PR: 12
File: packages/auth-service/src/routes/choose-handle.ts:279-306
Timestamp: 2026-03-12T07:21:30.286Z
Learning: In `packages/auth-service/src/routes/choose-handle.ts` and `packages/auth-service/src/routes/otp-verification.ts` (hypercerts-org/ePDS): Do NOT call `ctx.db.recordClientLogin(email, clientId)` here. `recordClientLogin` must be called only on the consent page (after the user grants consent), not on the choose-handle or OTP-verification step. There is also a known bug where new users are not being shown the consent page — this is tracked in PR `#11`.

Learnt from: Kzoeps
Repo: hypercerts-org/ePDS PR: 43
File: e2e/step-definitions/consent.steps.ts:25-42
Timestamp: 2026-04-06T06:55:53.275Z
Learning: In `e2e/step-definitions/consent.steps.ts` (hypercerts-org/ePDS), the "it shows the demo client's name" step uses `await expect(page.getByText(clientName, { exact: true })).toBeVisible()`. The consent page renders `<p class="subtitle"><strong>${clientName}</strong> wants to access your account</p>`. Playwright's `getByText` with `exact: true` matches child text nodes, so it correctly finds the `<strong>` element whose full text is exactly `clientName` — not the `<p>` which has additional text. This is intentional and idiomatic; do not suggest changing it to a `.subtitle strong` locator.

@Kzoeps
Copy link
Copy Markdown
Contributor Author

Kzoeps commented Apr 1, 2026

@coderabbitai please review this

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 1, 2026

@Kzoeps: Sure! I'll start reviewing the changes in this PR right away.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

🧹 Nitpick comments (1)
e2e/step-definitions/auth.steps.ts (1)

50-55: Consider extracting the nested ternary for readability.

The charset detection logic works correctly but the nested ternary is dense. Extracting to a helper or using early returns would improve clarity.

♻️ Optional refactor to improve readability
-  const otpCharset =
-    inputModeAttr === 'numeric' || patternAttr === `[0-9]{${otpLength}}`
-      ? 'numeric'
-      : patternAttr === `[A-Z0-9]{${otpLength}}`
-        ? 'alphanumeric'
-        : testEnv.otpCharset
+  let otpCharset: 'numeric' | 'alphanumeric'
+  if (inputModeAttr === 'numeric' || patternAttr === `[0-9]{${otpLength}}`) {
+    otpCharset = 'numeric'
+  } else if (patternAttr === `[A-Z0-9]{${otpLength}}`) {
+    otpCharset = 'alphanumeric'
+  } else {
+    otpCharset = testEnv.otpCharset
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/auth.steps.ts` around lines 50 - 55, The nested ternary
that computes otpCharset is hard to read; extract the logic into a small helper
like getOtpCharset(inputModeAttr, patternAttr, otpLength, testEnv) or replace
the ternary with an explicit if/else block: check if inputModeAttr === 'numeric'
or patternAttr matches `[0-9]{${otpLength}}` then return 'numeric', else if
patternAttr matches `[A-Z0-9]{${otpLength}}` return 'alphanumeric', otherwise
return testEnv.otpCharset; update the original assignment to call that helper or
use the clearer control flow and keep the same variable name otpCharset.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@e2e/step-definitions/auth.steps.ts`:
- Around line 50-55: The nested ternary that computes otpCharset is hard to
read; extract the logic into a small helper like getOtpCharset(inputModeAttr,
patternAttr, otpLength, testEnv) or replace the ternary with an explicit if/else
block: check if inputModeAttr === 'numeric' or patternAttr matches
`[0-9]{${otpLength}}` then return 'numeric', else if patternAttr matches
`[A-Z0-9]{${otpLength}}` return 'alphanumeric', otherwise return
testEnv.otpCharset; update the original assignment to call that helper or use
the clearer control flow and keep the same variable name otpCharset.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ab9c56de-0558-4235-a822-2eb6e4c37ff7

📥 Commits

Reviewing files that changed from the base of the PR and between 3aa4220 and fb0eb71.

📒 Files selected for processing (9)
  • .github/workflows/e2e-pr.yml
  • e2e/.env.example
  • e2e/cucumber.mjs
  • e2e/step-definitions/auth.steps.ts
  • e2e/step-definitions/internal-api.steps.ts
  • e2e/support/env.ts
  • e2e/support/utils.ts
  • e2e/support/world.ts
  • features/internal-api.feature
✅ Files skipped from review due to trivial changes (2)
  • e2e/support/env.ts
  • e2e/.env.example
🚧 Files skipped from review as they are similar to previous changes (2)
  • e2e/cucumber.mjs
  • e2e/support/utils.ts

@aspiers aspiers deleted the branch hypercerts-org:main April 2, 2026 16:48
@aspiers aspiers closed this Apr 2, 2026
@aspiers
Copy link
Copy Markdown
Contributor

aspiers commented Apr 2, 2026

Oh this was a stacked PR and it gets auto-closed if the target branch is deleted. Restored and reopening.

@aspiers aspiers reopened this Apr 2, 2026
Given "[email protected]" has a PDS account with DID "did:plc:alice"
When GET /_internal/[email protected] is called with valid x-internal-secret
Then the response is 200 with { "did": "did:plc:alice" }
Scenario: Account lookup by email returns the account's DID
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.

@Kzoeps I'm not sure it's a good idea to be testing internal stuff. These are not part of a feature but rather implementation details, and the point of E2E tests is to test functionality for users in a more black box way. Were there any particular reasons why we need this file?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@aspiers this was already in the list of feature files and isnt a new addition. Just decided to implement it since i thought it was required but we could remove the whole feature file then?

e2e/cucumber.mjs Outdated
import: ['e2e/step-definitions/**/*.ts', 'e2e/support/**/*.ts'],
format: ['pretty', 'html:reports/e2e.html'],
tags: 'not @manual',
tags: 'not @manual and not @docker-only',
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.

We should have an @email tag for tests involving sending email.

@Kzoeps Kzoeps force-pushed the e2e-test-additional branch from 745d436 to 169a040 Compare April 6, 2026 05:52
@Kzoeps Kzoeps changed the base branch from e2e-tests to main April 6, 2026 06:51
@coveralls-official
Copy link
Copy Markdown

coveralls-official bot commented Apr 6, 2026

Coverage Report for CI Build 24075231286

Coverage remained the same at 29.583%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 1638
Covered Lines: 517
Line Coverage: 31.56%
Relevant Branches: 931
Covered Branches: 243
Branch Coverage: 26.1%
Branches in Coverage %: Yes
Coverage Strength: 2.6 hits per line

💛 - Coveralls

@Kzoeps
Copy link
Copy Markdown
Contributor Author

Kzoeps commented Apr 6, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 1

🧹 Nitpick comments (5)
e2e/step-definitions/consent.steps.ts (1)

1-7: Tracked TODO for future automation.

The TODO comment documents a known gap (CSS injection for consent branding). Consider creating a GitHub issue to track this work if not already tracked.

Would you like me to help create an issue to track the automation of Scenario 5 (consent page client branding)?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/consent.steps.ts` around lines 1 - 7, Add a tracked
issue for automating Scenario 5 (consent page shows client branding) by creating
a GitHub issue that references the TODO in
e2e/step-definitions/consent.steps.ts, names the missing work as "inject
clientBrandingCss into renderConsent()" and describes that renderConsent() must
accept and apply clientBrandingCss from client metadata (include scenario
id/name and test file path), assign appropriate labels (e2e, enhancement) and
link back to this PR so the TODO is tracked.
features/account-settings.feature (1)

26-32: Consider uncommenting or removing the handle assertion.

Line 31 has a commented-out step # And the page displays their current handle. The renderSettingsPage in account-settings.ts now includes the "Current Handle" row, and the step definition the page displays their current handle exists in account-settings.steps.ts.

Is this intentionally commented out, or should it be enabled now that the feature is implemented?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@features/account-settings.feature` around lines 26 - 32, The commented-out
step "# And the page displays their current handle" in the feature should be
re-enabled (or removed if intentionally deprecated) to match the implemented UI
and existing step definition: update features/account-settings.feature to
uncomment that line so the scenario includes "And the page displays their
current handle", ensuring the step definition in account-settings.steps.ts is
used by the test and that renderSettingsPage (which renders the "Current Handle"
row) is validated; run the feature tests and remove the step instead only if the
handle assertion is no longer desired.
e2e/step-definitions/pds.steps.ts (1)

53-62: Consider using replaceAll for consistency (optional).

Static analysis suggests using String.prototype.replaceAll() instead of replace() with a global regex. This is a minor style preference.

The regex-from-variable warning is a false positive here since pdsDomain comes from trusted configuration (testEnv.pdsUrl), not user input.

♻️ Optional: Use replaceAll for cleaner escaping
     const pattern = new RegExp(
-      `^[a-z0-9]{4,20}\\.${pdsDomain.replace(/\./g, '\\.')}$`,
+      `^[a-z0-9]{4,20}\\.${pdsDomain.replaceAll('.', '\\.')}$`,
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/pds.steps.ts` around lines 53 - 62, The pattern-building
uses pdsDomain.replace(/\./g, '\\.') which static analysis flags; replace it
with pdsDomain.replaceAll('.', '\\.') for consistency or, if you prefer to keep
the current approach, add a comment explaining pdsDomain is trusted to silence
the false positive; update the RegExp construction where pattern is created
(referencing pdsDomain and this.userHandle) so the escaped dots are produced via
replaceAll('.', '\\.') to build the same regex.
e2e/step-definitions/auth.steps.ts (2)

337-342: Synchronize on the submit button's disabled→enabled state instead of error message visibility.

The error message persists after the first failed verification attempt. If toBeVisible() passes on the first failure, it will satisfy immediately on subsequent loop iterations without waiting for the server to process the next verification request.

The verify button is disabled before each fetch (line 495 in login-page.ts) and re-enabled only after the response arrives (line 500), providing a reliable per-request synchronization point. Wait for the button to transition from :disabled back to enabled state to ensure each attempt completes before the next one submits.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/auth.steps.ts` around lines 337 - 342, The loop in
auth.steps.ts currently waits for the persistent error message ('#error-msg')
which can remain visible across iterations; instead synchronize each attempt on
the verify button's disabled→enabled transition. After submitting with the
selector '#form-verify-otp .btn-primary', wait for that button to become enabled
(e.g. use Playwright's expect(locator).toBeEnabled() or waitFor state) so each
request has completed before the next fill/click; this uses the same disable
logic implemented in login-page.ts around the verify button.

70-74: Import the session cookie name from the shared constant instead of hardcoding.

The demo client explicitly configures the session cookie as SESSION_COOKIE in packages/demo/src/lib/session.ts. Import and use this constant instead of hardcoding 'session_id'. Additionally, scope the cookies() call to testEnv.demoUrl for clarity—though the navigation has already occurred, it makes the intent explicit and aligns with best practices for cookie assertions across origins during auth flows.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/auth.steps.ts` around lines 70 - 74, Replace the
hardcoded 'session_id' and the unscoped cookies() call: import the
SESSION_COOKIE constant (named SESSION_COOKIE) from the demo session module and
use it in the cookie lookup (cookies.find(cookie => cookie.name ===
SESSION_COOKIE)), and call page.context().cookies(testEnv.demoUrl) so the
cookies() call is scoped to the demo origin; update the sessionCookie check that
currently references sessionCookie?.value to continue to throw the same error if
missing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/step-definitions/account-settings.steps.ts`:
- Around line 63-77: The test helper assertGetSessionFails is using the DID
string as a Bearer token (Authorization: Bearer ${did}), which is invalid;
either remove this redundant assertion (the existing assertPdsAccountMissing
flow already verifies deletion via describeRepo) or change assertGetSessionFails
to use a real access token captured during session setup (store the session's
access token when creating the test account and pass that token into
assertGetSessionFails) so the request to
`${testEnv.pdsUrl}/xrpc/com.atproto.server.getSession` uses a JWT access token
instead of the DID.

---

Nitpick comments:
In `@e2e/step-definitions/auth.steps.ts`:
- Around line 337-342: The loop in auth.steps.ts currently waits for the
persistent error message ('#error-msg') which can remain visible across
iterations; instead synchronize each attempt on the verify button's
disabled→enabled transition. After submitting with the selector
'#form-verify-otp .btn-primary', wait for that button to become enabled (e.g.
use Playwright's expect(locator).toBeEnabled() or waitFor state) so each request
has completed before the next fill/click; this uses the same disable logic
implemented in login-page.ts around the verify button.
- Around line 70-74: Replace the hardcoded 'session_id' and the unscoped
cookies() call: import the SESSION_COOKIE constant (named SESSION_COOKIE) from
the demo session module and use it in the cookie lookup (cookies.find(cookie =>
cookie.name === SESSION_COOKIE)), and call
page.context().cookies(testEnv.demoUrl) so the cookies() call is scoped to the
demo origin; update the sessionCookie check that currently references
sessionCookie?.value to continue to throw the same error if missing.

In `@e2e/step-definitions/consent.steps.ts`:
- Around line 1-7: Add a tracked issue for automating Scenario 5 (consent page
shows client branding) by creating a GitHub issue that references the TODO in
e2e/step-definitions/consent.steps.ts, names the missing work as "inject
clientBrandingCss into renderConsent()" and describes that renderConsent() must
accept and apply clientBrandingCss from client metadata (include scenario
id/name and test file path), assign appropriate labels (e2e, enhancement) and
link back to this PR so the TODO is tracked.

In `@e2e/step-definitions/pds.steps.ts`:
- Around line 53-62: The pattern-building uses pdsDomain.replace(/\./g, '\\.')
which static analysis flags; replace it with pdsDomain.replaceAll('.', '\\.')
for consistency or, if you prefer to keep the current approach, add a comment
explaining pdsDomain is trusted to silence the false positive; update the RegExp
construction where pattern is created (referencing pdsDomain and
this.userHandle) so the escaped dots are produced via replaceAll('.', '\\.') to
build the same regex.

In `@features/account-settings.feature`:
- Around line 26-32: The commented-out step "# And the page displays their
current handle" in the feature should be re-enabled (or removed if intentionally
deprecated) to match the implemented UI and existing step definition: update
features/account-settings.feature to uncomment that line so the scenario
includes "And the page displays their current handle", ensuring the step
definition in account-settings.steps.ts is used by the test and that
renderSettingsPage (which renders the "Current Handle" row) is validated; run
the feature tests and remove the step instead only if the handle assertion is no
longer desired.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 43cee5d0-d4b6-4958-a03d-f7ed2737f749

📥 Commits

Reviewing files that changed from the base of the PR and between fb0eb71 and e33b95a.

📒 Files selected for processing (20)
  • .github/workflows/e2e-pr.yml
  • e2e/README.md
  • e2e/cucumber.mjs
  • e2e/step-definitions/account-settings.steps.ts
  • e2e/step-definitions/auth.steps.ts
  • e2e/step-definitions/consent.steps.ts
  • e2e/step-definitions/internal-api.steps.ts
  • e2e/step-definitions/pds.steps.ts
  • e2e/support/flows.ts
  • e2e/support/hooks.ts
  • e2e/support/mailpit.ts
  • e2e/support/utils.ts
  • e2e/support/world.ts
  • features/IMPLEMENTATION-PLAN.md
  • features/account-recovery.feature
  • features/account-settings.feature
  • features/client-branding.feature
  • features/email-delivery.feature
  • features/passwordless-authentication.feature
  • packages/auth-service/src/routes/account-settings.ts
✅ Files skipped from review due to trivial changes (3)
  • features/email-delivery.feature
  • features/account-recovery.feature
  • e2e/README.md
🚧 Files skipped from review as they are similar to previous changes (8)
  • features/passwordless-authentication.feature
  • .github/workflows/e2e-pr.yml
  • e2e/support/flows.ts
  • e2e/support/mailpit.ts
  • e2e/step-definitions/internal-api.steps.ts
  • e2e/support/utils.ts
  • e2e/support/world.ts
  • e2e/support/hooks.ts

@Kzoeps Kzoeps marked this pull request as ready for review April 7, 2026 08:22
@Kzoeps Kzoeps changed the title E2e test additional test(e2e): expand e2e coverage to account-settings, consent and auto-account-creation Apr 7, 2026
@Kzoeps
Copy link
Copy Markdown
Contributor Author

Kzoeps commented Apr 7, 2026

@coderabbitai please review this

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

@Kzoeps: Sure! I'll start reviewing the changes in this PR right away.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Kzoeps Kzoeps force-pushed the e2e-test-additional branch from 4c8fbbb to a809c0a Compare April 7, 2026 08:26
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: 2

🧹 Nitpick comments (2)
features/account-settings.feature (1)

26-31: Consider adding @email tag to scenarios that depend on OTP login.

The "User views their account information" scenario relies on the user is logged into account settings, which internally requires Mailpit for OTP login. While the Mailpit guard gracefully marks steps as pending, adding the @email tag would make the dependency explicit and allow for easier filtering in CI configurations.

The same consideration applies to the session management (lines 47-53) and account deletion (lines 57-62) scenarios.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@features/account-settings.feature` around lines 26 - 31, Add the `@email` tag
to scenarios that depend on Mailpit/OTP login to make the dependency explicit
and filterable: apply `@email` to the "User views their account information"
scenario (which uses the step "the user is logged into account settings"), and
likewise add `@email` to the session management and account deletion scenarios
that depend on OTP-based login; this ensures CI can include/exclude these tests
and documents the Mailpit dependency.
e2e/step-definitions/account-settings.steps.ts (1)

17-19: Minor: Use replaceAll for clarity.

SonarCloud suggests using String.prototype.replaceAll() instead of replace() with a global regex. This is a minor readability improvement.

♻️ Suggested fix
 function escapeForRegex(value: string): string {
-  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+  return value.replaceAll(/[.*+?^${}()|[\]\\]/g, '\\$&')
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/account-settings.steps.ts` around lines 17 - 19, The
escapeForRegex function currently uses value.replace with a global regex; update
it to use String.prototype.replaceAll for clarity by iterating over the set of
regex-special characters and calling value.replaceAll(char, `\\${char}`) (or
using an array.reduce over that characters list) so escapeForRegex still returns
the same escaped string; reference the function name escapeForRegex and the
replacement of the regex-based replace call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/cucumber.mjs`:
- Around line 2-7: The paths array in e2e/cucumber.mjs is missing the new
feature file; update the paths array (the paths variable/entry list) to include
'features/internal-api.feature' alongside the existing entries so Cucumber will
run that feature (i.e., add 'features/internal-api.feature' to the paths array
declaration).

In `@e2e/step-definitions/account-settings.steps.ts`:
- Around line 491-509: Remove the unused step definition whose regex/string is
/^the browser is redirected away from \/account \(signed out\)$/ from
e2e/step-definitions/account-settings.steps.ts; delete the entire Then(...)
block (the anonymous async function that calls getPage(this),
escapeForRegex(testEnv.authUrl), expectations on page URL and heading, and the
final page.goto/expect) so no orphaned step remains—ensure no other references
to that regex string remain in the repository after removal.

---

Nitpick comments:
In `@e2e/step-definitions/account-settings.steps.ts`:
- Around line 17-19: The escapeForRegex function currently uses value.replace
with a global regex; update it to use String.prototype.replaceAll for clarity by
iterating over the set of regex-special characters and calling
value.replaceAll(char, `\\${char}`) (or using an array.reduce over that
characters list) so escapeForRegex still returns the same escaped string;
reference the function name escapeForRegex and the replacement of the
regex-based replace call.

In `@features/account-settings.feature`:
- Around line 26-31: Add the `@email` tag to scenarios that depend on Mailpit/OTP
login to make the dependency explicit and filterable: apply `@email` to the "User
views their account information" scenario (which uses the step "the user is
logged into account settings"), and likewise add `@email` to the session
management and account deletion scenarios that depend on OTP-based login; this
ensures CI can include/exclude these tests and documents the Mailpit dependency.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8315871e-6ca0-4def-8b9e-771aa46282fb

📥 Commits

Reviewing files that changed from the base of the PR and between e33b95a and 4c8fbbb.

📒 Files selected for processing (5)
  • docs/design/testing-gaps.md
  • e2e/cucumber.mjs
  • e2e/step-definitions/account-settings.steps.ts
  • e2e/support/mailpit.ts
  • features/account-settings.feature
✅ Files skipped from review due to trivial changes (1)
  • docs/design/testing-gaps.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • e2e/support/mailpit.ts

Comment on lines +2 to +7
paths: [
'features/passwordless-authentication.feature',
'features/automatic-account-creation.feature',
'features/consent-screen.feature',
'features/account-settings.feature',
],
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Apr 7, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if internal-api.feature exists in the features directory
fd -t f 'internal-api.feature' features/

Repository: hypercerts-org/ePDS

Length of output: 92


Add internal-api.feature to the paths array.

The file features/internal-api.feature exists in the repository but is not listed in the paths array. If this feature file was added in this PR and is ready for testing, include it here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/cucumber.mjs` around lines 2 - 7, The paths array in e2e/cucumber.mjs is
missing the new feature file; update the paths array (the paths variable/entry
list) to include 'features/internal-api.feature' alongside the existing entries
so Cucumber will run that feature (i.e., add 'features/internal-api.feature' to
the paths array declaration).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

as @aspiers mentioned in one of the comments above maybe it shouldnt be tested. also i added it since it was in the initial list of feature files but maybe better to not have it since it isnt really e2e

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

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: 1

♻️ Duplicate comments (1)
e2e/cucumber.mjs (1)

2-7: ⚠️ Potential issue | 🟠 Major

Add internal-api.feature to the Cucumber paths.

features/internal-api.feature is still not included, so the new internal API scenarios won’t execute in this suite.

➕ Suggested patch
   paths: [
     'features/passwordless-authentication.feature',
     'features/automatic-account-creation.feature',
     'features/consent-screen.feature',
+    'features/internal-api.feature',
     'features/account-settings.feature',
   ],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/cucumber.mjs` around lines 2 - 7, The Cucumber `paths` array is missing
the new feature file, so add 'features/internal-api.feature' to the existing
paths array (the array literal containing
'features/passwordless-authentication.feature',
'features/automatic-account-creation.feature',
'features/consent-screen.feature', 'features/account-settings.feature') so the
internal API scenarios run as part of the suite; update the `paths` array in the
cucumber configuration to include 'features/internal-api.feature'.
🧹 Nitpick comments (3)
e2e/support/mailpit.ts (1)

89-104: Heuristic OTP extraction assumes uppercase alphanumeric codes.

The alphanumeric patterns (/^([A-Z0-9]{4,12})$/m and /\bis:\s*([A-Z0-9]{4,12})\s*$/m) only match uppercase letters. If the auth service ever generates lowercase alphanumeric OTPs, extraction will fail silently and fall through to the error.

This appears intentional based on the OTP alphabet defined elsewhere (ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789), but the comment should clarify this assumption.

📝 Optional: Add case-insensitive matching or document the assumption

Either add the i flag for case-insensitivity:

-  const isolatedAlphanum = /^([A-Z0-9]{4,12})$/m.exec(body)
+  const isolatedAlphanum = /^([A-Z0-9]{4,12})$/mi.exec(body)

Or document the uppercase assumption in the comment block (lines 71-83).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/support/mailpit.ts` around lines 89 - 104, The OTP extraction regexes
(isolatedAlphanum and inlineAlphanum) only match uppercase letters and will fail
if a lowercase alphanumeric OTP is sent; update the two patterns (the
/^([A-Z0-9]{4,12})$/m and /\bis:\s*([A-Z0-9]{4,12})\s*$/m used in the
isolatedAlphanum and inlineAlphanum checks) to be case-insensitive by adding the
i flag, or alternatively add a clear comment near these heuristics stating the
function assumes uppercase-only OTPs to make the intention explicit; adjust
tests if needed to reflect the chosen behavior.
e2e/step-definitions/auth.steps.ts (1)

54-62: Consider extracting the nested ternary for readability.

The charset inference logic is functional but the nested ternary is hard to follow. SonarCloud flagged this as a maintainability concern.

♻️ Suggested refactor to improve readability
-  const otpCharset =
-    inputModeAttr === 'numeric' || patternAttr === `[0-9]{${otpLength}}`
-      ? 'numeric'
-      : patternAttr === `[A-Z0-9]{${otpLength}}`
-        ? 'alphanumeric'
-        : testEnv.otpCharset
+  let otpCharset: 'numeric' | 'alphanumeric'
+  if (inputModeAttr === 'numeric' || patternAttr === `[0-9]{${otpLength}}`) {
+    otpCharset = 'numeric'
+  } else if (patternAttr === `[A-Z0-9]{${otpLength}}`) {
+    otpCharset = 'alphanumeric'
+  } else {
+    otpCharset = testEnv.otpCharset
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/auth.steps.ts` around lines 54 - 62, The nested ternary
that computes otpCharset (using inputModeAttr, patternAttr, otpLength, and
testEnv.otpCharset) is hard to read; replace it with a small, well-named helper
or an explicit if/else block (e.g., deriveOtpCharset(inputModeAttr, patternAttr,
otpLength, testEnv)) that returns 'numeric', 'alphanumeric', or the default
testEnv.otpCharset, then call mutateOtpCode('0'.repeat(otpLength), otpCharset)
unchanged; reference otpLength, otpCharset, inputModeAttr, patternAttr, testEnv,
and mutateOtpCode when locating the code to change.
e2e/step-definitions/account-settings.steps.ts (1)

388-441: Session revocation loop is pragmatic but may be flaky.

The comment acknowledges this approach's fragility. The loop revokes sessions one at a time until the secondary context is logged out, which works but depends on timing. The 2-second timeout (line 429) helps, but consider increasing it for slower CI environments.

💡 Consider increasing timeout for CI stability
       await expect(secondaryPage).toHaveURL(
         new RegExp(`^${authBase}/account/login(\\?.*)?$`),
-        { timeout: 2_000 },
+        { timeout: 5_000 },
       )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/account-settings.steps.ts` around lines 388 - 441, The
revocation loop is brittle due to the hardcoded 2_000ms timeout when checking
secondary session logout; in the When('the user revokes another session', ...)
step update the expect call on secondaryPage.toHaveURL (the call using {
timeout: 2_000 }) to use a larger timeout (e.g., 10_000) or preferably read a
configurable value (e.g., process.env.REVOCATION_TIMEOUT_MS) so slower CI runs
have more time; change the numeric timeout at the expect(new
RegExp(`^${authBase}/account/login(\\?.*)?$`), { timeout: 2_000 }) location and
ensure the env-derived value is parsed to a number and has a sensible default.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/step-definitions/pds.steps.ts`:
- Around line 131-141: The step definition for 'the createSession request fails
with an auth error' currently treats any 4xx-or-5xx as a failure; update the
assertion that checks this.lastHttpStatus so it only accepts 4xx statuses
(400–499). In the step function (the anonymous function using
this.lastHttpStatus), replace the loose check (status < 400) with a tighter
check that throws if status is not in the 400–499 range (e.g., if status < 400
|| status >= 500) and keep the existing error messages and guards (the initial
pending check for testEnv.mailpitPass and the "No HTTP status" throw).

---

Duplicate comments:
In `@e2e/cucumber.mjs`:
- Around line 2-7: The Cucumber `paths` array is missing the new feature file,
so add 'features/internal-api.feature' to the existing paths array (the array
literal containing 'features/passwordless-authentication.feature',
'features/automatic-account-creation.feature',
'features/consent-screen.feature', 'features/account-settings.feature') so the
internal API scenarios run as part of the suite; update the `paths` array in the
cucumber configuration to include 'features/internal-api.feature'.

---

Nitpick comments:
In `@e2e/step-definitions/account-settings.steps.ts`:
- Around line 388-441: The revocation loop is brittle due to the hardcoded
2_000ms timeout when checking secondary session logout; in the When('the user
revokes another session', ...) step update the expect call on
secondaryPage.toHaveURL (the call using { timeout: 2_000 }) to use a larger
timeout (e.g., 10_000) or preferably read a configurable value (e.g.,
process.env.REVOCATION_TIMEOUT_MS) so slower CI runs have more time; change the
numeric timeout at the expect(new RegExp(`^${authBase}/account/login(\\?.*)?$`),
{ timeout: 2_000 }) location and ensure the env-derived value is parsed to a
number and has a sensible default.

In `@e2e/step-definitions/auth.steps.ts`:
- Around line 54-62: The nested ternary that computes otpCharset (using
inputModeAttr, patternAttr, otpLength, and testEnv.otpCharset) is hard to read;
replace it with a small, well-named helper or an explicit if/else block (e.g.,
deriveOtpCharset(inputModeAttr, patternAttr, otpLength, testEnv)) that returns
'numeric', 'alphanumeric', or the default testEnv.otpCharset, then call
mutateOtpCode('0'.repeat(otpLength), otpCharset) unchanged; reference otpLength,
otpCharset, inputModeAttr, patternAttr, testEnv, and mutateOtpCode when locating
the code to change.

In `@e2e/support/mailpit.ts`:
- Around line 89-104: The OTP extraction regexes (isolatedAlphanum and
inlineAlphanum) only match uppercase letters and will fail if a lowercase
alphanumeric OTP is sent; update the two patterns (the /^([A-Z0-9]{4,12})$/m and
/\bis:\s*([A-Z0-9]{4,12})\s*$/m used in the isolatedAlphanum and inlineAlphanum
checks) to be case-insensitive by adding the i flag, or alternatively add a
clear comment near these heuristics stating the function assumes uppercase-only
OTPs to make the intention explicit; adjust tests if needed to reflect the
chosen behavior.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 58435f6d-28ec-42df-ba66-930e305b4f92

📥 Commits

Reviewing files that changed from the base of the PR and between 4c8fbbb and a809c0a.

📒 Files selected for processing (26)
  • docs/design/testing-gaps.md
  • e2e/.env.example
  • e2e/README.md
  • e2e/cucumber.mjs
  • e2e/step-definitions/account-settings.steps.ts
  • e2e/step-definitions/auth.steps.ts
  • e2e/step-definitions/common.steps.ts
  • e2e/step-definitions/consent.steps.ts
  • e2e/step-definitions/email.steps.ts
  • e2e/step-definitions/internal-api.steps.ts
  • e2e/step-definitions/pds.steps.ts
  • e2e/support/env.ts
  • e2e/support/flows.ts
  • e2e/support/hooks.ts
  • e2e/support/mailpit.ts
  • e2e/support/utils.ts
  • e2e/support/world.ts
  • features/IMPLEMENTATION-PLAN.md
  • features/account-recovery.feature
  • features/account-settings.feature
  • features/automatic-account-creation.feature
  • features/client-branding.feature
  • features/consent-screen.feature
  • features/email-delivery.feature
  • features/internal-api.feature
  • features/passwordless-authentication.feature
✅ Files skipped from review due to trivial changes (7)
  • features/account-recovery.feature
  • docs/design/testing-gaps.md
  • features/client-branding.feature
  • e2e/.env.example
  • features/email-delivery.feature
  • e2e/README.md
  • e2e/support/utils.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • e2e/support/env.ts
  • features/passwordless-authentication.feature
  • features/IMPLEMENTATION-PLAN.md
  • e2e/step-definitions/email.steps.ts
  • e2e/step-definitions/common.steps.ts
  • e2e/support/world.ts
  • e2e/support/hooks.ts
  • features/internal-api.feature

@Kzoeps
Copy link
Copy Markdown
Contributor Author

Kzoeps commented Apr 7, 2026

@coderabbitai resume reviews

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

@Kzoeps: Sure! I'll resume automatic reviews for this PR.

✅ Actions performed

Reviews resumed.

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.

🧹 Nitpick comments (6)
e2e/step-definitions/account-settings.steps.ts (2)

388-441: Session revocation loop is pragmatic but inherently flaky.

The comment on lines 401-405 correctly documents the limitation: without product-side identifiers linking UI rows to browser contexts, the test must iterate. The 2-second timeout fallback and explicit error if the secondary session isn't revoked are reasonable safeguards.

Consider adding a brief inline comment noting this is a known limitation for future maintainers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/account-settings.steps.ts` around lines 388 - 441, Add a
short inline comment clarifying that the loop in the "When('the user revokes
another session', ...)" step is a known/flaky workaround due to lack of
product-side identifiers (so maintainers understand why we iterate over
revokeButtons and use the 2_000ms secondaryPage timeout), placing the note near
the existing pragmatic comment block above the for loop (which references
revokeButtons, secondaryPage, and revokedSecondarySession).

17-35: Static analysis warnings are false positives here.

The escapeForRegex and assertHandleIsRandomSubdomain functions construct regexes from testEnv values (controlled test configuration), not user input. ReDoS risk is negligible.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/account-settings.steps.ts` around lines 17 - 35, The
static-analysis ReDoS warnings are false positives because testEnv values are
trusted test configuration; update assertHandleIsRandomSubdomain to build the
regex safely using the existing escapeForRegex(getPdsDomain()) and replace the
manual pdsDomain.replace(...) call, and add a short inline linter suppression
comment (e.g., /* eslint-disable-next-line no-redos */) immediately above the
RegExp construction in assertHandleIsRandomSubdomain to silence the warning
while keeping escapeForRegex and getPdsDomain intact.
e2e/step-definitions/auth.steps.ts (1)

54-60: Consider extracting nested ternary for readability.

The charset inference logic works correctly but the nested ternary is dense. An extracted helper would improve readability, though this is optional.

♻️ Optional: Extract charset inference
+function inferOtpCharset(
+  inputModeAttr: string | null,
+  patternAttr: string | null,
+  otpLength: number,
+): 'numeric' | 'alphanumeric' {
+  if (inputModeAttr === 'numeric' || patternAttr === `[0-9]{${otpLength}}`) {
+    return 'numeric'
+  }
+  if (patternAttr === `[A-Z0-9]{${otpLength}}`) {
+    return 'alphanumeric'
+  }
+  return testEnv.otpCharset
+}

-  const otpCharset =
-    inputModeAttr === 'numeric' || patternAttr === `[0-9]{${otpLength}}`
-      ? 'numeric'
-      : patternAttr === `[A-Z0-9]{${otpLength}}`
-        ? 'alphanumeric'
-        : testEnv.otpCharset
+  const otpCharset = inferOtpCharset(inputModeAttr, patternAttr, otpLength)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/auth.steps.ts` around lines 54 - 60, Extract the nested
ternary that computes otpCharset into a small helper function (e.g.,
inferOtpCharset) that accepts inputModeAttr, patternAttr, otpLength and testEnv
and returns 'numeric' | 'alphanumeric' | testEnv.otpCharset; replace the inline
ternary with a call to inferOtpCharset where otpCharset is assigned, keeping the
existing logic: prefer 'numeric' when inputModeAttr === 'numeric' or patternAttr
=== `[0-9]{${otpLength}}`, else 'alphanumeric' when patternAttr ===
`[A-Z0-9]{${otpLength}}`, otherwise fall back to testEnv.otpCharset.
e2e/step-definitions/pds.steps.ts (1)

46-63: Static analysis false positive — ReDoS risk is negligible here.

The pdsDomain value comes from testEnv.pdsUrl, a controlled test configuration, not user input. The escaped regex pattern is safe. The replaceAll / String.raw suggestions are cosmetic and optional.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/pds.steps.ts` around lines 46 - 63, The regex flagged
for ReDoS is a false positive because pdsDomain is derived from the controlled
test configuration (testEnv.pdsUrl) rather than user input; update the Then step
(the step definition that builds the pattern variable) to add a concise inline
comment explaining that testEnv.pdsUrl is trusted/config-controlled and that
escaping via pdsDomain.replace(/\./g,'\\.') makes the RegExp safe, so the static
analyzer can be ignored here; leave the RegExp construction (pattern = new
RegExp(...)) and the validation of this.userHandle unchanged.
e2e/step-definitions/internal-api.steps.ts (2)

36-42: Optional DRY refactor for response persistence.

The repeated status/body -> world assignments can be centralized into a small helper to reduce duplication and future drift.

Also applies to: 64-66, 100-102, 131-133, 157-159, 169-171, 181-183

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/internal-api.steps.ts` around lines 36 - 42, Create a
small helper (e.g., setLastApiResponse or persistApiResponse) that accepts the
result of callInternalApi (an object with status and body) and assigns
this.lastHttpStatus = status and this.lastApiResponse = body; then replace the
repeated two-line assignments in internal-api.steps.ts (the call sites around
the shown block and the other occurrences you noted) with a single call to that
helper to centralize response persistence and avoid duplication.

150-156: Extract repeated magic literals into module constants.

The incorrect secret and static test identifiers are repeated inline. Hoisting them to SCREAMING_SNAKE_CASE constants will improve readability and reduce typo risk.

♻️ Proposed refactor
+const INCORRECT_INTERNAL_SECRET = 'definitely-wrong-secret'
+const STATIC_TEST_EMAIL = '[email protected]'
+const STATIC_TEST_HANDLE = 'somehandle.pds.test'
+
 When(
   'the check-handle endpoint is called with an incorrect secret',
   async function (this: EpdsWorld) {
     const { status, body } = await callInternalApi(
-      `/_internal/check-handle?handle=somehandle.pds.test`,
-      'definitely-wrong-secret',
+      `/_internal/check-handle?handle=${STATIC_TEST_HANDLE}`,
+      INCORRECT_INTERNAL_SECRET,
     )
@@
 When(
   'an internal endpoint is called without the secret header',
   async function (this: EpdsWorld) {
     const { status, body } = await callInternalApi(
-      `/_internal/[email protected]`,
+      `/_internal/account-by-email?email=${STATIC_TEST_EMAIL}`,
       null,
     )
@@
 When(
   'an internal endpoint is called with an incorrect secret',
   async function (this: EpdsWorld) {
     const { status, body } = await callInternalApi(
-      `/_internal/[email protected]`,
-      'definitely-wrong-secret',
+      `/_internal/account-by-email?email=${STATIC_TEST_EMAIL}`,
+      INCORRECT_INTERNAL_SECRET,
     )

As per coding guidelines, "Constants naming convention: use SCREAMING_SNAKE_CASE for module-level magic values, camelCase for const variables".

Also applies to: 165-167, 177-180

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/step-definitions/internal-api.steps.ts` around lines 150 - 156, Extract
repeated magic literals (e.g., 'definitely-wrong-secret' and
'somehandle.pds.test' used in the callInternalApi invocation inside the When
step and the similar literals at the other occurrences) into module-level
SCREAMING_SNAKE_CASE constants (e.g., INCORRECT_SECRET, TEST_HANDLE) at the top
of e2e/step-definitions/internal-api.steps.ts, then replace the inline strings
in the When step and the other referenced calls (the callInternalApi usages
around the other noted lines) with those constants so the tests use centralized,
well-named values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@e2e/step-definitions/account-settings.steps.ts`:
- Around line 388-441: Add a short inline comment clarifying that the loop in
the "When('the user revokes another session', ...)" step is a known/flaky
workaround due to lack of product-side identifiers (so maintainers understand
why we iterate over revokeButtons and use the 2_000ms secondaryPage timeout),
placing the note near the existing pragmatic comment block above the for loop
(which references revokeButtons, secondaryPage, and revokedSecondarySession).
- Around line 17-35: The static-analysis ReDoS warnings are false positives
because testEnv values are trusted test configuration; update
assertHandleIsRandomSubdomain to build the regex safely using the existing
escapeForRegex(getPdsDomain()) and replace the manual pdsDomain.replace(...)
call, and add a short inline linter suppression comment (e.g., /*
eslint-disable-next-line no-redos */) immediately above the RegExp construction
in assertHandleIsRandomSubdomain to silence the warning while keeping
escapeForRegex and getPdsDomain intact.

In `@e2e/step-definitions/auth.steps.ts`:
- Around line 54-60: Extract the nested ternary that computes otpCharset into a
small helper function (e.g., inferOtpCharset) that accepts inputModeAttr,
patternAttr, otpLength and testEnv and returns 'numeric' | 'alphanumeric' |
testEnv.otpCharset; replace the inline ternary with a call to inferOtpCharset
where otpCharset is assigned, keeping the existing logic: prefer 'numeric' when
inputModeAttr === 'numeric' or patternAttr === `[0-9]{${otpLength}}`, else
'alphanumeric' when patternAttr === `[A-Z0-9]{${otpLength}}`, otherwise fall
back to testEnv.otpCharset.

In `@e2e/step-definitions/internal-api.steps.ts`:
- Around line 36-42: Create a small helper (e.g., setLastApiResponse or
persistApiResponse) that accepts the result of callInternalApi (an object with
status and body) and assigns this.lastHttpStatus = status and
this.lastApiResponse = body; then replace the repeated two-line assignments in
internal-api.steps.ts (the call sites around the shown block and the other
occurrences you noted) with a single call to that helper to centralize response
persistence and avoid duplication.
- Around line 150-156: Extract repeated magic literals (e.g.,
'definitely-wrong-secret' and 'somehandle.pds.test' used in the callInternalApi
invocation inside the When step and the similar literals at the other
occurrences) into module-level SCREAMING_SNAKE_CASE constants (e.g.,
INCORRECT_SECRET, TEST_HANDLE) at the top of
e2e/step-definitions/internal-api.steps.ts, then replace the inline strings in
the When step and the other referenced calls (the callInternalApi usages around
the other noted lines) with those constants so the tests use centralized,
well-named values.

In `@e2e/step-definitions/pds.steps.ts`:
- Around line 46-63: The regex flagged for ReDoS is a false positive because
pdsDomain is derived from the controlled test configuration (testEnv.pdsUrl)
rather than user input; update the Then step (the step definition that builds
the pattern variable) to add a concise inline comment explaining that
testEnv.pdsUrl is trusted/config-controlled and that escaping via
pdsDomain.replace(/\./g,'\\.') makes the RegExp safe, so the static analyzer can
be ignored here; leave the RegExp construction (pattern = new RegExp(...)) and
the validation of this.userHandle unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 72d9899b-dd55-4264-b228-d24805075f90

📥 Commits

Reviewing files that changed from the base of the PR and between 4c8fbbb and b0d5804.

📒 Files selected for processing (26)
  • docs/design/testing-gaps.md
  • e2e/.env.example
  • e2e/README.md
  • e2e/cucumber.mjs
  • e2e/step-definitions/account-settings.steps.ts
  • e2e/step-definitions/auth.steps.ts
  • e2e/step-definitions/common.steps.ts
  • e2e/step-definitions/consent.steps.ts
  • e2e/step-definitions/email.steps.ts
  • e2e/step-definitions/internal-api.steps.ts
  • e2e/step-definitions/pds.steps.ts
  • e2e/support/env.ts
  • e2e/support/flows.ts
  • e2e/support/hooks.ts
  • e2e/support/mailpit.ts
  • e2e/support/utils.ts
  • e2e/support/world.ts
  • features/IMPLEMENTATION-PLAN.md
  • features/account-recovery.feature
  • features/account-settings.feature
  • features/automatic-account-creation.feature
  • features/client-branding.feature
  • features/consent-screen.feature
  • features/email-delivery.feature
  • features/internal-api.feature
  • features/passwordless-authentication.feature
✅ Files skipped from review due to trivial changes (6)
  • e2e/support/env.ts
  • features/email-delivery.feature
  • features/account-recovery.feature
  • docs/design/testing-gaps.md
  • features/client-branding.feature
  • features/IMPLEMENTATION-PLAN.md
🚧 Files skipped from review as they are similar to previous changes (11)
  • e2e/.env.example
  • e2e/README.md
  • e2e/step-definitions/email.steps.ts
  • e2e/support/flows.ts
  • features/internal-api.feature
  • e2e/support/world.ts
  • e2e/cucumber.mjs
  • features/account-settings.feature
  • e2e/support/mailpit.ts
  • e2e/support/hooks.ts
  • e2e/support/utils.ts

@Kzoeps Kzoeps force-pushed the e2e-test-additional branch from 8accba8 to b0d5804 Compare April 7, 2026 09:49
@Kzoeps
Copy link
Copy Markdown
Contributor Author

Kzoeps commented Apr 7, 2026

opening this from origin intsead of kzoeps

@Kzoeps Kzoeps closed this Apr 7, 2026
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 7, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

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.

2 participants