Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .e2e.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,12 @@ export MULTICHAIN_DAPP_URL="https://metamask.github.io/test-dapp-multichain/late
# E2E prebuild paths
export PREBUILT_IOS_APP_PATH='build/MetaMask.app'
export PREBUILT_ANDROID_APK_PATH='build/MetaMask.apk'
export PREBUILT_ANDROID_TEST_APK_PATH='build/MetaMask-Test.apk'
export PREBUILT_ANDROID_TEST_APK_PATH='build/MetaMask-Test.apk'

export TEST_SRP_1=
export TEST_SRP_2=
export TEST_SRP_3=
export BROWSERSTACK_USERNAME=
export BROWSERSTACK_ACCESS_KEY=
export SEEDLESS_ONBOARDING_ENABLED=
export SOLANA_MODAL_ENABLED=
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module.exports = {
],
overrides: [
{
files: ['e2e/**/*.{js,ts}'],
files: ['e2e/**/*.{js,ts}', 'appwright/**/*.{js,ts}'],
extends: ['./e2e/framework/.eslintrc.js'],
},
{
Expand Down
11 changes: 9 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ buck-out/
# bundle artifact
*.jsbundle

# testing
tests

# app-specific
/scripts/inpage-bridge/dist
Expand Down Expand Up @@ -110,6 +108,15 @@ e2e/reports
# Performance test results
e2e/specs/performance/reports/*-performance-results.json

# appwright
appwright/report
playwright-report/*
appwright/test-reports/
test-results/
test-reports/
appwright/reporters/reports/*
*.apk

# anvil binaries
.metamask/*
# ppom
Expand Down
70 changes: 70 additions & 0 deletions appwright/appwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// In appwright.config.ts
import dotenv from 'dotenv';
dotenv.config({ path: '.e2e.env' });
import { defineConfig, Platform } from 'appwright';
export default defineConfig({
testMatch: '**/tests/performance/*.spec.js',
timeout: 7 * 60 * 1000, //7 minutes until we introduce fixtures
expect: {
timeout: 30 * 1000, //30 seconds
},
reporter: [
// The default HTML reporter from Appwright
[
'html',
{ open: 'never', outputFolder: './test-reports/appwright-report' },
],
['./reporters/custom-reporter.js'],
['list'],
],

projects: [
{
name: 'android',
use: {
platform: Platform.ANDROID,
device: {
provider: 'emulator',
name: 'Samsung Galaxy S24 Ultra', // this can be changed to your emulator name
osVersion: '14', // this can be changed to your emulator version
},
buildPath: 'PATH-TO-BUILD', // Path to your .apk file
},
},
{
name: 'ios',
use: {
platform: Platform.IOS,
device: {
provider: 'emulator',
osVersion: '16.0', // this can be changed to your simulator version
},
buildPath: 'PATH-TO-BUILD', // Path to your .app file
},
},
{
name: 'browserstack-android',
use: {
platform: Platform.ANDROID,
device: {
provider: 'browserstack',
name: process.env.BROWSERSTACK_DEVICE || 'Samsung Galaxy S23 Ultra', // this can changed
osVersion: process.env.BROWSERSTACK_OS_VERSION || '13.0', // this can changed
},
buildPath: process.env.BROWSERSTACK_ANDROID_APP_URL, // Path to Browserstack url
},
},
{
name: 'browserstack-ios',
use: {
platform: Platform.IOS,
device: {
provider: 'browserstack',
name: process.env.BROWSERSTACK_DEVICE || 'iPhone 14 Pro Max',
osVersion: process.env.BROWSERSTACK_OS_VERSION || '16.0',
},
buildPath: process.env.BROWSERSTACK_IOS_APP_URL, // Path to Browserstack url
},
},
],
});
17 changes: 10 additions & 7 deletions appwright/reporters/custom-reporter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable */
// import fs from 'fs';
// import path from 'path';
/* eslint-disable import/no-nodejs-modules */
import fs from 'fs';
import path from 'path';

class CustomReporter {
constructor() {
Expand Down Expand Up @@ -72,6 +72,7 @@ class CustomReporter {
);
fs.writeFileSync(jsonPath, JSON.stringify(this.metrics, null, 2));
// Generate HTML report
/* eslint-disable */
const html = `
<!DOCTYPE html>
<html>
Expand All @@ -82,10 +83,10 @@ class CustomReporter {
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
th { background-color: #4CAF50; color: white; }
tr:nth-child(even) { background-color: #f2f2f2; }
.total { font-weight: bold; background-color: #e7f3e7; }
th, td { border: 1px solid #e0e0e0; padding: 12px; text-align: left; }
th { background-color: #2e7d32; color: white; }
tr:nth-child(even) { background-color: #f5f5f5; }
.total { font-weight: bold; background-color: #e8f5e8; }
</style>
</head>
<body>
Expand Down Expand Up @@ -120,6 +121,7 @@ class CustomReporter {
</tr>
`;
}
return ''; // Return empty string for device key
})
.join('')}
</table>
Expand All @@ -130,6 +132,7 @@ class CustomReporter {
</body>
</html>
`;
/* eslint-enable */

const reportPath = path.join(
reportsDir,
Expand Down
74 changes: 74 additions & 0 deletions appwright/tests/performance/scenario1.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { test, expect } from 'appwright';

import TimerHelper from '../../utils/TimersHelper.js';
import { PerformanceTracker } from '../../reporters/PerformanceTracker.js';
import WelcomeScreen from '../../../wdio/screen-objects/Onboarding/OnboardingCarousel.js';
import TermOfUseScreen from '../../../wdio/screen-objects/Modals/TermOfUseScreen.js';
import OnboardingScreen from '../../../wdio/screen-objects/Onboarding/OnboardingScreen.js';
import CreateNewWalletScreen from '../../../wdio/screen-objects/Onboarding/CreateNewWalletScreen.js';
import MetaMetricsScreen from '../../../wdio/screen-objects/Onboarding/MetaMetricsScreen.js';
import OnboardingSucessScreen from '../../../wdio/screen-objects/OnboardingSucessScreen.js';
import OnboardingSheet from '../../../wdio/screen-objects/Onboarding/OnboardingSheet.js';
import SolanaFeatureSheet from '../../../wdio/screen-objects/Modals/SolanaFeatureSheet.js';
import WalletAccountModal from '../../../wdio/screen-objects/Modals/WalletAccountModal.js';
import SkipAccountSecurityModal from '../../../wdio/screen-objects/Modals/SkipAccountSecurityModal.js';
import ImportFromSeedScreen from '../../../wdio/screen-objects/Onboarding/ImportFromSeedScreen.js';
import CreatePasswordScreen from '../../../wdio/screen-objects/Onboarding/CreatePasswordScreen.js';
import AccountListComponent from '../../../wdio/screen-objects/AccountListComponent.js';
import AddAccountModal from '../../../wdio/screen-objects/Modals/AddAccountModal.js';
import WalletMainScreen from '../../../wdio/screen-objects/WalletMainScreen.js';
import { importSRPFlow, onboardingFlowImportSRP } from '../../utils/Flows.js';

test('Account creation with 50+ accounts, SRP 1 + SRP 2 + SRP 3', async ({
device,
}, testInfo) => {
WelcomeScreen.device = device;
TermOfUseScreen.device = device;
OnboardingScreen.device = device;
CreateNewWalletScreen.device = device;
MetaMetricsScreen.device = device;
OnboardingSucessScreen.device = device;
OnboardingSheet.device = device;
SolanaFeatureSheet.device = device;
WalletAccountModal.device = device;
SkipAccountSecurityModal.device = device;
ImportFromSeedScreen.device = device;
CreatePasswordScreen.device = device;
WalletMainScreen.device = device;
AccountListComponent.device = device;
AddAccountModal.device = device;

await onboardingFlowImportSRP(device, process.env.TEST_SRP_1);
await importSRPFlow(device, process.env.TEST_SRP_2);
await importSRPFlow(device, process.env.TEST_SRP_3);

const screen1Timer = new TimerHelper(
'Time since the user clicks on "Account list" button until the account list is visible',
);
const screen2Timer = new TimerHelper(
'Time since the user clicks on "Add account" button until the next modal is visible',
);
const screen3Timer = new TimerHelper(
'Time since the user clicks on "Create Ethereum account" button until the Token list is visible',
);

await WalletMainScreen.isTokenVisible('Ethereum');
screen1Timer.start();
await WalletMainScreen.tapIdenticon();
await AccountListComponent.isComponentDisplayed();
screen1Timer.stop();
screen2Timer.start();
await AccountListComponent.tapAddAccountButton();
screen2Timer.stop();
screen3Timer.start();
await AddAccountModal.tapCreateEthereumAccountButton();
await WalletMainScreen.isTokenVisible('Ethereum');
screen3Timer.stop();

const performanceTracker = new PerformanceTracker();
performanceTracker.addTimer(screen1Timer);
performanceTracker.addTimer(screen2Timer);
performanceTracker.addTimer(screen3Timer);

await performanceTracker.attachToTest(testInfo);
});
115 changes: 115 additions & 0 deletions appwright/tests/performance/scenario2.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { test, expect } from 'appwright';

import TimerHelper from '../../utils/TimersHelper.js';
import { PerformanceTracker } from '../../reporters/PerformanceTracker.js';
import WelcomeScreen from '../../../wdio/screen-objects/Onboarding/OnboardingCarousel.js';
import TermOfUseScreen from '../../../wdio/screen-objects/Modals/TermOfUseScreen.js';
import OnboardingScreen from '../../../wdio/screen-objects/Onboarding/OnboardingScreen.js';
import CreateNewWalletScreen from '../../../wdio/screen-objects/Onboarding/CreateNewWalletScreen.js';
import MetaMetricsScreen from '../../../wdio/screen-objects/Onboarding/MetaMetricsScreen.js';
import OnboardingSucessScreen from '../../../wdio/screen-objects/OnboardingSucessScreen.js';
import OnboardingSheet from '../../../wdio/screen-objects/Onboarding/OnboardingSheet.js';
import SolanaFeatureSheet from '../../../wdio/screen-objects/Modals/SolanaFeatureSheet.js';
import WalletAccountModal from '../../../wdio/screen-objects/Modals/WalletAccountModal.js';
import SkipAccountSecurityModal from '../../../wdio/screen-objects/Modals/SkipAccountSecurityModal.js';
import WalletMainScreen from '../../../wdio/screen-objects/WalletMainScreen.js';

const SOLANA_MODAL_ENABLED = process.env.SOLANA_MODAL_ENABLED
? process.env.SOLANA_MODAL_ENABLED
: false;
test('Onboarding new wallet, SRP 1 + SRP 2 + SRP 3', async ({
device,
}, testInfo) => {
const screen1Timer = new TimerHelper(
'Time until the user clicks on the "Get Started" button',
);
screen1Timer.start();
WelcomeScreen.device = device;
TermOfUseScreen.device = device;
OnboardingScreen.device = device;
CreateNewWalletScreen.device = device;
MetaMetricsScreen.device = device;
OnboardingSucessScreen.device = device;
OnboardingSheet.device = device;
SolanaFeatureSheet.device = device;
WalletAccountModal.device = device;
SkipAccountSecurityModal.device = device;
WalletMainScreen.device = device;

const timer1 = new TimerHelper(
'Time since the user clicks on "Get Started" button until the Term of Use screen is visible',
);
const timer2 = new TimerHelper(
'Time since the user clicks on "Aggree to T&C" button until the Onboarding screen is visible',
);
const timer3 = new TimerHelper(
'Time since the user clicks on "Create new wallet" button until "continue with SRP" button is visible',
);
const timer4 = new TimerHelper(
'Time since the user clicks on "Continue with SRP" button until password fields are visible',
);
const timer5 = new TimerHelper(
'Time since the user clicks on "Create Password" button until "Remind me later" shows up',
);
const timer6 = new TimerHelper(
'Time since the user clicks on "Proceed without wallet secure" button until Metrics screen is displayed',
);
const timer7 = new TimerHelper(
'Time since the user clicks on "Aggree" button on Metrics screen until Onboarding Success screen is visible',
);
const timer8 = new TimerHelper(
'Time since the user clicks on "Done" button until Solana feature sheet is visible',
);

timer1.start();
await WelcomeScreen.clickGetStartedButton();
await TermOfUseScreen.isDisplayed();
timer1.stop();
await TermOfUseScreen.tapAgreeCheckBox();
await TermOfUseScreen.tapScrollEndButton();
timer2.start();
await TermOfUseScreen.tapAcceptButton();
await OnboardingScreen.isScreenTitleVisible();
timer2.stop();
timer3.start();
await OnboardingScreen.tapCreateNewWalletButton();
await OnboardingSheet.isVisible();
timer3.stop();
timer4.start();
await OnboardingSheet.tapImportSeedButton();
await CreateNewWalletScreen.isNewAccountScreenFieldsVisible();
timer4.stop();
await CreateNewWalletScreen.inputPasswordInFirstField('123456789');
await CreateNewWalletScreen.inputConfirmPasswordField('123456789');
timer5.start();
await CreateNewWalletScreen.tapSubmitButton();
await CreateNewWalletScreen.tapRemindMeLater();
await SkipAccountSecurityModal.isVisible();
timer5.stop();
timer6.start();
await SkipAccountSecurityModal.proceedWithoutWalletSecure();
await MetaMetricsScreen.isScreenTitleVisible();
timer6.stop();
timer7.start();
await MetaMetricsScreen.tapIAgreeButton();
await OnboardingSucessScreen.isVisible();
timer7.stop();
timer8.start();
await OnboardingSucessScreen.tapDone();
if (SOLANA_MODAL_ENABLED) {
await SolanaFeatureSheet.isVisible();
await SolanaFeatureSheet.tapNotNowButton();
}
timer8.stop();

const performanceTracker = new PerformanceTracker();
performanceTracker.addTimer(timer1);
performanceTracker.addTimer(timer2);
performanceTracker.addTimer(timer3);
performanceTracker.addTimer(timer4);
performanceTracker.addTimer(timer5);
performanceTracker.addTimer(timer6);
performanceTracker.addTimer(timer7);
performanceTracker.addTimer(timer8);
await performanceTracker.attachToTest(testInfo);
});
Loading
Loading