Skip to content
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

Closed
wants to merge 1 commit into from
Closed

Conversation

seaona
Copy link
Contributor

@seaona seaona commented Jan 9, 2024

Description

Related issues

Fixes:

Manual testing steps

  1. Go to this page...

Screenshots/Recordings

Before

After

Pre-merge author checklist

  • I’ve followed MetaMask Coding Standards.
  • I've clearly explained what problem this PR is solving and how it is solved.
  • I've linked related issues
  • I've included manual testing steps
  • I've included screenshots/recordings if applicable
  • I’ve included tests if applicable
  • I’ve documented my code using JSDoc format if applicable
  • I’ve applied the right labels on the PR (see labeling guidelines). Not required for external contributors.
  • I’ve properly set the pull request status:
    • In case it's not yet "ready for review", I've set it to "draft".
    • In case it's "ready for review", I've changed it from "draft" to "non-draft".

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • 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.

Copy link
Contributor

github-actions bot commented Jan 9, 2024

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');
Copy link
Contributor Author

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(
Copy link
Contributor Author

@seaona seaona Jan 9, 2024

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);
Copy link
Contributor Author

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 {
Copy link
Contributor Author

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');
Copy link
Contributor Author

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);
}
Copy link
Contributor Author

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

Copy link
Contributor

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();
Copy link
Contributor Author

@seaona seaona Jan 10, 2024

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:

  1. login
  2. start a send flow
  3. add a recipient address
  4. add a token amount
  5. proceed to the next screen
  6. confirm transaction
  7. go to activity list
  8. (in the assertion) check tx is there

);
await sendAmountPage.addAmount('1');
Copy link
Contributor

@chloeYue chloeYue Jan 16, 2024

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');
},
);
});
Copy link
Contributor

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.

Copy link
Contributor

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.

@github-actions github-actions bot added the stale issues and PRs marked as stale label Mar 16, 2024
Copy link
Contributor

This PR was closed because there has been no follow up activity in the last 14 days. Thank you for your contributions.

@github-actions github-actions bot closed this Mar 30, 2024
chloeYue added a commit that referenced this pull request Jul 2, 2024
… 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stale issues and PRs marked as stale
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants