Skip to content

Conversation

@ibalosh
Copy link
Contributor

@ibalosh ibalosh commented May 16, 2025

I have decided to refactor one of the admin browser tests - two factor authentication.

The reason for this is based on an observation that the browser tests can be improved in my opinion.

I see couple of reasons for improvement

  • locators are hard coded in each test, which makes them brittle to changes
  • there are better choices for locating objects on the page, that will make the tests more long lasting
  • tests could use a bit better arrange act assert pattern, to make them more readable
  • by better choice of names, tests can better act as documentation of features

If tests are more resilient on refactoring, developers will be more motivated on updating them if they fail.
Less false positives, more motivation for the updates.

The updates made to the test

  • clean up code a bit
  • have better choices in terms of locators
  • arrange code in more AAA pattern fashion
  • introduce page objects

This should allow better readability of the code, allow you to reuse pages with their methods, without the need of hard coding locators, or needing to update them in multiple places.

In my opinion, this should make it easier to update, and add new tests. I have only refactored one file, to see if you like this direction, and see the value too in the update.

  • I've read and followed the Contributor Guide
  • I've explained my change
  • I've written an automated test to prove my change works

@coderabbitai
Copy link
Contributor

coderabbitai bot commented May 16, 2025

Walkthrough

The changes remove the existing two-factor authentication (2FA) end-to-end test file and replace it with a new test suite that uses a Page Object Model design pattern. Three new classes are introduced: AdminPage for common admin page functionality, AdminLoginPage for login and 2FA-related interactions, and AdminDashboardPage for dashboard page assertions. The new test suite verifies 2FA login and resend token functionality by leveraging these page objects and Playwright for browser automation. No exported or public entities outside these new classes are modified.

Suggested labels

browser-tests

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.


Note

⚡️ Faster reviews with caching

CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 30th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.
Enjoy the performance boost—your workflow just got faster.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc924ef4a11a06bdaaaacee0cdb821cb544c950f and f13713e.

📒 Files selected for processing (5)
  • ghost/core/test/e2e-browser/admin/2fa.spec.js (0 hunks)
  • ghost/core/test/e2e-browser/admin/pages/admin-dashboard.page.js (1 hunks)
  • ghost/core/test/e2e-browser/admin/pages/admin-login.page.js (1 hunks)
  • ghost/core/test/e2e-browser/admin/pages/admin-page.js (1 hunks)
  • ghost/core/test/e2e-browser/admin/two-factor-auth.spec.js (1 hunks)
💤 Files with no reviewable changes (1)
  • ghost/core/test/e2e-browser/admin/2fa.spec.js
✅ Files skipped from review due to trivial changes (1)
  • ghost/core/test/e2e-browser/admin/pages/admin-login.page.js
🚧 Files skipped from review as they are similar to previous changes (3)
  • ghost/core/test/e2e-browser/admin/pages/admin-dashboard.page.js
  • ghost/core/test/e2e-browser/admin/two-factor-auth.spec.js
  • ghost/core/test/e2e-browser/admin/pages/admin-page.js
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: Database tests (Node 22.13.1, mysql8)
  • GitHub Check: Ghost-CLI tests
  • GitHub Check: Database tests (Node 20.11.1, mysql8)
  • GitHub Check: Database tests (Node 20.11.1, sqlite3)
  • GitHub Check: Unit tests (Node 20.11.1)
  • GitHub Check: Unit tests (Node 22.13.1)
  • GitHub Check: Regression tests (Node 20.11.1, sqlite3)
  • GitHub Check: Regression tests (Node 20.11.1, mysql8)
  • GitHub Check: Lint
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
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: 0

🧹 Nitpick comments (1)
ghost/core/test/e2e-browser/admin/pages/admin-login.page.js (1)

26-29: Consider adding await to the visit method

The visit() method should use await when navigating to ensure the page load completes before subsequent actions.

visit() {
-    this.page.goto('/ghost');
+    return this.page.goto('/ghost');
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 30b2cb2 and 3e003c61657a07fe4da52b559f964a97d53b8f63.

📒 Files selected for processing (5)
  • ghost/core/test/e2e-browser/admin/2fa.spec.js (0 hunks)
  • ghost/core/test/e2e-browser/admin/pages/admin-dashboard.page.js (1 hunks)
  • ghost/core/test/e2e-browser/admin/pages/admin-login.page.js (1 hunks)
  • ghost/core/test/e2e-browser/admin/pages/admin-page.js (1 hunks)
  • ghost/core/test/e2e-browser/admin/two-factor-auth.spec.js (1 hunks)
💤 Files with no reviewable changes (1)
  • ghost/core/test/e2e-browser/admin/2fa.spec.js
🧰 Additional context used
🧬 Code Graph Analysis (3)
ghost/core/test/e2e-browser/admin/pages/admin-page.js (2)
ghost/core/test/e2e-browser/admin/pages/admin-dashboard.page.js (1)
  • AdminPage (1-1)
ghost/core/test/e2e-browser/admin/pages/admin-login.page.js (1)
  • AdminPage (1-1)
ghost/core/test/e2e-browser/admin/pages/admin-login.page.js (2)
ghost/core/test/e2e-browser/admin/pages/admin-dashboard.page.js (1)
  • AdminPage (1-1)
ghost/core/test/e2e-browser/admin/two-factor-auth.spec.js (2)
  • require (2-2)
  • AdminLoginPage (4-4)
ghost/core/test/e2e-browser/admin/pages/admin-dashboard.page.js (1)
ghost/core/test/e2e-browser/admin/pages/admin-login.page.js (1)
  • AdminPage (1-1)
🔇 Additional comments (7)
ghost/core/test/e2e-browser/admin/pages/admin-page.js (1)

1-10: Good base class implementation for page objects

The AdminPage class provides a clean base for all admin page objects with a useful logoutByCookieClear method. This follows good object-oriented design principles by abstracting common functionality that will be shared across page objects.

ghost/core/test/e2e-browser/admin/pages/admin-dashboard.page.js (1)

1-11: Good use of page object inheritance and element encapsulation

The implementation follows the Page Object Model pattern correctly by extending the base AdminPage class and encapsulating the site title element. The locator strategy using CSS selector .gh-nav-menu for the site title appears to be a resilient way to verify successful login.

ghost/core/test/e2e-browser/admin/pages/admin-login.page.js (2)

3-24: Excellent use of accessibility-focused locators

The page object properly initializes elements using Playwright's recommended accessibility-focused locators such as getByLabel and getByRole. This is a significant improvement over hard-coded CSS/XPath selectors as they're more resilient to UI changes and better represent how users interact with the page.


30-43: Well-structured action methods following AAA pattern

The methods for signing in, verifying tokens, and resending tokens are clear, focused, and follow a consistent pattern. Each method encapsulates a specific user action, making the test code more readable and maintainable.

ghost/core/test/e2e-browser/admin/two-factor-auth.spec.js (3)

7-15: Good test setup with clear separation of concerns

The test file correctly uses the beforeEach hook to ensure a clean state before each test by clearing cookies. The comment explaining why a shared page isn't used provides valuable context for future maintenance.


17-27: Well-structured 2FA authentication test

This test follows the AAA pattern effectively:

  • Arrange: Fetch user data and set up login page
  • Act: Perform login and 2FA verification
  • Assert: Verify successful navigation to dashboard

The use of page objects keeps the test concise and focused on behavior rather than implementation details.


29-42: Comprehensive test for 2FA resend functionality

This test thoroughly verifies the 2FA resend workflow with appropriate assertions at each step:

  1. Checks if the "Sent" button appears after resending
  2. Verifies successful login after using the resent token

The structure follows the same clean AAA pattern as the previous test.

Copy link
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: 0

🧹 Nitpick comments (3)
ghost/core/test/e2e-browser/admin/pages/admin-page.js (3)

1-7: Good implementation of the Page Object base class.

This is a solid implementation of the base Page Object class that follows good OOP principles. The constructor properly allows customization of the page URL while providing a sensible default.

However, consider adding JSDoc comments to document the class and its parameters, especially since this will be extended by other page objects.

+/**
+ * Base admin page object that provides common functionality for all admin pages.
+ */
 class AdminPage {
     pageUrl = '/';
 
+    /**
+     * @param {import('@playwright/test').Page} page - Playwright page object
+     * @param {string} pageUrl - URL path to the admin page
+     */
     constructor(page, pageUrl = '/') {
         this.page = page;
         this.pageUrl = pageUrl;
     }

9-12: Logout implementation is clean and effective.

The method clears cookies from the browser context, which is a reliable way to ensure the user is logged out between tests. This approach is better than navigating through UI elements to perform logout, making tests more robust.

Consider a more general method name if this is intended for broader session cleanup beyond just logout.

-    async logoutByCookieClear() {
+    /**
+     * Clears cookies to log out and reset the session state
+     */
+    async clearSession() {
         const context = await this.page.context();
         await context.clearCookies();
     }

14-16: Simple and effective page navigation method.

This method handles the basic page navigation well. For additional flexibility, consider allowing the method to accept an optional URL parameter to override the instance's pageUrl.

-    async visit() {
-        await this.page.goto(this.pageUrl);
+    /**
+     * Navigate to the page URL
+     * @param {string} [url] - Optional URL to override the default page URL
+     */
+    async visit(url) {
+        await this.page.goto(url || this.pageUrl);
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e003c61657a07fe4da52b559f964a97d53b8f63 and 5fd0b3bd52d85a589f110e4bf696d16745839f81.

📒 Files selected for processing (2)
  • ghost/core/test/e2e-browser/admin/pages/admin-login.page.js (1 hunks)
  • ghost/core/test/e2e-browser/admin/pages/admin-page.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • ghost/core/test/e2e-browser/admin/pages/admin-login.page.js
🧰 Additional context used
🧬 Code Graph Analysis (1)
ghost/core/test/e2e-browser/admin/pages/admin-page.js (2)
ghost/core/test/e2e-browser/admin/pages/admin-login.page.js (1)
  • AdminPage (1-1)
ghost/core/test/e2e-browser/admin/pages/admin-dashboard.page.js (1)
  • AdminPage (1-1)
🔇 Additional comments (1)
ghost/core/test/e2e-browser/admin/pages/admin-page.js (1)

19-20: Appropriate module export.

The class is properly exported for use in other test files.

@ibalosh ibalosh changed the title Improve e2e tests - admin 2 factor authentication test 🎨 Improve e2e tests - admin 2 factor authentication test May 16, 2025
@cmraible cmraible self-assigned this May 21, 2025
ibalosh added 5 commits May 22, 2025 10:21
no-ref

Browser tests like admin 2fa tests can be improved. They should be easier to read, with less hard coded values, like repeatable locators.
To make them easier to maintain, more readable and reusable, 2fa admin test was refactored with usage of page objects, less
britle locators, better variable names, closer match to AAA (arrange act assert) way of writing test cases. If the style of this test is
acceptable, it can be used as blueprint for other tests to follow it.
@cmraible cmraible force-pushed the improve-e2e-tests branch from bc924ef to f13713e Compare May 22, 2025 17:21
@cmraible
Copy link
Collaborator

Hi @ibalosh thanks for this, looks really good! I'm just rebasing and kicking off CI, if everything checks out I'm aiming to merge this today

@cmraible cmraible changed the title 🎨 Improve e2e tests - admin 2 factor authentication test Improved admin 2FA browser tests May 22, 2025
@cmraible cmraible merged commit 72d0d6d into TryGhost:main May 22, 2025
76 of 94 checks passed
@ibalosh
Copy link
Contributor Author

ibalosh commented May 24, 2025

thanks @cmraible !

ibalosh added a commit to ibalosh/Ghost that referenced this pull request May 25, 2025
ref TryGhost#23370

Related to improvement to 2fa admin auth tests, decided to add more improvements, through cleaning up
i18n and more usage of page objects.
@ibalosh ibalosh mentioned this pull request May 25, 2025
3 tasks
ibalosh added a commit that referenced this pull request Jun 18, 2025
ref #23370

* This is using more reusable page objects, and cleans up a bit this test
to be more readable.
* As more tests are added, POM objects will be improved, as it will help
figure out which parts of the pages can be reused.
* I did not touch things like `createPostDraft` in this refactor, since
that function is reused a lot, and change should be done gradually.

**Note:** 
I see that fixtures are setup very deeply down in `ghost-test.js` and
`e2e-browser-utils.js` , which makes it hard to figure out in this and
other tests that `user 1` from data generator is used. This should be
more flexible, and something to consider to decouple a bit in future.
ibalosh added a commit that referenced this pull request Jun 19, 2025
ref #23370

* This is using more reusable page objects, and cleans up a bit this test
to be more readable.
* As more tests are added, POM objects will be improved, as it will help
figure out which parts of the pages can be reused.
* I did not touch things like `createPostDraft` in this refactor, since
that function is reused a lot, and change should be done gradually.

**Note:** 
I see that fixtures are setup very deeply down in `ghost-test.js` and
`e2e-browser-utils.js` , which makes it hard to figure out in this and
other tests that `user 1` from data generator is used. This should be
more flexible, and something to consider to decouple a bit in future.
@ibalosh ibalosh deleted the improve-e2e-tests branch October 3, 2025 09:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants