Skip to content

Commit

Permalink
test(cat-voices): playwright refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
emiride committed Nov 14, 2024
1 parent c7ba56d commit 5bd5615
Show file tree
Hide file tree
Showing 16 changed files with 358 additions and 159 deletions.
2 changes: 2 additions & 0 deletions .config/dictionaries/project.dic
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ tablestats
tacho
testcov
testdocs
testid
testplan
testunit
thiserror
Expand All @@ -280,6 +281,7 @@ Toastify
todos
toggleable
tojunit
tomjs
Traceback
traefik
trailings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ src:
COPY --dir utils ./utils
COPY playwright.config.ts .
COPY test-fixtures.ts .
COPY setup.ts .

package-test:
FROM +src
ENV APP_URL http://test-app:80
ENV APP_URL http://test-app:80
RUN mkdir /results
VOLUME /results
ENTRYPOINT ["/bin/sh", "-c", "/usr/bin/xvfb-run --auto-servernum npm test", ""]
Expand All @@ -31,12 +31,12 @@ package-test:

builder:
DO flutter-ci+SETUP
COPY ../../../../+repo-catalyst-voices-all/repo .
COPY ../../../../../+repo-catalyst-voices/repo .
DO flutter-ci+BOOTSTRAP

build-web:
FROM +builder
ARG WORKDIR=/frontend/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example
ARG WORKDIR=/frontend/packages/libs/catalyst_cardano/catalyst_cardano/example
DO flutter-ci+BUILD_WEB --TARGET=lib/main.dart --WORKDIR=$WORKDIR
SAVE ARTIFACT web

Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,49 @@
# Wallet Automation

Welcome to wallet automation, a testing package in Playwright that tests wallet integration for Catalyst Voices.

## Introduction

Wallet automation is a testing package in Playwright that automates the wallet creation process for the Catalyst project.
Wallet automation is a testing package in Playwright that automates the wallet creation process for the Catalyst project.
It is a part of the Catalyst Voices ecosystem.

## Getting Started

1. Clone this repository:

```sh
git clone
cd catalyst-voices
```
```sh
git clone
cd catalyst-voices
```

2. Install Flutter and Dart:

```sh
brew install flutter
```
```sh
brew install flutter
```

3. Bootstrap the project:

```sh
melos bootstrap
```
```sh
melos bootstrap
```

4. Execute earthly command from this directory:

```sh
earthly +package-app
```
```sh
earthly +package-app
```

5. Use docker compose to run the app:

```sh
docker compose up
```
The app should be running on `localhost:8000`.
```sh
docker compose up
```

The app should be running on `localhost:8000`.

6. You can now run tests with the following command:

```sh
npx playwright test
```
```sh
npx playwright test
```
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Page, Locator, expect } from '@playwright/test';
import { BrowserExtensionName } from '../utils/extensions';
import { Modal, ModalName } from './modal';
import { WalletConfig } from '../utils/wallets/walletUtils';

export interface UTxO {
tx: string;
Expand All @@ -10,7 +10,7 @@ export interface UTxO {

export interface WalletCipData {
balance: number;
extensions: string;
extensions: string[];
networkId: string;
changeAddress: string;
rewardAddresses: string[];
Expand Down Expand Up @@ -61,7 +61,7 @@ export class HomePage {
async getWalletCipData() {
const walletCipData: WalletCipData = {
balance: 0,
extensions: '',
extensions: [],
networkId: '',
changeAddress: '',
rewardAddresses: [],
Expand Down Expand Up @@ -106,15 +106,16 @@ export class HomePage {
}
}

async getExtensions(): Promise<string> {
async getExtensions(): Promise<string[]> {
const isVisible = await this.extensionsLabel.isVisible();
if (!isVisible) {
throw new Error('Extensions label is not visible');
}
const extensionsText = await this.extensionsLabel.textContent();
const match = extensionsText?.trim().match(/^Extensions:\s*(.+)$/);
if (match && match[1]) {
return match[1].trim();
const trimmedText = match[1].trim();
return trimmedText.split(',').map(ext => ext.trim()).filter(ext => ext.length > 0);
} else {
throw new Error(`Unable to extract extensions from text: ${extensionsText}`);
}
Expand Down Expand Up @@ -272,9 +273,9 @@ export class HomePage {
}
}

async assertBasicWalletCipData(actualWalletCipData: WalletCipData, extensionName: BrowserExtensionName) {
async assertBasicWalletCipData(actualWalletCipData: WalletCipData, walletConfig: WalletConfig) {
expect(actualWalletCipData.balance).toBeGreaterThan(500000000);
expect(actualWalletCipData.extensions).toBe(extensionName === BrowserExtensionName.Typhon ? 'cip-30' : 'cip-95');
await this.assertExtensions(actualWalletCipData.extensions, walletConfig.cipBridge);
expect(actualWalletCipData.networkId).not.toBeNaN();
expect(actualWalletCipData.changeAddress).not.toBeNaN();
expect(actualWalletCipData.rewardAddresses.length).toBeGreaterThan(0);
Expand All @@ -283,6 +284,12 @@ export class HomePage {
expect(actualWalletCipData.utxos.length).toBeGreaterThan(0);
expect(actualWalletCipData.publicDRepKey).not.toBeNaN();
expect(actualWalletCipData.registeredPublicStakeKeys).not.toBeNaN();

}

//Check if expected extensions are present in actual extensions
async assertExtensions(actualExtensions: string[], expectedExtensions: string[]) {
for (const ext of expectedExtensions) {
expect(actualExtensions).toContain(ext);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ export enum ModalName {
SignData = 'SignData',
SignAndSubmitTx = 'SignAndSubmitTx',
SignAndSubmitRBACTx = 'SignAndSubmitRBACTx',
SignDataUserDeclined = 'UserDeclined',
SignTxUserDeclined = 'SignTxUserDeclined',
SignRBACTxUserDeclined = 'SignRBACTxUserDeclined',
}

export interface ModalContent {
Expand All @@ -24,6 +27,18 @@ export const modalContents: { [key in ModalName]: ModalContent } = {
header: 'Sign & submit RBAC tx',
unchangingText: 'Tx hash:',
},
[ModalName.SignDataUserDeclined]: {
header: 'Sign data',
unchangingText: 'user declined sign data',
},
[ModalName.SignTxUserDeclined]: {
header: 'Sign & submit tx',
unchangingText: 'user declined sign tx',
},
[ModalName.SignRBACTxUserDeclined]: {
header: 'Sign & submit RBAC tx',
unchangingText: 'user declined sign tx',
},
};

export class Modal {
Expand All @@ -35,8 +50,8 @@ export class Modal {
constructor(page: Page, modalName: ModalName) {
this.page = page;
this.content = modalContents[modalName];
this.modalHeader = this.page.getByText(this.content.header)
this.modalBody = this.page.getByText(this.content.unchangingText)
this.modalHeader = this.page.getByText(this.content.header, { exact: true });
this.modalBody = this.page.getByText(this.content.unchangingText)
}

async assertModalIsVisible() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Locator, Page } from '@playwright/test';
import { BrowserExtensionName } from '../utils/extensions';


export class WalletListPage {
readonly page: Page;

constructor(page: Page) {
this.page = page;
}
async clickEnableWallet(walletName: BrowserExtensionName): Promise<void> {
const enableButton = (walletName: BrowserExtensionName) => this.page.locator(
`flt-semantics:has(flt-semantics-img[aria-label*="Name: ${walletName.toLowerCase()}"]) ` +
`flt-semantics[role="button"]:has-text("Enable wallet")`
);
await enableButton(walletName).click();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default defineConfig({
reporter: [
['html', { open: 'never', outputFolder: '/results' }],
['junit', { outputFile: '/results/cardano-wallet.junit-report.xml' }]],
timeout: 120 * 1000,
timeout: 60 * 1000,
projects: [
{
name: 'chromium',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { BrowserContext, chromium, Page } from "@playwright/test";
import { ExtensionDownloader } from "./utils/extensionDownloader";
import { BrowserExtensionName } from "./utils/extensions";
import {
allowExtension,
onboardWallet,
WalletConfig,
} from "./utils/wallets/walletUtils";

const installExtension = async (extensionName: BrowserExtensionName) => {
const extensionPath = await new ExtensionDownloader().getExtension(
extensionName
);
const browser = await chromium.launchPersistentContext("", {
headless: false, // extensions only work in headful mode
args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
],
});
let [background] = browser.serviceWorkers();
if (!background) background = await browser.waitForEvent("serviceworker");
return browser;
};

export const restoreWallet = async (walletConfig: WalletConfig) => {
const browser = await installExtension(walletConfig.extension.Name);
const extensionTab = browser.pages()[0];
walletConfig.extension.HomeUrl = await getDynamicUrlInChrome(
extensionTab,
walletConfig
);
await extensionTab.goto(walletConfig.extension.HomeUrl);
await onboardWallet(extensionTab, walletConfig);
return browser;
};

export const enableWallet = async (
walletConfig: WalletConfig,
browser: BrowserContext
) => {
const page = browser.pages()[0];
await page.reload();
await page.goto("/");
await page.waitForTimeout(4000);
const [walletPopup] = await Promise.all([
browser.waitForEvent("page"),
page.locator('//*[text()="Enable wallet"]').click(),
]);
await walletPopup.waitForTimeout(2000);
await allowExtension(walletPopup, walletConfig.extension.Name);
await page.waitForTimeout(2000);
return browser;
};

/**
* We need this because some extensions have dynamic URLs
**/
const getDynamicUrlInChrome = async (
extensionTab: Page,
walletConfig: WalletConfig
): Promise<string> => {
await extensionTab.goto("chrome://extensions/");
const extensionId = await extensionTab
.locator("extensions-item")
.getAttribute("id");
return `chrome-extension://${extensionId}/${walletConfig.extension.HomeUrl}`;
};
Loading

0 comments on commit 5bd5615

Please sign in to comment.