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(e2e): Testnet assets FE-889 #1707

Merged
merged 18 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
501896f
chore: remove VITE_ADDR_OWNER from environment variables
nelitow Dec 11, 2024
14eef3a
test(e2e): add asset validation tests and update workflow environment
nelitow Dec 12, 2024
30e09a2
Merge branch 'master' into nj/test/e2e-for-wallet-assets
nelitow Dec 12, 2024
66ba10f
feat(e2e-assets): add end-to-end tests and configuration for asset va…
nelitow Dec 12, 2024
5aa2b8d
refactor(e2e-assets): update end-to-end test configurations and remov…
nelitow Dec 12, 2024
466f3d4
refactor(e2e-assets): improve asset input handling with logging for b…
nelitow Dec 12, 2024
f564556
refactor(e2e-assets): streamline asset tests and enhance page navigat…
nelitow Dec 12, 2024
5c044f5
feat(e2e-assets): add test report upload step for failed end-to-end t…
nelitow Dec 12, 2024
937d66e
refactor(e2e-assets): add logging for wallet mnemonic import in asset…
nelitow Dec 12, 2024
c14a18a
refactor(e2e-assets): simplify wallet mnemonic import by using clipbo…
nelitow Dec 12, 2024
35dbb2e
refactor(e2e-assets): add wait timeout before pasting seed phrase in …
nelitow Dec 12, 2024
804c479
refactor(e2e-assets): add clipboard permissions to Playwright configu…
nelitow Dec 12, 2024
20a9148
refactor(e2e-assets): streamline seed phrase import by removing unnec…
nelitow Dec 12, 2024
d72089f
refactor(e2e-assets): extract wallet import logic into a separate fun…
nelitow Dec 12, 2024
364f16a
Create dry-cobras-rescue.md
nelitow Dec 13, 2024
9cdf9b5
refactor(e2e-assets): disable tests for added tokens until refresh is…
nelitow Dec 13, 2024
ad37ef8
refactor(e2e-assets): increase the number of workers in Playwright co…
nelitow Dec 13, 2024
5485555
refactor(e2e-assets): update package version and clean up configurati…
nelitow Dec 13, 2024
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
2 changes: 2 additions & 0 deletions .changeset/dry-cobras-rescue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
38 changes: 38 additions & 0 deletions .github/workflows/pr-tests-e2e-assets.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Tests E2E - Assets Testnet

on:
pull_request:
branches: [main, master, sdk-v2]
types: [opened, synchronize, reopened]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
tests-e2e-assets:
name: Test
timeout-minutes: 10
runs-on: buildjet-8vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v3
- uses: FuelLabs/github-actions/setups/node@master
with:
node-version: 20.11.0
pnpm-version: 9.5.0

- uses: ./.github/actions/setup-playwright

- name: Run E2E Tests - Assets Testnet
run: xvfb-run --auto-servernum -- pnpm test:e2e:assets
timeout-minutes: 15
env:
NODE_ENV: test
READONLY_TESTNET_ASSETS_VIEW: ${{secrets.READONLY_TESTNET_ASSETS_VIEW}}

- name: Upload Test Report
if: failure()
uses: actions/upload-artifact@v3
with:
name: e2e-test-report
path: packages/e2e-assets/playwright-html
1 change: 1 addition & 0 deletions .github/workflows/pr-tests-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ jobs:
timeout-minutes: 15
env:
NODE_ENV: test
READONLY_TESTNET_ASSETS_VIEW: ${{ secrets.READONLY_TESTNET_ASSETS_VIEW }}

- uses: actions/upload-artifact@v4
if: always()
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"test:e2e": "NODE_ENV=test pnpm build:crx && playwright test --config=packages/app/playwright.config.ts --project=chromium",
"test:e2e:beta": "NODE_ENV=test pnpm build:crx && playwright test --config=packages/app/playwright.config.ts --project=chrome-beta",
"test:e2e:crx-lock": "NODE_ENV=test pnpm build:crx && playwright test --config=packages/app/playwright.crx-lock.config.ts",
"test:e2e:assets": "NODE_ENV=test pnpm playwright test --config=packages/e2e-assets/playwright.config.ts",
"test:e2e:contracts": "NODE_ENV=test pnpm build:crx && pnpm --filter=@fuel-wallet/e2e-contract-tests test:e2e",
"test:appfile": "pnpm --filter=fuels-wallet test --",
"ts:check": "turbo run ts:check",
Expand Down
1 change: 0 additions & 1 deletion packages/app/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ VITE_FUEL_PROVIDER_URL=http://localhost:4000/v1/graphql
VITE_FUEL_FAUCET_URL=http://localhost:4040
VITE_EXPLORER_URL=https://app.fuel.network/
VITE_MNEMONIC_WORDS=12
VITE_ADDR_OWNER=0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298
GENESIS_SECRET=0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298
VITE_AUTO_LOCK_IN_MINUTES=1
VITE_SENTRY_DSN=
1 change: 0 additions & 1 deletion packages/app/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ declare namespace NodeJS {
readonly VITE_FUEL_PROVIDER_URL: string;
readonly VITE_FUEL_FAUCET_URL: string;
readonly VITE_MNEMONIC_WORDS: string;
readonly VITE_ADDR_OWNER: string;
readonly VITE_CRX_NAME: string;
readonly VITE_CRX_VERSION_API: string;
readonly VITE_CRX_RELEASE: string;
Expand Down
1 change: 0 additions & 1 deletion packages/app/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export const {
VITE_MNEMONIC_WORDS,
VITE_FUEL_PROVIDER_URL,
VITE_FUEL_FAUCET_URL,
VITE_ADDR_OWNER,
VITE_APP_VERSION,
VITE_DATABASE_VERSION,
VITE_CRX_NAME,
Expand Down
1 change: 0 additions & 1 deletion packages/app/src/vite-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ interface ImportMetaEnv {
readonly VITE_APP_VERSION: string;
readonly VITE_CRX: string;
readonly VITE_CRX_VERSION_API: string;
readonly VITE_ADDR_OWNER: string;
readonly VITE_CRX_NAME: string;
readonly VITE_AUTO_LOCK_IN_MINUTES: number;
readonly VITE_SENTRY_DSN: string;
Expand Down
2 changes: 2 additions & 0 deletions packages/e2e-assets/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
READONLY_TESTNET_ASSETS_VIEW='this mnemonic is available in the project secrets'
PORT=3000
Empty file.
Empty file.
44 changes: 44 additions & 0 deletions packages/e2e-assets/load.envs.cts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { config } from 'dotenv';

function getVersion() {
const packageJson = JSON.parse(
readFileSync(resolve(__dirname, './package.json')).toString()
);
return {
version: packageJson.version,
database: packageJson.database,
};
}

function getEnvName() {
if (process.env.NODE_ENV === 'production') {
return '.env.production';
}
}

// Load from more specific env file to generic ->
// biome-ignore lint/complexity/noForEach: <explanation>
[getEnvName(), '.env'].forEach((envFile) => {
if (!envFile) return;
config({
path: resolve(__dirname, envFile),
});
});

export function getPublicEnvs() {
const WHITELIST = ['NODE_ENV', 'PUBLIC_URL'];
return Object.fromEntries(
Object.entries(process.env).filter(([key]) =>
WHITELIST.some((k) => k === key || key.match(/^VITE_/))
)
);
}

// Export the version to be used on database
// and application level
const versions = getVersion();
process.env.PORT = '3000';
process.env.VITE_APP_VERSION = process.env.VITE_APP_VERSION || versions.version;
process.env.VITE_DATABASE_VERSION = versions.database;
9 changes: 9 additions & 0 deletions packages/e2e-assets/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "@fuel-wallet/e2e-assets",
"private": true,
"version": "0.1.0",
"type": "module",
"devDependencies": {
"@playwright/test": "1.46.1"
}
}
31 changes: 31 additions & 0 deletions packages/e2e-assets/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { defineConfig } from '@playwright/test';
import './load.envs.cts';
const PORT = process.env.PORT || 3000;
const IS_CI = process.env.CI;

export default defineConfig({
workers: 2,
retries: IS_CI ? 1 : 0,
testMatch: 'playwright/**/*.test.ts',
testDir: 'playwright/',
outputDir: 'playwright-results/',
maxFailures: IS_CI ? 2 : undefined,
reporter: [
['list', { printSteps: true }],
['html', { outputFolder: './playwright-html/' }],
],
webServer: {
command: 'NODE_ENV=test pnpm -w run dev:crx',
reuseExistingServer: true,
timeout: 20000,
url: `http://localhost:${PORT}`,
},
use: {
baseURL: `http://localhost:${PORT}/`,
trace: 'on-first-retry',
actionTimeout: 5000,
permissions: ['clipboard-read', 'clipboard-write'],
screenshot: 'only-on-failure',
headless: false,
},
});
2 changes: 2 additions & 0 deletions packages/e2e-assets/playwright/commons/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './locator';
export * from './text';
17 changes: 17 additions & 0 deletions packages/e2e-assets/playwright/commons/locator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Page } from '@playwright/test';

export function getByAriaLabel(page: Page, selector: string) {
return page.locator(`[aria-label="${selector}"]`);
}

export async function waitAriaLabel(page: Page, selector: string) {
return page.waitForSelector(`[aria-label="${selector}"]`);
}

export function getInputByName(page: Page, name: string) {
return page.locator(`input[name="${name}"]`);
}

export function getInputByValue(page: Page, value: string) {
return page.locator(`input[value='${value}']`);
}
29 changes: 29 additions & 0 deletions packages/e2e-assets/playwright/commons/text.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Page } from '@playwright/test';
import { expect } from '@playwright/test';

export async function hasText(
page: Page,
text: string | RegExp,
position = 0,
timeout = 5000
) {
const textFound = page.getByText(text).nth(position);
await expect(textFound).toHaveText(text, {
useInnerText: true,
timeout,
});
return textFound;
}

export async function hasNoText(
page: Page,
text: string | RegExp,
position = 0
) {
return await expect(page.getByText(text).nth(position)).rejects.toThrow();
}

export async function hasAriaLabel(page: Page, value: string) {
const selector = await page.waitForSelector(`[aria-label="${value}"]`);
expect(await selector.getAttribute('aria-label')).toBe(value);
}
131 changes: 131 additions & 0 deletions packages/e2e-assets/playwright/crx/assets.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { type Browser, type Page, expect, test } from '@playwright/test';
import { hasText, waitAriaLabel } from '../commons';

const loadWallet = async (page: Page, _browser: Browser) => {
await page.goto('http://localhost:3000', {
waitUntil: 'domcontentloaded',
});

await test.step('Import wallet', async () => {
await page.getByRole('heading', { name: 'Import seed phrase' }).click();
await page.getByText('I Agree to the Terms Of Use').click();
await page.getByRole('button', { name: 'Next: Seed Phrase' }).click();
const mnemonic = process.env.READONLY_TESTNET_ASSETS_VIEW;

await page.evaluate(async (text) => {
await navigator.clipboard.writeText(text);
}, mnemonic);

await page.waitForTimeout(1000);
await page.getByRole('button', { name: 'Paste seed phrase' }).click();

await page.getByRole('button', { name: 'Next: Your password' }).click();
await page.getByPlaceholder('Type your password').fill('qwe123QWE!@#');
await page.getByPlaceholder('Confirm your password').fill('qwe123QWE!@#');
await page.getByRole('button', { name: 'Next: Finish set-up' }).click();
await hasText(page, /Wallet created successfully/i);
await page.goto('http://localhost:3000/#/wallet');
await waitAriaLabel(page, 'Account 1 selected');
await page.getByLabel('Selected Network').click();
await page.getByText('Fuel Sepolia Testnet').click();
await waitAriaLabel(page, 'Account 1 selected');
});
};

test.describe('Check assets', () => {
test.describe.configure({ mode: 'parallel' });
let page: Page;
test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
await loadWallet(page, browser);
});

test('should show valid asset value 0.002000', async () => {
expect(
await page.getByText('0.002000', { exact: true }).isVisible()
).toBeTruthy();
});

test('should show USDCIcon AlertTriangle', async () => {
expect(
await page.getByText('USDCIcon AlertTriangle').isVisible()
).toBeTruthy();
});

test('should show 1 SCAM NFT', async () => {
expect(await page.getByText('1 SCAM').isVisible()).toBeTruthy();
});

// Verified assets should never show the (Add) button
test('should not show (Add) button for verified assets', async () => {
expect(
await page.getByRole('button', { name: '(Add)' }).isVisible()
).toBeFalsy();
});

// Verified assets will never be inside of "Hidden assets" part
test('should not show verified assets in hidden assets', async () => {
// get all h6 text from div.fuel_CardList as an array, and then click Show unknown assets button, and then check if the array added a new element with a name other than Unknown
const h6Texts = await page.$$eval('div.fuel_CardList h6', (els) =>
els.map((el) => el.textContent)
);
await page.getByRole('button', { name: 'Show unknown assets' }).click();
await page.waitForTimeout(1000);
const h6TextsAfter = await page.$$eval('div.fuel_CardList h6', (els) =>
els.map((el) => el.textContent)
);
expect(h6TextsAfter.length).toBeGreaterThan(h6Texts.length);

for (const el of h6Texts) {
expect(el.includes('(Add)')).toBeFalsy();
}
// removing all elements from h6TextsAfter that are in h6Texts
const diff = h6TextsAfter.filter((el) => !h6Texts.includes(el));
// all elements in diff should include Unknown
for (const el of diff) {
expect(el.includes('Unknown')).toBeTruthy();
}
});
});

test.describe('Check assets', () => {
let page: Page;
test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
await loadWallet(page, browser);
});

test('Should add unknown asset', async () => {
await page.getByRole('button', { name: 'Show unknown assets' }).click();
await page.getByRole('button', { name: '(Add)' }).nth(1).click();
await page.getByPlaceholder('Asset name').fill('Token 2');
await page.getByPlaceholder('Asset symbol').fill('TKN2');
await page.getByLabel('Save Asset').click();
await page.waitForTimeout(1000);
await page.reload({ waitUntil: 'domcontentloaded' });
await page.waitForTimeout(1000);
await waitAriaLabel(page, 'Account 1 selected');
await page.waitForTimeout(1000);
expect(await page.getByText('1 TKN2').isVisible()).toBeTruthy();

// The following tests are disabled because the added tokens need a refresh to show up. Fix FE-1122 and enable these.

// await page.waitForTimeout(1000);
// await page.reload({ waitUntil: 'domcontentloaded' });
// await page.waitForTimeout(1000);
// // Non-verified asset that was added to asset list will never be inside of "Hidden assets" part
// // The TKN2 asset should not be in the hidden assets list
// const h6Texts = await page.$$eval('div.fuel_CardList h6', (els) =>
// els.map((el) => el.textContent?.trim())
// );
// await page.getByRole('button', { name: 'Show unknown assets' }).click();
// await page.waitForTimeout(1000);
// const h6TextsAfter = await page.$$eval('div.fuel_CardList h6', (els) =>
// els.map((el) => el.textContent?.trim())
// );
// const diff = h6TextsAfter.filter((el) => !h6Texts.includes(el));
// console.log(diff);
// // expect at least one of the elements in diff to be Token 2
// expect(diff.some((el) => el === 'Token 2')).toBeTruthy();
});
});
18 changes: 18 additions & 0 deletions packages/e2e-assets/tsconfig.node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"allowJs": true,
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"target": "es2017",
"outDir": "dist-crx"
},
"include": [
"load.envs.cts",
"env.d.ts",
"playwright/**/*.ts",
"./package.json"
]
}
Loading
Loading