Skip to content

Commit

Permalink
feat(e2e): Migrated backup tests (#17150)
Browse files Browse the repository at this point in the history
* feat(e2e): Migrated backup tests

* fix(ci): Removed device management group from cypress tests pipeline

* fix(e2e): Added missing await

* fix(e2e): Fixed PR review comments
  • Loading branch information
HajekOndrej authored Feb 24, 2025
1 parent 73ba107 commit a010814
Show file tree
Hide file tree
Showing 14 changed files with 256 additions and 262 deletions.
3 changes: 0 additions & 3 deletions .github/workflows/test-suite-web-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,6 @@ jobs:
fail-fast: false
matrix:
include:
- TEST_GROUP: "@group_device-management"
CONTAINERS: "trezor-user-env-unix"
CYPRESS_USE_TREZOR_USER_ENV_BRIDGE: "1"
- TEST_GROUP: "@group_wallet"
CONTAINERS: "trezor-user-env-unix bitcoin-regtest"
CYPRESS_USE_TREZOR_USER_ENV_BRIDGE: "1"
Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/test-suite-web-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ jobs:
- TEST_GROUP: "@group_suite"
CONTAINERS: "trezor-user-env-unix"
CYPRESS_USE_TREZOR_USER_ENV_BRIDGE: "1"
- TEST_GROUP: "@group_device-management"
CONTAINERS: "trezor-user-env-unix"
CYPRESS_USE_TREZOR_USER_ENV_BRIDGE: "1"
- TEST_GROUP: "@group_wallet"
CONTAINERS: "trezor-user-env-unix bitcoin-regtest"
CYPRESS_USE_TREZOR_USER_ENV_BRIDGE: "1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class DashboardActions {
readonly passphraseSubmitButton: Locator;
readonly passphraseShowButton: Locator;
readonly loading: Locator;
readonly notificationNoBackupButton: Locator;

constructor(
private readonly page: Page,
Expand Down Expand Up @@ -62,6 +63,7 @@ export class DashboardActions {
this.passphraseSubmitButton = this.page.getByTestId('@passphrase/hidden/submit-button');
this.passphraseShowButton = this.page.getByTestId('@passphrase/show-toggle');
this.loading = this.page.getByTestId('@dashboard/loading');
this.notificationNoBackupButton = this.page.getByTestId('@notification/no-backup/button');
}

@step()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { DevicePromptActions } from '../devicePromptActions';

export class BackupActions {
readonly startButton: Locator;
readonly undertandWhatSeedIsCheckbox: Locator;
readonly hasEnoughTimeCheckbox: Locator;
readonly isInPrivateCheckbox: Locator;
readonly wroteSeedProperlyCheckbox: Locator;
readonly madeNoDigitalCopyCheckbox: Locator;
readonly willHideSeedCheckbox: Locator;
Expand All @@ -15,6 +18,11 @@ export class BackupActions {
private devicePrompt: DevicePromptActions,
) {
this.startButton = page.getByTestId('@backup/start-button');
this.undertandWhatSeedIsCheckbox = page.getByTestId(
'@backup/check-item/understands-what-seed-is',
);
this.hasEnoughTimeCheckbox = page.getByTestId('@backup/check-item/has-enough-time');
this.isInPrivateCheckbox = page.getByTestId('@backup/check-item/is-in-private');
this.wroteSeedProperlyCheckbox = page.getByTestId('@backup/check-item/wrote-seed-properly');
this.madeNoDigitalCopyCheckbox = page.getByTestId(
'@backup/check-item/made-no-digital-copy',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Locator, Page } from '@playwright/test';

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

export class DeviceActions {
readonly createMultiShareBackupButton: Locator;
readonly multiShareBackupGotItButton: Locator;
private readonly firstInfoSubmitButton: Locator;
private readonly secondInfoSubmitButton: Locator;

constructor(private readonly page: Page) {
this.createMultiShareBackupButton = page.getByTestId(
'@settings/device/create-multi-share-backup-button',
);
this.multiShareBackupGotItButton = page.getByTestId(
'@multi-share-backup/done/got-it-button',
);
this.firstInfoSubmitButton = page.getByTestId('@multi-share-backup/1st-info/submit-button');
this.secondInfoSubmitButton = page.getByTestId(
'@multi-share-backup/2nd-info/submit-button',
);
}

@step()
async proceedMultiShareBackupModal(): Promise<void> {
await this.page.getByTestId('@multi-share-backup/checkbox/1').click();
await this.page.getByTestId('@multi-share-backup/checkbox/2').click();
await this.firstInfoSubmitButton.click();
await this.secondInfoSubmitButton.click();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Locator, Page, test } from '@playwright/test';
import { capitalizeFirstLetter } from '@trezor/utils';

import { CoinsActions } from './coinActions';
import { DeviceActions } from './deviceActions';
import { TrezorUserEnvLinkProxy, step } from '../../common';
import { expect } from '../../customMatchers';

Expand Down Expand Up @@ -38,6 +39,7 @@ const backgroundImages = {
export class SettingsActions {
private readonly TIMES_CLICK_TO_SET_DEBUG_MODE = 5;
readonly coins: CoinsActions;
readonly device: DeviceActions;

readonly settingsMenuButton: Locator;
readonly settingsHeader: Locator;
Expand Down Expand Up @@ -73,6 +75,7 @@ export class SettingsActions {
private readonly apiURL: string,
) {
this.coins = new CoinsActions(page);
this.device = new DeviceActions(page);

this.settingsMenuButton = this.page.getByTestId('@suite/menu/settings');
this.settingsHeader = this.page.getByTestId('@settings/menu/title');
Expand Down
52 changes: 52 additions & 0 deletions packages/suite-desktop-core/e2e/tests/backup/t2t1-fail.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { EventType } from '@trezor/suite-analytics';
import { ExtractByEventType } from '@trezor/suite-web/e2e/support/types';

import { expect, test } from '../../support/fixtures';

test.describe('Backup fail', { tag: ['@group=device-management'] }, () => {
test.use({
emulatorStartConf: { model: 'T2T1', wipe: true },
emulatorSetupConf: { needs_backup: true },
});

test.beforeEach(async ({ onboardingPage, analytics }) => {
await onboardingPage.completeOnboarding();
await analytics.interceptAnalytics();
});

test('Device disconnected during action', async ({
page,
analytics,
onboardingPage,
dashboardPage,
devicePrompt,
trezorUserEnvLink,
}) => {
await dashboardPage.notificationNoBackupButton.click();
await onboardingPage.backup.undertandWhatSeedIsCheckbox.click();
await onboardingPage.backup.hasEnoughTimeCheckbox.click();
await onboardingPage.backup.isInPrivateCheckbox.click();
await onboardingPage.backup.startButton.click();
await devicePrompt.confirmOnDevicePromptIsShown();
await trezorUserEnvLink.pressYes();
await trezorUserEnvLink.stopEmu();

await expect(page.getByTestId('@backup/no-device')).toBeVisible();

await trezorUserEnvLink.startEmu();

await expect(page.getByTestId('@backup/error-message')).toBeVisible({ timeout: 30000 });

// Now go to dashboard and see if security card and notification reflects backup failed state correctly
await onboardingPage.backup.closeButton.click();
await expect(page.getByTestId('@notification/failed-backup/cta')).toBeVisible();

const createBackupEvent = analytics.findAnalyticsEventByType<
ExtractByEventType<EventType.CreateBackup>
>(EventType.CreateBackup);
expect(createBackupEvent.status).toEqual('error');
expect(createBackupEvent.error).toMatch(
/device\+disconnected\+during\+action|Device\+disconnected|session\+not\+found/,
);
});
});
54 changes: 54 additions & 0 deletions packages/suite-desktop-core/e2e/tests/backup/t2t1-misc.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { expect, test } from '../../support/fixtures';

test.describe('Backup misc', { tag: ['@group=device-management'] }, () => {
test.use({
emulatorStartConf: { model: 'T2T1', wipe: true },
emulatorSetupConf: { needs_backup: true },
});

test.beforeEach(async ({ onboardingPage, dashboardPage }) => {
await onboardingPage.completeOnboarding();
await dashboardPage.discoveryShouldFinish();
});

test('Backup should reset if modal is closed', async ({ onboardingPage, dashboardPage }) => {
await dashboardPage.notificationNoBackupButton.click();
await onboardingPage.backup.undertandWhatSeedIsCheckbox.click();
await onboardingPage.backup.hasEnoughTimeCheckbox.click();
await onboardingPage.backup.isInPrivateCheckbox.click();
await expect(
onboardingPage.backup.undertandWhatSeedIsCheckbox.locator('input'),
).toBeChecked();
await expect(onboardingPage.backup.hasEnoughTimeCheckbox.locator('input')).toBeChecked();
await expect(onboardingPage.backup.isInPrivateCheckbox.locator('input')).toBeChecked();
await onboardingPage.backup.closeButton.click();
await dashboardPage.notificationNoBackupButton.click();

//at this moment, after modal was closed and opened again, no checkbox should be checked
await expect(
onboardingPage.backup.undertandWhatSeedIsCheckbox.locator('input'),
).not.toBeChecked();
await expect(
onboardingPage.backup.hasEnoughTimeCheckbox.locator('input'),
).not.toBeChecked();
await expect(onboardingPage.backup.isInPrivateCheckbox.locator('input')).not.toBeChecked();
});

test('User disconnected device that is remembered. Should not be allowed to initiate backup', async ({
page,
dashboardPage,
onboardingPage,
trezorUserEnvLink,
}) => {
await expect(dashboardPage.graph).toBeVisible();
await dashboardPage.openDeviceSwitcher();
await dashboardPage.walletAtIndex(0).click();
await dashboardPage.notificationNoBackupButton.click();
await onboardingPage.backup.undertandWhatSeedIsCheckbox.click();
await onboardingPage.backup.hasEnoughTimeCheckbox.click();
await onboardingPage.backup.isInPrivateCheckbox.click();

await trezorUserEnvLink.stopEmu();
await expect(page.getByTestId('@backup/no-device')).toBeVisible();
});
});
62 changes: 62 additions & 0 deletions packages/suite-desktop-core/e2e/tests/backup/t2t1-success.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { EventType } from '@trezor/suite-analytics';
import { ExtractByEventType } from '@trezor/suite-web/e2e/support/types';

import { expect, test } from '../../support/fixtures';

test.describe('Backup success', { tag: ['@group=device-management'] }, () => {
test.use({
emulatorStartConf: { model: 'T2T1', wipe: true },
emulatorSetupConf: { needs_backup: true, mnemonic: 'mnemonic_all' },
});

test.beforeEach(async ({ onboardingPage, analytics }) => {
await analytics.interceptAnalytics();
await onboardingPage.completeOnboarding();
});

test('Successful backup happy path', async ({
analytics,
onboardingPage,
dashboardPage,
devicePrompt,
trezorUserEnvLink,
}) => {
// access from notification
await dashboardPage.notificationNoBackupButton.click();

await onboardingPage.backup.undertandWhatSeedIsCheckbox.click();
await onboardingPage.backup.hasEnoughTimeCheckbox.click();
await onboardingPage.backup.isInPrivateCheckbox.click();

// Create backup on device
await onboardingPage.backup.startButton.click();

await devicePrompt.confirmOnDevicePromptIsShown();

//await trezorUserEnvLink.readAndConfirmMnemonicEmu(); should be used here, but it is flaky
// TODO: https://github.com/trezor/trezor-suite/issues/17148
await trezorUserEnvLink.pressYes();
await trezorUserEnvLink.pressYes();
await trezorUserEnvLink.swipeEmu('up');
await trezorUserEnvLink.swipeEmu('up');
await trezorUserEnvLink.pressYes();
await trezorUserEnvLink.inputEmu('all');
await trezorUserEnvLink.inputEmu('all');
await trezorUserEnvLink.inputEmu('all');
await trezorUserEnvLink.pressYes();
await trezorUserEnvLink.pressYes();

// Click all after checkboxes and close backup modal
await expect(onboardingPage.backup.closeButton).toBeDisabled();
await onboardingPage.backup.wroteSeedProperlyCheckbox.click();
await onboardingPage.backup.madeNoDigitalCopyCheckbox.click();
await onboardingPage.backup.willHideSeedCheckbox.click();
await expect(onboardingPage.backup.closeButton).toBeEnabled();

const createBackupEvent = analytics.findAnalyticsEventByType<
ExtractByEventType<EventType.CreateBackup>
>(EventType.CreateBackup);
expect(createBackupEvent.status).toEqual('finished');
expect(createBackupEvent.error).toEqual('');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { MNEMONICS } from '@trezor/trezor-user-env-link';

import { test } from '../../support/fixtures';

test.describe('Create additional share', { tag: ['@group=device-management'] }, () => {
test.use({
emulatorSetupConf: { mnemonic: 'mnemonic_academic' },
});

test.beforeEach(async ({ onboardingPage }) => {
await onboardingPage.completeOnboarding({ enableViewOnly: true });
});

test('Successfuly added additional share', async ({
page,
settingsPage,
trezorUserEnvLink,
}) => {
await settingsPage.navigateTo('device');
await settingsPage.device.createMultiShareBackupButton.click();
await settingsPage.device.proceedMultiShareBackupModal();

// [device screen] check your backup?
await trezorUserEnvLink.pressYes();

// [device screen] select the number of words in your backup
await trezorUserEnvLink.inputEmu('20');

// [device screen] backup instructions
await trezorUserEnvLink.pressYes();

for (const word of MNEMONICS.mnemonic_academic.split(' ')) {
// [device screen] enter next word
await trezorUserEnvLink.inputEmu(word);
}

// [device screen] create additional backup?
await page.waitForTimeout(1000); // without this timeout, backup on device simply disappears, it stinks TODO: https://github.com/trezor/trezor-suite/issues/17128
await trezorUserEnvLink.pressYes();
await trezorUserEnvLink.readAndConfirmShamirMnemonicEmu({ shares: 3, threshold: 2 });

await settingsPage.device.multiShareBackupGotItButton.click();
});
});
55 changes: 0 additions & 55 deletions packages/suite-web/e2e/tests/backup/t2t1-fail.test.ts

This file was deleted.

Loading

0 comments on commit a010814

Please sign in to comment.