Skip to content

Commit

Permalink
test: NOTIFY-1256 - Extending E2E tests for Account Sync (#28067)
Browse files Browse the repository at this point in the history
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

* Starts using Page Object Model for accounts syncing tests
* Adds new tests

[![Open in GitHub
Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28067?quickstart=1)

## **Related issues**

Fixes:

## **Manual testing steps**

1. All e2e tests should run and pass on the CI

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **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
- [ ] 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**

- [ ] 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.
  • Loading branch information
cmd-ob authored Oct 24, 2024
1 parent cbfd131 commit 27a3c01
Show file tree
Hide file tree
Showing 11 changed files with 821 additions and 69 deletions.
107 changes: 86 additions & 21 deletions test/e2e/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ const completeImportSRPOnboardingFlowWordByWord = async (
await driver.clickElement('[data-testid="onboarding-import-wallet"]');

// metrics

await driver.clickElement('[data-testid="metametrics-no-thanks"]');

// import with recovery phrase, word by word
Expand Down Expand Up @@ -564,37 +565,85 @@ const onboardingPinExtension = async (driver) => {
await driver.clickElement('[data-testid="pin-extension-done"]');
};

const onboardingCompleteWalletCreationWithOptOut = async (driver) => {
/**
* Completes the onboarding flow with optional opt-out settings for wallet creation.
*
* This function navigates through the onboarding process, allowing for opt-out of certain features.
* It waits for the appropriate heading to appear, then proceeds to opt-out of third-party API
* integration for general and assets sections if specified in the optOutOptions.
*
* @param {WebDriver} driver - The Selenium WebDriver instance.
* @param {object} optOutOptions - Optional. An object specifying which features to opt-out of.
* @param {boolean} optOutOptions.basicFunctionality - Optional. Defaults to true. Opt-out of basic functionality.
* @param {boolean} optOutOptions.profileSync - Optional. Defaults to true. Opt-out of profile sync.
* @param {boolean} optOutOptions.assets - Optional. Defaults to true. Opt-out of assets options.
* @param {boolean} optOutOptions.isNewWallet - Optional. Defaults to true. Indicates if this is a new wallet creation.
*/
const onboardingCompleteWalletCreationWithOptOut = async (
driver,
optOutOptions = {},
) => {
const defaultOptOutOptions = {
basicFunctionality: true,
profileSync: true,
assets: true,
isNewWallet: true,
};

const optOutOptionsToUse = { ...defaultOptOutOptions, ...optOutOptions };

// wait for h2 to appear
await driver.findElement({ text: 'Congratulations!', tag: 'h2' });
await driver.findElement({
text: optOutOptionsToUse.isNewWallet
? 'Congratulations'
: 'Your wallet is ready',
tag: 'h2',
});

// opt-out from third party API on general section
await driver.clickElementAndWaitToDisappear({
text: 'Manage default privacy settings',
tag: 'button',
});
await driver.clickElement({ text: 'General', tag: 'p' });
await driver.clickElement(
'[data-testid="basic-functionality-toggle"] .toggle-button',
);
await driver.clickElement('[id="basic-configuration-checkbox"]');
await driver.clickElementAndWaitToDisappear({
tag: 'button',
text: 'Turn off',
});

// opt-out from third party API on assets section
await driver.clickElement('[data-testid="category-back-button"]');
await driver.clickElement({ text: 'Assets', tag: 'p' });
await Promise.all(
(
await driver.findClickableElements(
'.toggle-button.toggle-button--on:not([data-testid="basic-functionality-toggle"] .toggle-button)',
)
).map((toggle) => toggle.click()),
);
if (optOutOptionsToUse.basicFunctionality) {
await driver.clickElement(
'[data-testid="basic-functionality-toggle"] .toggle-button',
);
await driver.clickElement('[id="basic-configuration-checkbox"]');
await driver.clickElementAndWaitToDisappear({
tag: 'button',
text: 'Turn off',
});
}

if (optOutOptionsToUse.profileSync) {
await driver.clickElement(
'[data-testid="profile-sync-toggle"] .toggle-button',
);
await driver.clickElementAndWaitToDisappear({
tag: 'button',
text: 'Turn off',
});
}

await driver.clickElement('[data-testid="category-back-button"]');

if (optOutOptionsToUse.assets) {
// opt-out from third party API on assets section
await driver.clickElement({ text: 'Assets', tag: 'p' });
await Promise.all(
(
await driver.findClickableElements(
'.toggle-button.toggle-button--on:not([data-testid="basic-functionality-toggle"] .toggle-button)',
)
).map((toggle) => toggle.click()),
);

await driver.clickElement('[data-testid="category-back-button"]');
}

// Wait until the onboarding carousel has stopped moving
// otherwise the click has no effect.
await driver.waitForElementToStopMoving(
Expand All @@ -610,15 +659,30 @@ const onboardingCompleteWalletCreationWithOptOut = async (driver) => {
await onboardingPinExtension(driver);
};

/**
* Completes the onboarding flow for creating a new wallet with opt-out options.
*
* This function guides the user through the onboarding process of creating a new wallet,
* including opting out of certain features as specified by the `optOutOptions` parameter.
*
* @param {object} driver - The Selenium driver instance.
* @param {string} password - The password to use for the new wallet.
* @param {object} optOutOptions - An object specifying the features to opt out of.
* @param {boolean} optOutOptions.isNewWallet - Indicates if this is a new wallet creation.
* @param {boolean} optOutOptions.basicFunctionality - Indicates if basic functionality should be opted out.
* @param {boolean} optOutOptions.profileSync - Indicates if profile sync should be opted out.
* @param {boolean} optOutOptions.assets - Indicates if assets should be opted out.
*/
const completeCreateNewWalletOnboardingFlowWithOptOut = async (
driver,
password,
optOutOptions,
) => {
await onboardingBeginCreateNewWallet(driver);
await onboardingChooseMetametricsOption(driver, false);
await onboardingCreatePassword(driver, password);
await onboardingRevealAndConfirmSRP(driver);
await onboardingCompleteWalletCreationWithOptOut(driver);
await onboardingCompleteWalletCreationWithOptOut(driver, optOutOptions);
};

const completeCreateNewWalletOnboardingFlow = async (driver, password) => {
Expand Down Expand Up @@ -1323,6 +1387,7 @@ module.exports = {
onboardingCreatePassword,
onboardingRevealAndConfirmSRP,
onboardingCompleteWalletCreation,
onboardingCompleteWalletCreationWithOptOut,
onboardingPinExtension,
assertInAnyOrder,
genRandInitBal,
Expand Down
55 changes: 55 additions & 0 deletions test/e2e/page-objects/pages/account-list-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,15 @@ class AccountListPage {
private readonly addAccountConfirmButton =
'[data-testid="submit-add-account-with-name"]';

private readonly importAccountConfirmButton =
'[data-testid="import-account-confirm-button"]';

private readonly addEthereumAccountButton =
'[data-testid="multichain-account-menu-popover-add-account"]';

private readonly addImportedAccountButton =
'[data-testid="multichain-account-menu-popover-add-imported-account"]';

private readonly addSnapAccountButton = {
text: 'Add account Snap',
tag: 'button',
Expand Down Expand Up @@ -54,6 +60,8 @@ class AccountListPage {
private readonly saveAccountLabelButton =
'[data-testid="save-account-label-input"]';

private readonly importAccountPrivateKeyInput = '#private-key-box';

constructor(driver: Driver) {
this.driver = driver;
}
Expand Down Expand Up @@ -86,6 +94,34 @@ class AccountListPage {
);
}

/**
* Adds a new account with default next available name.
*
*/
async addNewAccountWithDefaultName(): Promise<void> {
console.log(`Adding new account with next available name`);
await this.driver.clickElement(this.createAccountButton);
await this.driver.clickElement(this.addEthereumAccountButton);
await this.driver.clickElementAndWaitToDisappear(
this.addAccountConfirmButton,
);
}

/**
* Adds a new account with a custom label.
*
* @param privateKey - Private key of the account
*/
async addNewImportedAccount(privateKey: string): Promise<void> {
console.log(`Adding new imported account`);
await this.driver.clickElement(this.createAccountButton);
await this.driver.clickElement(this.addImportedAccountButton);
await this.driver.fill(this.importAccountPrivateKeyInput, privateKey);
await this.driver.clickElementAndWaitToDisappear(
this.importAccountConfirmButton,
);
}

/**
* Changes the label of the current account.
*
Expand Down Expand Up @@ -227,6 +263,25 @@ class AccountListPage {
console.log(`Check that hidden accounts list is displayed in account list`);
await this.driver.waitForSelector(this.hiddenAccountsList);
}

/**
* Verifies number of accounts currently showing in the accounts menu.
*
* @param expectedNumberOfAccounts - The expected number of accounts showing.
*/
async check_numberOfAvailableAccounts(
expectedNumberOfAccounts: number,
): Promise<void> {
console.log(
`Verify the number of accounts in the account menu is: ${expectedNumberOfAccounts}`,
);
await this.driver.wait(async () => {
const internalAccounts = await this.driver.findElements(
this.accountListItem,
);
return internalAccounts.length === expectedNumberOfAccounts;
}, 20000);
}
}

export default AccountListPage;
15 changes: 15 additions & 0 deletions test/e2e/tests/notifications/account-syncing/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
import { isManifestV3 } from '../../../../../shared/modules/mv3.utils';
import {
completeSRPRevealQuiz,
openSRPRevealQuiz,
tapAndHoldToRevealSRP,
} from '../../../helpers';
import { Driver } from '../../../webdriver/driver';

export const IS_ACCOUNT_SYNCING_ENABLED = isManifestV3;

export const getSRP = async (driver: Driver, password: string) => {
await openSRPRevealQuiz(driver);
await completeSRPRevealQuiz(driver);
await driver.fill('[data-testid="input-password"]', password);
await driver.press('[data-testid="input-password"]', driver.Key.ENTER);
await tapAndHoldToRevealSRP(driver);
return (await driver.findElement('[data-testid="srp_text"]')).getText();
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,22 @@ import {
import FixtureBuilder from '../../../fixture-builder';
import { mockNotificationServices } from '../mocks';
import {
NOTIFICATIONS_TEAM_IMPORTED_PRIVATE_KEY,
NOTIFICATIONS_TEAM_PASSWORD,
NOTIFICATIONS_TEAM_SEED_PHRASE,
} from '../constants';
import { UserStorageMockttpController } from '../../../helpers/user-storage/userStorageMockttpController';
import HeaderNavbar from '../../../page-objects/pages/header-navbar';
import AccountListPage from '../../../page-objects/pages/account-list-page';
import { accountsSyncMockResponse } from './mockData';
import { IS_ACCOUNT_SYNCING_ENABLED } from './helpers';

describe('Account syncing @no-mmi', function () {
describe('Account syncing - Import With Private Key @no-mmi', function () {
if (!IS_ACCOUNT_SYNCING_ENABLED) {
return;
}
describe('from inside MetaMask', function () {
it('syncs newly added accounts', async function () {
it('does not sync accounts imported with private keys', async function () {
const userStorageMockttpController = new UserStorageMockttpController();

await withFixtures(
Expand All @@ -46,25 +49,24 @@ describe('Account syncing @no-mmi', function () {
NOTIFICATIONS_TEAM_PASSWORD,
);

await driver.clickElement('[data-testid="account-menu-icon"]');
const header = new HeaderNavbar(driver);
await header.check_pageIsLoaded();
await header.openAccountMenu();

await driver.wait(async () => {
const internalAccounts = await driver.findElements(
'.multichain-account-list-item',
);
return internalAccounts.length === accountsSyncMockResponse.length;
}, 20000);

await driver.clickElement(
'[data-testid="multichain-account-menu-popover-action-button"]',
const accountListPage = new AccountListPage(driver);
await accountListPage.check_pageIsLoaded();
await accountListPage.check_numberOfAvailableAccounts(
accountsSyncMockResponse.length,
);
await driver.clickElement(
'[data-testid="multichain-account-menu-popover-add-account"]',
await accountListPage.check_accountDisplayedInAccountList(
'My First Synced Account',
);
await driver.fill('#account-name', 'My third account');

await driver.clickElementAndWaitToDisappear(
'[data-testid="submit-add-account-with-name"]',
await accountListPage.check_accountDisplayedInAccountList(
'My Second Synced Account',
);
await accountListPage.openAccountOptionsMenu();
await accountListPage.addNewImportedAccount(
NOTIFICATIONS_TEAM_IMPORTED_PRIVATE_KEY,
);
},
);
Expand All @@ -90,29 +92,19 @@ describe('Account syncing @no-mmi', function () {
NOTIFICATIONS_TEAM_PASSWORD,
);

await driver.clickElement('[data-testid="account-menu-icon"]');
const header = new HeaderNavbar(driver);
await header.check_pageIsLoaded();
await header.openAccountMenu();

await driver.wait(async () => {
const internalAccounts = await driver.findElements(
'.multichain-account-list-item',
);
return (
internalAccounts.length ===
userStorageMockttpController.paths.get('accounts')?.response
.length
);
}, 20000);

await driver.wait(async () => {
const internalAccounts = await driver.findElements(
'.multichain-account-list-item .multichain-account-list-item__account-name',
);
const lastAccountName = await internalAccounts[
internalAccounts.length - 1
].getText();

return lastAccountName === 'My third account';
}, 20000);
const accountListPage = new AccountListPage(driver);
await accountListPage.check_pageIsLoaded();
await accountListPage.check_numberOfAvailableAccounts(2);
await accountListPage.check_accountDisplayedInAccountList(
'My First Synced Account',
);
await accountListPage.check_accountDisplayedInAccountList(
'My Second Synced Account',
);
},
);
});
Expand Down
Loading

0 comments on commit 27a3c01

Please sign in to comment.