Skip to content

Migrated staff invite browser test to root e2e framework#26977

Merged
9larsons merged 1 commit intomainfrom
migrate-invite-browser-test-to-e2e
Mar 26, 2026
Merged

Migrated staff invite browser test to root e2e framework#26977
9larsons merged 1 commit intomainfrom
migrate-invite-browser-test-to-e2e

Conversation

@9larsons
Copy link
Copy Markdown
Contributor

Summary

  • Migrated the staff invite acceptance test from ghost/core/test/e2e-browser/portal/invites.spec.js to e2e/tests/admin/settings/staff-invites.test.ts
  • The old file had two identical tests — the "2FA invite" variant never enabled 2FA and was a duplicate. Replaced both with a single test.
  • Uses Mailpit to extract the invite URL from the email instead of direct database access via models.Invite.findOne()

Changes

  • New: e2e/tests/admin/settings/staff-invites.test.ts — invite acceptance test
  • New: e2e/helpers/pages/admin/invite-signup-page.ts — page object for /ghost/#/signup/:token
  • Modified: e2e/helpers/pages/admin/settings/sections/staff-section.ts — added inviteUser() method
  • Modified: e2e/helpers/services/email/utils.ts — added extractInviteLink()
  • Deleted: ghost/core/test/e2e-browser/portal/invites.spec.js

Test plan

  • yarn lint passes
  • yarn test:types passes
  • yarn test tests/admin/settings/staff-invites.test.ts passes

The old browser test in `ghost/core/test/e2e-browser/portal/invites.spec.js`
had two identical tests (the "2FA" variant never enabled 2FA). This replaces
both with a single e2e test that uses Mailpit to extract the invite URL
instead of reaching into the database via models.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

Walkthrough

This change refactors and modernizes the end-to-end testing infrastructure for staff invite functionality. It introduces new page object helpers (InviteSignupPage) for the invite signup flow, adds utilities to extract invite links from email messages, and creates a new test suite that verifies the complete invite and signup workflow. The changes include expanding the admin page helpers index, enhancing the staff section with invite capabilities, and removing a legacy test file in favor of the newly structured test suite.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Migrated staff invite browser test to root e2e framework' clearly and specifically describes the main change: moving a staff invite test to the new e2e framework.
Description check ✅ Passed The description is directly related to the changeset, providing clear context about the migration, deduplication of tests, and the switch from database access to Mailpit-based email extraction.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch migrate-invite-browser-test-to-e2e

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.

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown
Contributor

@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 (2)
e2e/tests/admin/settings/staff-invites.test.ts (1)

23-26: Consider using the page object's goto method for consistency.

Line 25 calls signupPage.goto(inviteUrl) on the raw Playwright page, while inviteSignup is the page object. Using inviteSignup.goto(inviteUrl) would be more consistent with page object patterns, as BasePage.goto() accepts an optional URL parameter that overrides pageUrl.

Suggested fix
         await withIsolatedPage(browser, {baseURL}, async ({page: signupPage}) => {
             const inviteSignup = new InviteSignupPage(signupPage);
-            await signupPage.goto(inviteUrl);
+            await inviteSignup.goto(inviteUrl);
             await inviteSignup.acceptInvite('Test Invite User', testEmail, 'test123456');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/admin/settings/staff-invites.test.ts` around lines 23 - 26, The
test uses the raw Playwright page object (signupPage.goto(inviteUrl)) instead of
the InviteSignupPage page-object; change the call to use the page-object's
navigation method (inviteSignup.goto(inviteUrl)) so BasePage.goto() can apply
its optional URL handling and keep the pattern consistent with InviteSignupPage
and its acceptInvite(...) usage.
e2e/helpers/pages/admin/invite-signup-page.ts (1)

20-26: Consider whether filling the email field is necessary.

When accepting an invite, Ghost typically pre-populates the email field from the invite token (it's the email the invite was sent to). The fill() call on line 23 may overwrite a pre-populated value with the same value, which is harmless but potentially redundant.

If the email field is ever made read-only in the future, this could cause the test to fail. Consider checking if filling is actually required, or adding a comment explaining why it's explicitly filled.

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

In `@e2e/helpers/pages/admin/invite-signup-page.ts` around lines 20 - 26, The
acceptInvite method is filling the email field even though the invite flow
usually pre-populates it; to avoid redundant writes or future failures if the
field becomes read-only, update acceptInvite to only fill the email when
necessary (e.g., if emailField.inputValue() is empty or emailField.isEditable()
returns true) or remove the emailField.fill(email) call and add a short comment
on why the email is omitted; reference the acceptInvite function and the
nameField, emailField, passwordField, and createAccountButton elements when
making the change.
🤖 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/tests/admin/settings/staff-invites.test.ts`:
- Around line 19-21: Add a guard after calling emailClient.search to ensure
messages[0] exists before passing it to emailClient.getMessageDetailed;
specifically, check the messages array returned by emailClient.search (used with
testEmail) and if empty/undefined throw or assert with a clear error like
"Invite email not found for testEmail" so extractInviteLink and
emailClient.getMessageDetailed are only called when a message is present.

---

Nitpick comments:
In `@e2e/helpers/pages/admin/invite-signup-page.ts`:
- Around line 20-26: The acceptInvite method is filling the email field even
though the invite flow usually pre-populates it; to avoid redundant writes or
future failures if the field becomes read-only, update acceptInvite to only fill
the email when necessary (e.g., if emailField.inputValue() is empty or
emailField.isEditable() returns true) or remove the emailField.fill(email) call
and add a short comment on why the email is omitted; reference the acceptInvite
function and the nameField, emailField, passwordField, and createAccountButton
elements when making the change.

In `@e2e/tests/admin/settings/staff-invites.test.ts`:
- Around line 23-26: The test uses the raw Playwright page object
(signupPage.goto(inviteUrl)) instead of the InviteSignupPage page-object; change
the call to use the page-object's navigation method
(inviteSignup.goto(inviteUrl)) so BasePage.goto() can apply its optional URL
handling and keep the pattern consistent with InviteSignupPage and its
acceptInvite(...) usage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1caaf755-cb58-4492-b7ed-4165149baf92

📥 Commits

Reviewing files that changed from the base of the PR and between c1a4d68 and 2a42df0.

📒 Files selected for processing (6)
  • e2e/helpers/pages/admin/index.ts
  • e2e/helpers/pages/admin/invite-signup-page.ts
  • e2e/helpers/pages/admin/settings/sections/staff-section.ts
  • e2e/helpers/services/email/utils.ts
  • e2e/tests/admin/settings/staff-invites.test.ts
  • ghost/core/test/e2e-browser/portal/invites.spec.js
💤 Files with no reviewable changes (1)
  • ghost/core/test/e2e-browser/portal/invites.spec.js

Comment on lines +19 to +21
const messages = await emailClient.search({subject: 'has invited you to join', to: testEmail});
const latestMessage = await emailClient.getMessageDetailed(messages[0]);
const inviteUrl = extractInviteLink(latestMessage);
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.

⚠️ Potential issue | 🟡 Minor

Add guard for missing email message.

If the invite email isn't found (e.g., timing issue or Mailpit delay), messages[0] will be undefined, leading to a confusing error from getMessageDetailed(). Adding an explicit check would provide a clearer failure message.

Suggested fix
         const messages = await emailClient.search({subject: 'has invited you to join', to: testEmail});
+        expect(messages.length).toBeGreaterThan(0);
         const latestMessage = await emailClient.getMessageDetailed(messages[0]);
         const inviteUrl = extractInviteLink(latestMessage);

Or with a more descriptive assertion:

         const messages = await emailClient.search({subject: 'has invited you to join', to: testEmail});
+        expect(messages, `Expected invite email to be sent to ${testEmail}`).toHaveLength(1);
         const latestMessage = await emailClient.getMessageDetailed(messages[0]);
📝 Committable suggestion

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

Suggested change
const messages = await emailClient.search({subject: 'has invited you to join', to: testEmail});
const latestMessage = await emailClient.getMessageDetailed(messages[0]);
const inviteUrl = extractInviteLink(latestMessage);
const messages = await emailClient.search({subject: 'has invited you to join', to: testEmail});
expect(messages, `Expected invite email to be sent to ${testEmail}`).toHaveLength(1);
const latestMessage = await emailClient.getMessageDetailed(messages[0]);
const inviteUrl = extractInviteLink(latestMessage);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/admin/settings/staff-invites.test.ts` around lines 19 - 21, Add a
guard after calling emailClient.search to ensure messages[0] exists before
passing it to emailClient.getMessageDetailed; specifically, check the messages
array returned by emailClient.search (used with testEmail) and if
empty/undefined throw or assert with a clear error like "Invite email not found
for testEmail" so extractInviteLink and emailClient.getMessageDetailed are only
called when a message is present.

@9larsons 9larsons merged commit 0ecd0c6 into main Mar 26, 2026
37 checks passed
@9larsons 9larsons deleted the migrate-invite-browser-test-to-e2e branch March 26, 2026 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant