Skip to content

Commit

Permalink
refactor(e2e): Refactors locators and methods of dashboard and settin…
Browse files Browse the repository at this point in the history
…gs PO (#15594)

refactors locators, they are now defined as page object class properties
splits onboarding page object from dashboard
simplifies majority of methods, removes plenty of redundant one
  • Loading branch information
Vere-Grey authored Nov 27, 2024
1 parent 5b6ecfe commit 33dc0a7
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 139 deletions.
6 changes: 6 additions & 0 deletions packages/suite-desktop-core/e2e/support/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { SettingsActions } from './pageActions/settingsActions';
import { SuiteGuide } from './pageActions/suiteGuideActions';
import { TopBarActions } from './pageActions/topBarActions';
import { WalletActions } from './pageActions/walletActions';
import { OnboardingActions } from './pageActions/onboardingActions';

type Fixtures = {
electronApp: ElectronApplication;
Expand All @@ -17,6 +18,7 @@ type Fixtures = {
suiteGuidePage: SuiteGuide;
topBar: TopBarActions;
walletPage: WalletActions;
onboardingPage: OnboardingActions;
};

const test = base.extend<Fixtures>({
Expand Down Expand Up @@ -53,6 +55,10 @@ const test = base.extend<Fixtures>({
const walletPage = new WalletActions(window);
await use(walletPage);
},
onboardingPage: async ({ window }, use) => {
const onboardingPage = new OnboardingActions(window);
await use(onboardingPage);
},
});

export { test };
Expand Down
Original file line number Diff line number Diff line change
@@ -1,72 +1,52 @@
import { Page, expect } from '@playwright/test';
import { Locator, Page, expect } from '@playwright/test';

import { NetworkSymbol } from '@suite-common/wallet-config';

import { waitForDataTestSelector } from '../common';

export class DashboardActions {
private readonly window: Page;
readonly discoveryBar: Locator;
readonly dashboardGraph: Locator;
readonly deviceSwitchingOpenButton: Locator;
readonly modal: Locator;
readonly walletAtIndex = (index: number) =>
this.window.getByTestId(`@switch-device/wallet-on-index/${index}`);
readonly walletAtIndexEjectButton = (index: number) =>
this.window.getByTestId(`@switch-device/wallet-on-index/${index}/eject-button`);
readonly confirmDeviceEjectButton: Locator;
readonly addStandardWalletButton: Locator;
readonly balanceOfNetwork = (network: NetworkSymbol) =>
this.window.getByTestId(`@wallet/coin-balance/value-${network}`);

constructor(window: Page) {
this.window = window;
}
optionallyDismissFwHashCheckError() {
// dismiss the error modal only if it appears (handle it async in parallel, not necessary to block the rest of the flow)
this.window
.$('[data-testid="@device-compromised/back-button"]')
.then(dismissFwHashCheckButton => dismissFwHashCheckButton?.click());
}

async passThroughInitialRun() {
await waitForDataTestSelector(this.window, '@welcome/title');
this.optionallyDismissFwHashCheckError();
await this.window.getByTestId('@analytics/continue-button').click();
await this.window.getByTestId('@onboarding/exit-app-button').click();

await this.window.getByTestId('@onboarding/viewOnly/skip').click();
await this.window.getByTestId('@viewOnlyTooltip/gotIt').click();
this.discoveryBar = this.window.getByTestId('@wallet/discovery-progress-bar');
this.dashboardGraph = this.window.getByTestId('@dashboard/graph');
this.deviceSwitchingOpenButton = this.window.getByTestId('@menu/switch-device');
this.modal = this.window.getByTestId('@modal');
this.confirmDeviceEjectButton = this.window.getByTestId('@switch-device/eject');
this.addStandardWalletButton = this.window.getByTestId('@switch-device/add-wallet-button');
}

async discoveryShouldFinish() {
const discoveryBarSelector = '@wallet/discovery-progress-bar';
await waitForDataTestSelector(this.window, discoveryBarSelector, {
state: 'attached',
timeout: 10_000,
});
await waitForDataTestSelector(this.window, discoveryBarSelector, {
state: 'detached',
timeout: 120_000,
});
await waitForDataTestSelector(this.window, '@dashboard/graph', { timeout: 30000 });
await this.discoveryBar.waitFor({ state: 'attached', timeout: 10_000 });
await this.discoveryBar.waitFor({ state: 'detached', timeout: 120_000 });
await expect(this.dashboardGraph).toBeVisible();
}

async openDeviceSwitcher() {
await this.window.getByTestId('@menu/switch-device').click();
const deviceSwitcherModal = this.window.getByTestId('@modal');
await deviceSwitcherModal.waitFor({ state: 'visible' });
await this.deviceSwitchingOpenButton.click();
await expect(this.modal).toBeVisible();
}

async ejectWallet(walletIndex: number = 0) {
const wallet = this.window.locator(
`[data-testid="@switch-device/wallet-on-index/${walletIndex}"]`,
);
await this.window
.locator('[data-testid="@switch-device/wallet-on-index/0/eject-button"]')
.click();
await this.window.locator('[data-testid="@switch-device/eject"]').click();
await wallet.waitFor({ state: 'detached' });
await this.walletAtIndexEjectButton(walletIndex).click();
await this.confirmDeviceEjectButton.click();
await this.walletAtIndex(walletIndex).waitFor({ state: 'detached' });
}

async addStandardWallet() {
const addStandardWallet = this.window.getByTestId('@switch-device/add-wallet-button');
await addStandardWallet.click();
await this.window.getByTestId('@modal').waitFor({ state: 'detached' });
await this.addStandardWalletButton.click();
await this.modal.waitFor({ state: 'detached' });
await this.discoveryShouldFinish();
}

// asserts
async assertHasVisibleBalanceOnFirstAccount(network: NetworkSymbol) {
const locator = this.window.getByTestId(`@wallet/coin-balance/value-${network}`).first();

await expect(locator).toBeVisible();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Locator, Page, expect } from '@playwright/test';

export class OnboardingActions {
private readonly window: Page;
readonly welcomeTitle: Locator;
readonly analyticsContinueButton: Locator;
readonly onboardingContinueButton: Locator;
readonly onboardingViewOnlySkipButton: Locator;
readonly viewOnlyTooltipGotItButton: Locator;

constructor(window: Page) {
this.window = window;
this.welcomeTitle = this.window.getByTestId('@welcome/title');
this.analyticsContinueButton = this.window.getByTestId('@analytics/continue-button');
this.onboardingContinueButton = this.window.getByTestId('@onboarding/exit-app-button');
this.onboardingViewOnlySkipButton = this.window.getByTestId('@onboarding/viewOnly/skip');
this.viewOnlyTooltipGotItButton = this.window.getByTestId('@viewOnlyTooltip/gotIt');
}

optionallyDismissFwHashCheckError() {
// dismisses the error modal only if it appears (handle it async in parallel, not necessary to block the rest of the flow)
this.window
.$('[data-testid="@device-compromised/back-button"]')
.then(dismissFwHashCheckButton => dismissFwHashCheckButton?.click());
}

async completeOnboarding() {
await expect(this.welcomeTitle).toBeVisible();
this.optionallyDismissFwHashCheckError();
await this.analyticsContinueButton.click();
await this.onboardingContinueButton.click();
await this.onboardingViewOnlySkipButton.click();
await this.viewOnlyTooltipGotItButton.click();
}
}
133 changes: 73 additions & 60 deletions packages/suite-desktop-core/e2e/support/pageActions/settingsActions.ts
Original file line number Diff line number Diff line change
@@ -1,85 +1,98 @@
import { Page } from '@playwright/test';
import { expect, Locator, Page } from '@playwright/test';

import { BackendType, NetworkSymbol } from '@suite-common/wallet-config';

import { waitForDataTestSelector } from '../common';

const settingSectionsLocators = {
debug: '@settings/debug/github',
general: '@general-settings/language',
device: '@settings/device/backup-recovery-seed',
wallet: '@settings/wallet/network/btc',
} as const;
type Section = keyof typeof settingSectionsLocators;

export class SettingsActions {
private readonly window: Page;
private readonly TIMES_CLICK_TO_SET_DEBUG_MODE = 5;
readonly settingsHeader: Locator;
readonly debugTabButton: Locator;
readonly applicationTabButton: Locator;
readonly deviceTabButton: Locator;
readonly coinsTabButton: Locator;
readonly earlyAccessJoinButton: Locator;
readonly earlyAccessConfirmCheck: Locator;
readonly earlyAccessConfirmButton: Locator;
readonly earlyAccessSkipButton: Locator;
readonly settingsCloseButton: Locator;
readonly modal: Locator;
//coin Advance settings
readonly coinNetworkButton = (coin: NetworkSymbol) =>
this.window.getByTestId(`@settings/wallet/network/${coin}`);
readonly coinAdvanceSettingsButton = (coin: NetworkSymbol) =>
this.window.getByTestId(`@settings/wallet/network/${coin}/advance`);
readonly coinBackendSelector: Locator;
readonly coinBackendSelectorOption = (backend: BackendType) =>
this.window.getByTestId(`@settings/advance/${backend}`);
readonly coinAddressInput: Locator;
readonly coinAdvanceSettingSaveButton: Locator;

constructor(window: Page) {
this.window = window;
this.settingsHeader = this.window.getByTestId('@settings/menu/title');
this.debugTabButton = this.window.getByTestId('@settings/menu/debug');
this.applicationTabButton = this.window.getByTestId('@settings/menu/general');
this.deviceTabButton = this.window.getByTestId('@settings/menu/device');
this.coinsTabButton = this.window.getByTestId('@settings/menu/wallet');
this.earlyAccessJoinButton = this.window.getByTestId('@settings/early-access-join-button');
this.earlyAccessConfirmCheck = this.window.getByTestId(
'@settings/early-access-confirm-check',
);
this.earlyAccessConfirmButton = this.window.getByTestId(
'@settings/early-access-confirm-button',
);
this.earlyAccessSkipButton = this.window.getByTestId('@settings/early-access-skip-button');
this.settingsCloseButton = this.window.getByTestId('@settings/menu/close');
this.modal = this.window.getByTestId('@modal');
//coin Advance settings
this.coinBackendSelector = this.window.getByTestId('@settings/advance/select-type/input');
this.coinAddressInput = this.window.getByTestId('@settings/advance/url');
this.coinAdvanceSettingSaveButton = this.window.getByTestId(
'@settings/advance/button/save',
);
}

async toggleDebugModeInSettings() {
const settingsHeader = this.window.getByTestId('@settings/menu/title');
await settingsHeader.waitFor({ state: 'visible', timeout: 10_000 });
const timesClickToSetDebugMode = 5;
for (let i = 0; i < timesClickToSetDebugMode; i++) {
await settingsHeader.click();
await expect(this.settingsHeader).toBeVisible();
for (let i = 0; i < this.TIMES_CLICK_TO_SET_DEBUG_MODE; i++) {
await this.settingsHeader.click();
}
await this.window.getByTestId('@settings/menu/debug').waitFor({ state: 'visible' });
await expect(this.debugTabButton).toBeVisible();
}

async goToSettingSection(section: Section) {
await this.window.getByTestId(`@settings/menu/${section}`).click();
//TODO: #15552 Refactor navigation verification to specific tests and remove this method completely
await this.window
.getByTestId(settingSectionsLocators[section])
.waitFor({ state: 'visible', timeout: 10_000 });
}

async openNetworkSettings(desiredNetwork: NetworkSymbol) {
await this.window.getByTestId(`@settings/wallet/network/${desiredNetwork}`).click();
const networkSettingsButton = this.window.getByTestId(
`@settings/wallet/network/${desiredNetwork}/advance`,
);
await networkSettingsButton.waitFor({ state: 'visible' });
await networkSettingsButton.click();
await waitForDataTestSelector(this.window, '@modal');
async openCoinAdvanceSettings(coin: NetworkSymbol) {
const isCoinActive = await this.coinNetworkButton(coin).getAttribute('data-active');
if (isCoinActive === 'false') {
await this.enableCoin(coin);
}
await this.coinNetworkButton(coin).hover();
await this.coinAdvanceSettingsButton(coin).click();
await expect(this.modal).toBeVisible();
}

async enableCoin(desiredNetwork: NetworkSymbol) {
await this.window.getByTestId(`@settings/wallet/network/${desiredNetwork}`).click();
async enableCoin(coin: NetworkSymbol) {
await this.coinNetworkButton(coin).click();
await expect(this.coinNetworkButton(coin)).toHaveAttribute('data-active', 'true');
}

async changeNetworkBackend(desiredNetworkBackend: BackendType, backendUrl: string) {
const networkBackendSelector = this.window.getByTestId(
'@settings/advance/select-type/input',
);
await networkBackendSelector.waitFor({ state: 'visible' });
await networkBackendSelector.click();
await this.window.getByTestId(`@settings/advance/${desiredNetworkBackend}`).click();
const electrumAddressInput = this.window.getByTestId('@settings/advance/url');
await electrumAddressInput.fill(backendUrl);
await this.window.getByTestId('@settings/advance/button/save').click();
async changeCoinBackend(backend: BackendType, backendUrl: string) {
await this.coinBackendSelector.click();
await this.coinBackendSelectorOption(backend).click();
await this.coinAddressInput.fill(backendUrl);
await this.coinAdvanceSettingSaveButton.click();
}

async joinEarlyAccessProgram() {
await this.window
.getByTestId('@settings/early-access-join-button')
.scrollIntoViewIfNeeded();
await this.window.getByTestId('@settings/early-access-join-button').click();
const eapModal = this.window.getByTestId('@modal');
await eapModal.waitFor({ state: 'visible' });
await eapModal.getByTestId('@settings/early-access-confirm-check').click();
await eapModal.getByTestId('@settings/early-access-confirm-button').click();
await eapModal.getByTestId('@settings/early-access-skip-button').click();
}

async getEarlyAccessButtonText() {
return await this.window.getByTestId('@settings/early-access-join-button').textContent();
await this.earlyAccessJoinButton.scrollIntoViewIfNeeded();
await this.earlyAccessJoinButton.click();
await expect(this.modal).toBeVisible();
await this.earlyAccessConfirmCheck.click();
await this.earlyAccessConfirmButton.click();
await this.earlyAccessSkipButton.click();
}

async closeSettings() {
await this.window.getByTestId('@settings/menu/close').click();
await this.window.getByTestId('@settings/menu/title').waitFor({ state: 'detached' });
await this.settingsCloseButton.click();
await this.settingsHeader.waitFor({ state: 'detached' });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class SuiteGuide {
await submitButton.click();
}

async sendBugreport({
async sendBugReport({
reportText,
desiredLocation,
}: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@ test.afterAll(() => {
* 3. Check that Staking section is available
*/
test.skip('Discover all Cardano account types', async ({
onboardingPage,
dashboardPage,
topBar,
settingsPage,
walletPage,
}) => {
await dashboardPage.passThroughInitialRun();
await onboardingPage.completeOnboarding();
await dashboardPage.discoveryShouldFinish();

await topBar.openSettings();
await settingsPage.goToSettingSection('wallet');
await settingsPage.coinsTabButton.click();
await settingsPage.enableCoin('ada');
await settingsPage.enableCoin('btc');

Expand Down
13 changes: 7 additions & 6 deletions packages/suite-desktop-core/e2e/tests/general/eap-modal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import { test, expect } from '../../support/fixtures';
// TODO: #15561 FIX settings cleanup: eap setting is remembered even after cache cleanup at the beginning of the test. This shouldn't affect gha run but breaks the local one.
test.skip(!process.env.GITHUB_ACTION, 'Test is working only in CI. Skipping local run.');

test('Join early access button', async ({ dashboardPage, topBar, settingsPage }) => {
const buttonText = 'Leave';
test.beforeAll(async ({ onboardingPage }) => {
await onboardingPage.completeOnboarding();
});

dashboardPage.optionallyDismissFwHashCheckError();
test('Join early access button', async ({ topBar, settingsPage }) => {
const buttonText = 'Leave';
await topBar.openSettings();
await settingsPage.goToSettingSection('general');
await settingsPage.applicationTabButton.click();
await settingsPage.joinEarlyAccessProgram();

expect(await settingsPage.getEarlyAccessButtonText()).toContain(buttonText);
expect(await settingsPage.earlyAccessJoinButton.textContent()).toContain(buttonText);
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import { test, expect } from '../../support/fixtures';
* 3. Write into feedback field
* 4. Submit bug report (reporttext)
*/
test('Send a bug report', async ({ dashboardPage, suiteGuidePage }) => {
test('Send a bug report', async ({ onboardingPage, suiteGuidePage }) => {
const testData = {
desiredLocation: 'Account',
reportText: 'Henlo this is testy test writing hangry test user report',
};

dashboardPage.optionallyDismissFwHashCheckError();
onboardingPage.optionallyDismissFwHashCheckError();

await suiteGuidePage.openSidePanel();
await suiteGuidePage.openFeedback();
await suiteGuidePage.sendBugreport(testData);
await suiteGuidePage.sendBugReport(testData);

expect(await suiteGuidePage.getSuccessToast()).toBeTruthy();
await suiteGuidePage.closeGuide();
Expand Down
Loading

0 comments on commit 33dc0a7

Please sign in to comment.