Skip to content

Commit

Permalink
test(e2e): Testnet assets FE-889 (#1707)
Browse files Browse the repository at this point in the history
You'll need a specific mnemonic to run this locally. Ask me if needed.
- [x] Assets should show always the value in balance formatted correctly
- [x] Verified assets should never show the (Add) button
- [x] Verified assets will never be inside of "Hidden assets" part
- [x] Non-verified asset that was added to asset list will never show
the (Add) button
- [x] Non-verified asset that was added to asset list will never be
inside of "Hidden assets" part
- [x] Non-verified asset that was NOT added to asset list will always
show the (Add) button
- [x] Non-verified asset that was NOT added to asset list will always be
inside of "Hidden assets" part
  • Loading branch information
nelitow authored Dec 13, 2024
1 parent d5861eb commit 7764155
Show file tree
Hide file tree
Showing 20 changed files with 364 additions and 7 deletions.
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 added packages/e2e-assets/.gitignore
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

0 comments on commit 7764155

Please sign in to comment.