Skip to content
This repository has been archived by the owner on Jun 19, 2024. It is now read-only.

Commit

Permalink
[Infrastructure] Implementing Playwright e2e tests for main swap flows (
Browse files Browse the repository at this point in the history
#63)

* chore: wip playwright setup

* chore: wip playwright setup

* chore: refactoring wallet manager; first full e2e tests

* chore: refining e2e tests; accessing components by ids

* chore: refining ci for e2e on github actions

* chore: testing ci

* chore: testing ci

* chore: testing ci

* chore: testing ci

* chore: testing ci

* chore: tweaking ci

* chore: testing ci

* chore: testing ci

* chore: testing ci

* chore: testing ci

* chore: testing ci

* chore: testing ci

* chore: testing ci

* chore: testing ci

* chore: testing ci

* chore: testing ci

* chore: testing ci

* feat: testing ci

* feat: testing ci

* chore: tweaking ci

* chore: tweaking readme

* feat: bumping version

* feat: testing ci

* feat: testing ci

* feat: fixing vercel error

Co-authored-by: MillionAlgosFather <info@algoworld.io>
  • Loading branch information
aorumbayev and MillionAlgosFather authored Jun 20, 2022
1 parent a370729 commit b47d551
Show file tree
Hide file tree
Showing 54 changed files with 1,081 additions and 454 deletions.
51 changes: 51 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: E2E Tests
on:
pull_request:
branches: [main]
jobs:
e2e_test:
env:
PR_NUMBER: ${{ github.event.number }}
E2E_TESTS_BASE_URL: 'https://pr${{ github.event.number }}-${{ github.run_number }}.vercel-action.millionalgos.com'
E2E_TESTS_BASE_URL_ALIAS: 'pr${{ github.event.number }}-${{ github.run_number }}.vercel-action.millionalgos.com'

timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Expose e2e base url
run: |
echo "${{ env.E2E_TESTS_BASE_URL }}"
shell: bash

- uses: actions/setup-node@v2
with:
node-version: '16.x'

- name: Install dependencies
run: yarn

- name: Install Playwright Browsers
run: npx playwright install chromium

- name: Deploy preview
uses: amondnet/vercel-action@v25.0.0
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }} # Required
github-token: ${{ secrets.GITHUB_TOKEN }} #Optional
vercel-org-id: ${{ secrets.VERCEL_ORG_ID}} #Required
vercel-project-id: ${{ secrets.VERCEL_STAGING_PROJECT_ID}} #Required
working-directory: ./
alias-domains: ${{ env.E2E_TESTS_BASE_URL_ALIAS }}

- name: Run Playwright tests
run: |
E2E_TESTS_BASE_URL=${{ env.E2E_TESTS_BASE_URL }} yarn run test:e2e:ci
- uses: actions/upload-artifact@v2
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,6 @@ yarn-error.log*

# Sentry
.sentryclirc
/test-results/
/playwright-report/
/playwright/.cache/
116 changes: 2 additions & 114 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,121 +17,9 @@ The following repository hosts the source codes for `AlgoWorld Swapper`. Free an
---

## Prerequisites
## 💁‍♂️ Docs

- `Node` >= 14.x
- `yarn` >= 1.12.15
- `vercel cli` >= 24.2.4
- [`pre-commit`](https://pre-commit.com/) >= 2.19.0

### `pre-commit` configuration

Run the following command from the root of the repo to setup hooks:

```bash
pre-commit install # for all hooks
pre-commit install --hook-type commit-msg # for commitlint checks
```

---

## 🚀 Overview

AlgoWorld Swapper currently offers usage of several smart signatures used for single and multi ASA transfers.

### Swapper

There are two different types of smart signatures available:

- **ASA to ASA swap | 🎴↔️🎴**: <br> Allows performing a swap of any single ASA of specified amount to any other single ASA of specified amount.

- **ASAs to ALGO swap | 🎴🎴🎴↔️💰**: <br> Allows performing a swap of multiple ASAs of specified amount to ALGO of specified amount.

> Detailed documentation is work in progress ⚠️
---

## ⚙️ Development guide

To start the project locally:

1. Create virtual environment for python functions:

```bash
python -m venv .venv
source .venv/bin/activate
```

2. Install all dependencies

```bash
pip install -r api/swappers/requirements.txt
```

3. Run the client locally:

```bash
vercel dev
```

Open `http://localhost:3000` with your browser to see the result.

> Running `vercel dev` for the first time will prompt you to setup and link with your existing/new vercel project. You can create a dummy project and link it to be able to run the development locally.
### Env variables

For running locally create a file called `.env.local` and fill it with the following default parameters (or replace with your own values):

| Variable Name | Required? | Description |
| --------------------------------- | :-------: | --------------------------------------------------------------------------------------------- |
| **AW_WEB_STORAGE_API_KEY** | yes | obtain your own api key on [ web3.storage ](https://web3.storage/). |
| **NEXT_PUBLIC_CHAIN_TYPE** | yes | set to `mainnet` or `testnet` to indicate which chain to use by default. |
| **NEXT_PUBLIC_GA_MEASUREMENT_ID** | no | a tag value for Google Analytics tracking. For local dev purposes you can skip it completely. |
| **NEXT_PUBLIC_SENTRY_DSN** | no | a tag value for Sentry error tracking. For local dev purposes you can skip it completely. |

### Directory Structure

- [`public`](./public) — Static assets such as robots.txt, images, and favicon.<br>
- [`src`](./src) — Application source code, including pages, components, styles.
- [`api`](./api) — Serverless vercel functions, contract compilation is using `python` and `pyteal` and ipfs storage is done with `node`.
- [`.pre-commit-config.yaml`](.pre-commit-config.yaml) — pre commit coniguration for formatting python serverless functions.

### Scripts

The section describes different modes of running the swapper for local dev purposes.

#### Client and functions

- `vercel dev` - executes both backend and frontend.

#### Frontend only

Below is for frontend client only (excluding `Vercel` serveless functions).

- `yarn dev` — Starts the application in development mode at `http://localhost:3000`.
- `yarn build` — Creates an optimized production build of your application.
- `yarn start` — Starts the application in production mode.
- `yarn type-check` — Validate code using TypeScript compiler.
- `yarn lint` — Runs ESLint for all files in the `src` directory.
- `yarn format` — Runs Prettier for all files in the `src` directory.
- `yarn commit` — Run commitizen. Alternative to `git commit`.

### Path Mapping

TypeScript are pre-configured with custom path mappings. To import components or files, use the `@` prefix.

```tsx
import { Button } from '@/components/Button';

// To import images or other files from the public folder
import avatar from '@/public/avatar.png';
```

---

## 🧪 Testing

TBD
Refer to [docs.algoworld.io](https://docs.algoworld.io/swapper) for detailed documentation.

---

Expand Down
3 changes: 3 additions & 0 deletions e2e/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const AW_SWAPPER_BASE_URL =
process.env.E2E_TESTS_BASE_URL ?? `http://localhost:3000/`;
console.log(process.env.E2E_TESTS_BASE_URL);
17 changes: 17 additions & 0 deletions e2e/pages/dashboard.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { test, expect } from '@playwright/test';
import { AW_SWAPPER_BASE_URL } from '../consts';

test(`should navigate to the about page`, async ({ page }) => {
await page.goto(AW_SWAPPER_BASE_URL);
await expect(page.locator(`h1`)).toContainText(`🏠 Dashboard`);

await page.locator(`button:has-text("About")`).click();
await expect(
page.locator(`text=AlgoWorld Swapper v0.2.2`).first(),
).toBeVisible();

await page.locator(`button:has-text("Close")`).click();
await expect(
page.locator(`text=AlgoWorld Swapper v0.2.2`).first(),
).toBeHidden();
});
154 changes: 154 additions & 0 deletions e2e/pages/swappers/asa-to-asa.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { test, expect, Page } from '@playwright/test';
import { AW_SWAPPER_BASE_URL } from '@/e2e/consts';
import {
NAV_BAR_CONNECT_BTN_ID,
NAV_BAR_SETTINGS_BTN_ID,
NAV_BAR_SETTINGS_MENU_ITEM_ID,
} from '@/components/Headers/constants';
import {
CONNECT_WALLET_DIALOG_ID,
FROM_ASSET_PICKER_DIALOG_SEARCH_ID,
DIALOG_SELECT_BTN_ID,
FROM_ASSET_PICKER_DIALOG_ID,
TO_ASSET_PICKER_DIALOG_SEARCH_ID,
TO_ASSET_PICKER_DIALOG_ID,
CONFIRM_DIALOG_ID,
SHARE_SWAP_DIALOG_ID,
SHARE_SWAP_COPY_BTN_ID,
DIALOG_CANCEL_BTN_ID,
SWAP_DEACTIVATION_PERFORMED_MESSAGE,
} from '@/components/Dialogs/constants';
import {
SWAP_TYPE_PICKER_CARD_ID,
FROM_SWAP_OFFERING_ASSET_BTN_ID,
TO_SWAP_REQUESTING_BTN_ID,
} from '@/components/Cards/constants';
import {
ASA_TO_ASA_PAGE_HEADER_ID,
CREATE_SWAP_BTN_ID,
MY_SWAPS_PAGE_HEADER_ID,
} from '@/common/constants';
import { MY_SWAPS_TABLE_MANAGE_BTN_ID } from '@/components/Tables/constants';
import { CRYPTO_TEXT_FIELD_ID } from '@/components/TextFields/constants';

const DUMMY_ASA_TO_OFFER = `96044943: AWS_TEST`;
const DUMMY_ASA_TO_REQUEST_TITLE = `SASA`;
const DUMMY_ASA_TO_REQUEST = `16040857: ${DUMMY_ASA_TO_REQUEST_TITLE}`;

let page: Page;

test.beforeAll(async ({ browser }) => {
const context = await browser.newContext();
context.grantPermissions([`clipboard-read`, `clipboard-write`]);
page = await context.newPage();
await page.goto(AW_SWAPPER_BASE_URL);
});

test.afterAll(async () => {
await page.close();
});

test.describe(`Asa to Asa Swap`, () => {
test(`should be able to load and connect wallet`, async () => {
await page.locator(`id=${NAV_BAR_CONNECT_BTN_ID}`).click();
await expect(page.locator(`id=${CONNECT_WALLET_DIALOG_ID}`)).toBeVisible();
await page.locator(`div[role="button"]:has-text("Mnemonic")`).click();
await expect(page.locator(`id=${CONNECT_WALLET_DIALOG_ID}`)).toBeHidden();
});

test(`should be able to navigate to Asa to Asa page`, async () => {
await expect(page.locator(`id=${ASA_TO_ASA_PAGE_HEADER_ID}`)).toBeHidden();
await Promise.all([
page.waitForNavigation(),
page.locator(`id=${SWAP_TYPE_PICKER_CARD_ID(`ASA to ASA`)}`).click(),
]);
await expect(page.locator(`id=${ASA_TO_ASA_PAGE_HEADER_ID}`)).toBeVisible();
});

test(`should be able to select offering asset`, async () => {
await expect(
page.locator(`id=${FROM_ASSET_PICKER_DIALOG_ID}`),
).toBeHidden();
await page.locator(`id=${FROM_SWAP_OFFERING_ASSET_BTN_ID}`).click();
await expect(
page.locator(`id=${FROM_ASSET_PICKER_DIALOG_ID}`),
).toBeVisible();

await page.locator(`id=${FROM_ASSET_PICKER_DIALOG_SEARCH_ID}`).click();
await page.locator(`text=${DUMMY_ASA_TO_OFFER}`).click();

await page.locator(`id=${CRYPTO_TEXT_FIELD_ID}`).click();
await page.locator(`id=${CRYPTO_TEXT_FIELD_ID}`).fill(`1`);
await page.locator(`id=${DIALOG_SELECT_BTN_ID}`).click();
await expect(
page.locator(`id=${FROM_ASSET_PICKER_DIALOG_ID}`),
).toBeHidden();
});

test(`should be able to select requesting asset`, async () => {
await expect(page.locator(`id=${TO_ASSET_PICKER_DIALOG_ID}`)).toBeHidden();
await page.locator(`id=${TO_SWAP_REQUESTING_BTN_ID}`).click();
await expect(page.locator(`id=${TO_ASSET_PICKER_DIALOG_ID}`)).toBeVisible();

await page
.locator(`id=${TO_ASSET_PICKER_DIALOG_SEARCH_ID}`)
.fill(DUMMY_ASA_TO_REQUEST_TITLE);
await page.locator(`text=${DUMMY_ASA_TO_REQUEST}`).click();

await page.locator(`id=${CRYPTO_TEXT_FIELD_ID}`).click();
await page.locator(`id=${CRYPTO_TEXT_FIELD_ID}`).fill(`1`);
await page.locator(`id=${DIALOG_SELECT_BTN_ID}`).click();
await expect(page.locator(`id=${TO_ASSET_PICKER_DIALOG_ID}`)).toBeHidden();
});

test(`should be able to initiate create swap`, async () => {
await expect(page.locator(`id=${CONFIRM_DIALOG_ID}`)).toBeHidden();
await page.locator(`id=${CREATE_SWAP_BTN_ID}`).click({ timeout: 120000 });
await expect(page.locator(`id=${CONFIRM_DIALOG_ID}`)).toBeVisible();
await page.locator(`id=${DIALOG_SELECT_BTN_ID}`).click();
await expect(page.locator(`id=${CONFIRM_DIALOG_ID}`)).toBeHidden();
});

test(`should successfully create swap and display share dialog`, async () => {
await expect(page.locator(`id=${SHARE_SWAP_DIALOG_ID}`)).toBeVisible({
timeout: 100000,
});
await page.locator(`id=${SHARE_SWAP_COPY_BTN_ID}`).click();
expect(
await page.evaluate(() => {
const content = navigator.clipboard.readText();
return content;
}),
).toContain(`/swap/`);
await page.locator(`id=${DIALOG_CANCEL_BTN_ID}`).click();
await expect(page.locator(`id=${SHARE_SWAP_DIALOG_ID}`)).toBeHidden();
});

test(`should successfully remove created swap`, async () => {
await page.locator(`id=${NAV_BAR_SETTINGS_BTN_ID}`).click();
await Promise.all([
page.waitForNavigation(),
page.locator(`id=${NAV_BAR_SETTINGS_MENU_ITEM_ID(`My Swaps`)}`).click(),
]);
await expect(page.locator(`id=${MY_SWAPS_PAGE_HEADER_ID}`)).toBeVisible();

const escrowAddress = await page.evaluate(async () => {
const clipboardContent = await navigator.clipboard.readText();
const escrow = clipboardContent
.substring(clipboardContent.lastIndexOf(`/`) + 1)
.split(`?`)[0];
return escrow;
});

await page
.locator(`id=${MY_SWAPS_TABLE_MANAGE_BTN_ID(escrowAddress)}`)
.click();
await page.locator(`text=Delete`).click();

await expect(
page.locator(`text=${SWAP_DEACTIVATION_PERFORMED_MESSAGE}`),
).toBeVisible({
timeout: 120000,
});
});
});
Loading

1 comment on commit b47d551

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for algoworld-swapper ready!

✅ Preview
https://algoworld-swapper-hqwy01yc1-algoworldexplorer.vercel.app

Built with commit b47d551.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.