-
Notifications
You must be signed in to change notification settings - Fork 5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test: migrate simple send spec to POM #22464
Conversation
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
@@ -0,0 +1,11 @@ | |||
const LoginPage = require('../page-objects/login.page'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
processes: are flows that keep repeating across different tests with few variations (captured by params in the functions)
This is the most simple unlock wallet process, but there are more that could be captured around that (in helpers file, we have logInWithBalanceValidation
unlockWallet
with a couple of variations - to be included in the future
|
||
await sendTransaction( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this method is at the moment defined in helpers
file and it has a flavor of a process - understood in the POM context. The decision here was to, instead of directly implement a process for send-transaction (which we will very likely need in the future) just to implement the steps one by one in the test.
When to add a process? if the group of steps is repeated in another tests then, we could abstract it as a process (like done in unlock wallet above).
The approach of not going over a process here serves as a demo purpose, as we can see here both processes and normal page object actions using their methods in the specs
@@ -14,14 +19,28 @@ describe('Simple send', function () { | |||
ganacheOptions: defaultGanacheOptions, | |||
title: this.test.fullTitle(), | |||
}, | |||
async ({ driver, ganacheServer }) => { | |||
await logInWithBalanceValidation(driver, ganacheServer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this method is at the moment defined in helpers file and it has a flavor of a process - this has been defined as the process unlock-wallet
as a very simple process - it should be expanded to match the different login options we have
@@ -0,0 +1,14 @@ | |||
class HeaderNavbar { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this component is not used in the simple-send
spec, but is added in order to build the skeleton of the Page Object Model patterns defined in the proposal. It will be used in other specs and can be expanded according to the specific needs.
Also more components can be added following this example
await homePage.goToActivityList(); | ||
|
||
const confirmedTx = await homePage.isConfirmedTxInActivity(); | ||
assert.equal(confirmedTx, true, 'Confirmed tx is not found'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assertions are always done on the spec level (never on Page Objects)
|
||
async isConfirmedTxInActivity() { | ||
return await this.driver.isElementPresent(this.confirmedTx); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
notice here we don't do any assertion, we only return a boolean. As stated in the guidelines, assertions should go on the spec level, not on the page object classes. See below how this method is called in the spec for doing the assertion there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I propose we consider incorporating assertion methods within our page object. This approach could offer several advantages:
Code Reusability: By creating methods like check_confirmedTxDisplayedInActivity() and check_confirmedTxNotDisplayedInActivity(), we can reuse these assertions throughout our tests, saving coding time.
async check_confirmedTxDisplayedInActivity() {
assert.equal(await isConfirmedTxInActivity(), true, 'Confirmed tx is not found');
}
async check_confirmedTxNotDisplayedInActivity() {
assert.equal(await isConfirmedTxInActivity(), false, 'Confirmed tx is displayed');
}
For the class organization: We already have elements and action methods, when we add a section for check methods in the page class, we could consider adopting a naming convention such as check_xxx for assertions and action_xxx for actions. Something like action_ startSendFlow(), This approach would make it easier to locate specific methods within the class, especially when dealing with a large list of class methods
We can also add parameter for check methods, which allowing us to adapt the error message dynamically. For example:
async check_confirmedTxNumber(expectedNumberOfTx) {
let currentNumberOfTx = txElements.length;
assert.equal(
expectedNumberOfTx,
currentNumberOfTx,
`We have ${expectedNumberOfTx} transactions appear instead of ${currentNumberOfTx}`
);
}
This approach would automatically generate a helpful error message for debugging purposes. In fact, I would recommend that all check methods have their error messages defined within the class. Currently, in our tests, error messages are not always specified in the test spec. By defining error messages within the class, we can ensure that meaningful error messages are consistently used throughout our tests, making it easier for people to create and debug tests.
await sendAmountPage.addAmount('1'); | ||
await sendAmountPage.goToNextScreen(); | ||
await confirmTxPage.confirmTx(); | ||
await homePage.goToActivityList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all the actions we perform in the test should be clear by their name. This can also be seen as documentation of flows. From this test we infer that Simple Send flow basically is:
- login
- start a send flow
- add a recipient address
- add a token amount
- proceed to the next screen
- confirm transaction
- go to activity list
- (in the assertion) check tx is there
); | ||
await sendAmountPage.addAmount('1'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to keep page objects independent, then when we switch page, we don't return the object of the new class. To improve tests stability, i suggest we add waitPageLoaded() method to each class, so when we switch page in test spec, we can call the waitPageLoaded() first to be sure that we are on the expected page and ready for actions.
Another option is to allow dependency, then we can do something like:
async startSendFlow() {
await this.driver.clickElement(this.btnSend);
await (new SendToPage(driver)). waitSendToPageLoaded();
}
The advantage is that we don't need to add waitPageLoaded() everywhere in the test body.
await homePage.goToActivityList(); | ||
|
||
const confirmedTx = await homePage.isConfirmedTxInActivity(); | ||
assert.equal(confirmedTx, true, 'Confirmed tx is not found'); | ||
}, | ||
); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we're migrating to TypeScript, it might be an ideal time to also adopt it when we implementing the POM. I suggest we consider writing our code in TypeScript during this process.
This PR has been automatically marked as stale because it has not had recent activity in the last 60 days. It will be closed in 14 days. Thank you for your contributions. |
This PR was closed because there has been no follow up activity in the last 14 days. Thank you for your contributions. |
… test suite (#25373) ## **Description** The Page Object Model (POM) is a design pattern that promotes code reusability and maintainability in e2e testing by encapsulating UI element selectors and interactions within page objects. This initial PR is the beginning of integrating POM into e2e test suite, aiming to enhance test code quality. The entire implementation is done in TypeScript instead of JavaScript. Key Considerations for Implementation: - Easy Adaptation: ensure that the structure is straightforward and intuitive, thereby accelerating the development speed and facilitating easier migration. - Enhanced Logging: offer better insights during test execution and debugging, making it easier to investigate flaky test Structure: The POM structure is organized around distinct page objects for each application UI screen. (Common components, such as the HeaderNavbar, are directly integrated as part of a screen's component when interaction is required. This approach eliminates the need for explicit class extension and allows for the exclusion of the HeaderNavbar in screens where interaction with it is unnecessary.) Page functions and process: I've introduced page objects, each designed to encapsulate the elements and interactions specific to their respective pages. Additionally, I've implemented processes such as `loginWaitBalance` and `sendTransaction` to efficiently manage common test flows that require interactions across multiple screens. Processes should be defined for sequences that involve multiple page objects, facilitating broader testing objectives like completing transactions or logging in. These are typically actions that navigate through several screens. Functions within a class (page object) are best used for actions and verifications that are specific to a single page. This approach promotes the encapsulation and reusability of code for individual UI components or screens, making the tests more modular and maintainable. 3 Migrated Test Cases: Migrated 3 transaction test cases to the new POM structure, showcasing the improved test architecture and log information. These migrations serve as a POC, demonstrating the effectiveness of POM in our testing environment and provide the base for future migrations. Additionally, I'm eliminating CSS selectors that aren't robust and replacing them with more stable selectors in this PR. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25373?quickstart=1) ## **Related issues** Fixes: #24985 Relates to: #22464 ## **Manual testing steps** Tests should pass on CI. Code should be easy to understand. ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** ![Screenshot 2024-06-14 at 12 01 32](https://github.com/MetaMask/metamask-extension/assets/105063779/e0a48e9e-d8e1-4508-8b3b-6e1923b65efc) ![Screenshot 2024-06-18 at 11 25 05](https://github.com/MetaMask/metamask-extension/assets/105063779/d10f9bc8-9a3c-4d80-a341-0d7a8fcf73fc) ### **After** ![Screenshot 2024-06-14 at 22 38 22](https://github.com/MetaMask/metamask-extension/assets/105063779/35b1b150-dc0c-436b-9062-af0a71dd48ef) ![Screenshot 2024-06-18 at 11 28 10](https://github.com/MetaMask/metamask-extension/assets/105063779/ddbb953c-7839-4d6c-ae1a-07fa1e825f7d) ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.
Description
Related issues
Fixes:
Manual testing steps
Screenshots/Recordings
Before
After
Pre-merge author checklist
Pre-merge reviewer checklist