diff --git a/.github/workflows/test_integration_playwright.yml b/.github/workflows/test_integration_playwright.yml index a1b2bacb7..474bbbfb4 100644 --- a/.github/workflows/test_integration_playwright.yml +++ b/.github/workflows/test_integration_playwright.yml @@ -62,6 +62,13 @@ jobs: name: allure-results path: tests/govtool-frontend/playwright/allure-results + - name: Upload lock logs + uses: actions/upload-artifact@v3 + if: always() + with: + name: lock-logs + path: tests/govtool-frontend/playwright/lock_logs.txt + env: HOST_URL: https://${{inputs.deployment || 'govtool.cardanoapi.io' }} API_URL: https://${{inputs.deployment || 'govtool.cardanoapi.io' }}/api diff --git a/tests/govtool-frontend/playwright/.gitignore b/tests/govtool-frontend/playwright/.gitignore index 504f04dab..42da36a46 100644 --- a/tests/govtool-frontend/playwright/.gitignore +++ b/tests/govtool-frontend/playwright/.gitignore @@ -13,4 +13,6 @@ allure-report/ .vars .lock-pool/ .logs/ -lib/_mock/wallets.json \ No newline at end of file +lib/_mock/*.json + +./lock_logs.txt diff --git a/tests/govtool-frontend/playwright/global-setup.ts b/tests/govtool-frontend/playwright/global-setup.ts new file mode 100644 index 000000000..89687f1a9 --- /dev/null +++ b/tests/govtool-frontend/playwright/global-setup.ts @@ -0,0 +1,49 @@ +import { faucetWallet } from "@constants/staticWallets"; +import { ShelleyWallet } from "@helpers/crypto"; +import { pollTransaction } from "@helpers/transaction"; +import { loadAmountFromFaucet } from "@services/faucetService"; +import kuberService from "@services/kuberService"; +import walletManager from "lib/walletManager"; + +async function generateWallets(num: number) { + return await Promise.all( + Array.from({ length: num }, () => + ShelleyWallet.generate().then((wallet) => wallet.json()) + ) + ); +} + +async function globalSetup() { + const registeredDRepWallets = await generateWallets(9); + const registerDRepWallets = await generateWallets(5); + + // faucet setup + const res = await loadAmountFromFaucet(faucetWallet.address); + await pollTransaction(res.txid); + + // initialize wallets + const initializeRes = await kuberService.initializeWallets([ + ...registeredDRepWallets, + ...registerDRepWallets, + ]); + await pollTransaction(initializeRes.txId, initializeRes.lockInfo); + + // register dRep + const registrationRes = await kuberService.multipleDRepRegistration( + registeredDRepWallets + ); + await pollTransaction(registrationRes.txId, registrationRes.lockInfo); + + // transfer 600 ADA for dRep registration + const amountOutputs = registerDRepWallets.map((wallet) => { + return { address: wallet.address, value: `${600}A` }; + }); + const transferRes = await kuberService.multipleTransferADA(amountOutputs); + await pollTransaction(transferRes.txId, transferRes.lockInfo); + + // save to file + await walletManager.writeWallets(registeredDRepWallets, "registeredDRep"); + await walletManager.writeWallets(registerDRepWallets, "registerDRep"); +} + +export default globalSetup; diff --git a/tests/govtool-frontend/playwright/lib/_mock/index.ts b/tests/govtool-frontend/playwright/lib/_mock/index.ts index 4c6872043..b9c46e638 100644 --- a/tests/govtool-frontend/playwright/lib/_mock/index.ts +++ b/tests/govtool-frontend/playwright/lib/_mock/index.ts @@ -17,6 +17,30 @@ export const invalid = { return `${scheme}://${randomDomain}`; }, + name: () => { + const choice = faker.number.int({ min: 1, max: 3 }); + if (choice === 1) { + // space invalid + return faker.lorem.word() + " " + faker.lorem.word(); + } else if (choice === 2) { + // maximum 80 words invalid + return faker.lorem.paragraphs().replace(/\s+/g, ""); + } + // empty invalid + return " "; + }, + + email: () => { + const choice = faker.number.int({ min: 1, max: 3 }); + + if (choice === 1) { + return faker.lorem.word() + faker.number + "@invalid.com"; + } else if (choice == 2) { + return faker.lorem.word() + "@"; + } + return faker.lorem.word() + "@gmail_com"; + }, + proposalTitle: () => { const choice = faker.number.int({ min: 1, max: 2 }); if (choice === 1) { diff --git a/tests/govtool-frontend/playwright/lib/constants/environments.ts b/tests/govtool-frontend/playwright/lib/constants/environments.ts index 7a8c65a6a..460bd382a 100644 --- a/tests/govtool-frontend/playwright/lib/constants/environments.ts +++ b/tests/govtool-frontend/playwright/lib/constants/environments.ts @@ -1,3 +1,6 @@ +import { config } from "dotenv"; +config(); + const CARDANO_API_METADATA_HOST_URL = process.env.CARDANOAPI_METADATA_URL || "https://metadata-govtool.cardanoapi.io"; @@ -21,6 +24,7 @@ const environments = { txTimeOut: parseInt(process.env.TX_TIMEOUT) || 240000, metadataBucketUrl: `${CARDANO_API_METADATA_HOST_URL}/data`, lockInterceptorUrl: `${CARDANO_API_METADATA_HOST_URL}/lock`, + ci: process.env.CI, }; export default environments; diff --git a/tests/govtool-frontend/playwright/lib/constants/staticWallets.ts b/tests/govtool-frontend/playwright/lib/constants/staticWallets.ts index 8c1b7d65a..cc929db8e 100644 --- a/tests/govtool-frontend/playwright/lib/constants/staticWallets.ts +++ b/tests/govtool-frontend/playwright/lib/constants/staticWallets.ts @@ -10,6 +10,7 @@ export const adaHolder01Wallet = staticWallets[3]; export const adaHolder02Wallet = staticWallets[4]; export const adaHolder03Wallet = staticWallets[6]; export const adaHolder04Wallet = staticWallets[7]; +export const adaHolder05Wallet = staticWallets[8]; // Does not takes part in transaction export const user01Wallet: StaticWallet = staticWallets[5]; @@ -19,6 +20,7 @@ export const adaHolderWallets = [ adaHolder02Wallet, adaHolder03Wallet, adaHolder04Wallet, + adaHolder05Wallet, ]; export const userWallets = [user01Wallet]; diff --git a/tests/govtool-frontend/playwright/lib/datafactory/createAuth.ts b/tests/govtool-frontend/playwright/lib/datafactory/createAuth.ts index 0b23467d9..f84e5a716 100644 --- a/tests/govtool-frontend/playwright/lib/datafactory/createAuth.ts +++ b/tests/govtool-frontend/playwright/lib/datafactory/createAuth.ts @@ -4,13 +4,14 @@ import { importWallet } from "@fixtures/importWallet"; import { ShelleyWallet } from "@helpers/crypto"; import LoginPage from "@pages/loginPage"; import { Page } from "@playwright/test"; +import { StaticWallet } from "@types"; const tempDRepAuth = ".auth/tempDRepAuth.json"; const tempUserAuth = ".auth/tempUserAuth.json"; const tempAdaHolderAuth = ".auth/tempAdaHolderAuth.json"; -export async function createTempDRepAuth(page: Page, wallet: ShelleyWallet) { - await importWallet(page, wallet.json()); +export async function createTempDRepAuth(page: Page, wallet: StaticWallet) { + await importWallet(page, wallet); const loginPage = new LoginPage(page); await loginPage.login(); diff --git a/tests/govtool-frontend/playwright/lib/helpers/crypto.ts b/tests/govtool-frontend/playwright/lib/helpers/crypto.ts index 9c7afc05b..dd38d2c51 100644 --- a/tests/govtool-frontend/playwright/lib/helpers/crypto.ts +++ b/tests/govtool-frontend/playwright/lib/helpers/crypto.ts @@ -1,3 +1,4 @@ +import environments from "../constants/environments"; import { ed25519 as ed } from "@noble/curves/ed25519"; import { bech32 } from "bech32"; import * as blake from "blakejs"; @@ -130,10 +131,22 @@ export class ShelleyWallet { 200 ); } + + dRepIdBech32() { + const stakePubKey = Buffer.from(this.stakeKey.public).toString("hex"); + const dRepKeyBytes = Buffer.from(stakePubKey, "hex"); + const dRepId = blake.blake2bHex(dRepKeyBytes, undefined, 28); + const words = bech32.toWords(Buffer.from(dRepId, "hex")); + const dRepIdBech32 = bech32.encode("drep", words); + return dRepIdBech32; + } + public json() { return { payment: this.paymentKey.json(), stake: this.stakeKey.json(), + dRepId: this.dRepIdBech32(), + address: this.addressBech32(environments.networkId), }; } diff --git a/tests/govtool-frontend/playwright/lib/helpers/page.ts b/tests/govtool-frontend/playwright/lib/helpers/page.ts index d3aca4d4b..56dc3c348 100644 --- a/tests/govtool-frontend/playwright/lib/helpers/page.ts +++ b/tests/govtool-frontend/playwright/lib/helpers/page.ts @@ -1,11 +1,11 @@ import { importWallet } from "@fixtures/importWallet"; import loadDemosExtension from "@fixtures/loadExtension"; -import { Browser, Page, expect } from "@playwright/test"; -import { ShelleyWallet } from "./crypto"; +import { Browser, Page } from "@playwright/test"; +import { StaticWallet } from "@types"; interface BrowserConfig { storageState: string; - wallet: ShelleyWallet; + wallet: StaticWallet; enableStakeSigning?: boolean; } @@ -19,7 +19,7 @@ export async function createNewPageWithWallet( const newPage = await context.newPage(); await loadDemosExtension(newPage, enableStakeSigning); - await importWallet(newPage, wallet.json()); + await importWallet(newPage, wallet); return newPage; } diff --git a/tests/govtool-frontend/playwright/lib/helpers/transaction.ts b/tests/govtool-frontend/playwright/lib/helpers/transaction.ts index 009f0766a..e8a04e3fc 100644 --- a/tests/govtool-frontend/playwright/lib/helpers/transaction.ts +++ b/tests/govtool-frontend/playwright/lib/helpers/transaction.ts @@ -33,8 +33,8 @@ export async function pollTransaction( if (!lockInfo) return; - await LockInterceptor.releaseLockForAddress( - lockInfo.address, + await LockInterceptor.releaseLock( + lockInfo.initiator, lockInfo.lockId, `Task completed for:${lockInfo.lockId}` ); @@ -42,8 +42,8 @@ export async function pollTransaction( if (lockInfo) { const errorMessage = { lockInfo, error: JSON.stringify(err) }; - await LockInterceptor.releaseLockForAddress( - lockInfo.address, + await LockInterceptor.releaseLock( + lockInfo.initiator, lockInfo.lockId, `Task failure: \n${JSON.stringify(errorMessage)}` ); @@ -58,30 +58,35 @@ export async function waitForTxConfirmation( triggerCallback?: () => Promise ) { let transactionHash: string | undefined; - const transactionStatusPromise = page.waitForRequest((request) => { - return request.url().includes("/transaction/status/"); - }); - - await triggerCallback?.call(this); - await expect( - page - .getByTestId("alert-warning") - .getByText("Transaction in progress", { exact: false }) - ).toBeVisible({ - timeout: 10_000, - }); - const url = (await transactionStatusPromise).url(); - const regex = /\/transaction\/status\/([^\/]+)$/; - const match = url.match(regex); - if (match) { - transactionHash = match[1]; - } + try { + await triggerCallback?.call(this); + const transactionStatusPromise = page.waitForRequest((request) => { + return request.url().includes("/transaction/status/"); + }); - if (transactionHash) { - await pollTransaction(transactionHash); await expect( - page.getByText("In Progress", { exact: true }).first() //FIXME: Only one element needs to be displayed - ).not.toBeVisible({ timeout: 20_000 }); + page + .getByTestId("alert-warning") + .getByText("Transaction in progress", { exact: false }) + ).toBeVisible({ + timeout: 10_000, + }); + const url = (await transactionStatusPromise).url(); + const regex = /\/transaction\/status\/([^\/]+)$/; + const match = url.match(regex); + if (match) { + transactionHash = match[1]; + } + + if (transactionHash) { + await pollTransaction(transactionHash); + await expect( + page.getByText("In Progress", { exact: true }).first() //FIXME: Only one element needs to be displayed + ).not.toBeVisible({ timeout: 20_000 }); + } + } catch (error) { + Logger.fail(error.message); + throw new Error(error); } } diff --git a/tests/govtool-frontend/playwright/lib/lockInterceptor.ts b/tests/govtool-frontend/playwright/lib/lockInterceptor.ts index 8db59c067..17661626b 100644 --- a/tests/govtool-frontend/playwright/lib/lockInterceptor.ts +++ b/tests/govtool-frontend/playwright/lib/lockInterceptor.ts @@ -1,25 +1,24 @@ -import { TxSubmitResponse } from "@services/kuberService"; +import { Logger } from "@helpers/logger"; import * as fs from "fs"; import * as lockfile from "lockfile"; -import { Logger } from "../../cypress/lib/logger/logger"; import path = require("path"); export interface LockInterceptorInfo { lockId: string; - address: string; + initiator: string; } export class LockInterceptor { private static async acquireLock( - address: string, - lockId: string + initiator: string, + lockId?: string ): Promise { - const lockFilePath = path.resolve(__dirname, `../${address}`); + const lockFilePath = path.resolve(__dirname, `../${initiator}`); try { await log( - `Initiator: ${address} \n---------------------> acquiring lock for:${lockId}` + `Initiator: ${initiator} \n---------------------> acquiring lock for:${lockId}` ); await new Promise((resolve, reject) => { lockfile.lock(lockFilePath, (err) => { @@ -31,22 +30,22 @@ export class LockInterceptor { }); }); await log( - `Initiator: ${address} \n---------------------> acquired lock for:${lockId}` + `Initiator: ${initiator} \n---------------------> acquired lock for:${lockId}` ); } catch (err) { throw err; } } - private static async releaseLock( - address: string, - lockId: string + private static async _releaseLock( + initiator: string, + lockId?: string ): Promise { - const lockFilePath = path.resolve(__dirname, `../${address}`); + const lockFilePath = path.resolve(__dirname, `../${initiator}`); try { await log( - `Initiator: ${address} \n---------------------> releasing lock for:${lockId}` + `Initiator: ${initiator} \n---------------------> releasing lock for:${lockId}` ); await new Promise((resolve, reject) => { lockfile.unlock(lockFilePath, async (err) => { @@ -58,7 +57,7 @@ export class LockInterceptor { }); }); await log( - `Initiator: ${address} \n---------------------> released lock for:${lockId}\n` + `Initiator: ${initiator} \n---------------------> released lock for:${lockId}\n` ); } catch (err) { throw err; @@ -66,19 +65,19 @@ export class LockInterceptor { } private static async waitForReleaseLock( - address: string, + initiator: string, lockId: string ): Promise { - const pollInterval = 4000; // 4 secs + const pollInterval = 100; // 100 milliseconds try { await log( - `Initiator: ${address} \n ---------------------> waiting lock for:${lockId}` + `Initiator: ${initiator} \n ---------------------> waiting lock for:${lockId}` ); return new Promise((resolve, reject) => { const pollFn = () => { try { - const isAddressLocked = checkAddressLock(address); + const isAddressLocked = checkLock(initiator); if (!isAddressLocked) { resolve(); } else { @@ -96,20 +95,19 @@ export class LockInterceptor { } } - static async intercept( - address: string, - callbackFn: () => Promise, - lockId: string, - provider: "local" | "server" = "local" - ): Promise { + static async intercept( + initiator: string, + callbackFn: () => Promise, + lockId?: string + ): Promise { while (true) { - const isAddressLocked = checkAddressLock(address); + const isAddressLocked = checkLock(initiator); if (isAddressLocked) { - await LockInterceptor.waitForReleaseLock(address, lockId); + await LockInterceptor.waitForReleaseLock(initiator, lockId); } try { - await LockInterceptor.acquireLock(address, lockId); + await LockInterceptor.acquireLock(initiator, lockId); break; } catch (err) { if (err.code === "EEXIST") { @@ -122,48 +120,38 @@ export class LockInterceptor { } try { const res = await callbackFn(); - return { ...res, lockInfo: { lockId, address } }; + return { ...res, lockInfo: { lockId, initiator } }; } catch (err) { const errorMessage = { lock_id: lockId, error: JSON.stringify(err) }; await log(`Task failure: \n${JSON.stringify(errorMessage)}`); - await LockInterceptor.releaseLock(address, lockId); + await LockInterceptor._releaseLock(initiator, lockId); throw err; } } - static async releaseLockForAddress( - address: string, - lockId: string, + static async releaseLock( + interceptor: string, + lockId?: string, message?: string ) { try { message && (await log(message)); - await this.releaseLock(address, lockId); + await this._releaseLock(interceptor, lockId); } catch { Logger.fail("Failed to write lock logs"); } } } -function checkAddressLock(address: string): boolean { - const lockFilePath = path.resolve(__dirname, `../${address}`); +function checkLock(initiator: string): boolean { + const lockFilePath = path.resolve(__dirname, `../${initiator}`); return lockfile.checkSync(lockFilePath); } function log(message: string): Promise { - const options: Intl.DateTimeFormatOptions = { - year: "numeric", - month: "2-digit", - day: "2-digit", - hour: "2-digit", - minute: "2-digit", - second: "2-digit", - hour12: false, - timeZone: "Asia/Kathmandu", - }; const logFilePath = path.resolve(__dirname, "../lock_logs.txt"); - const logMessage = `[${new Date().toLocaleString("en-US", options)}] ${message}\n`; + const logMessage = `[${new Date().toLocaleString()}] ${message}\n`; return new Promise((resolve, reject) => { fs.appendFile(logFilePath, logMessage, (err) => { if (err) { diff --git a/tests/govtool-frontend/playwright/lib/pages/dRepDirectoryPage.ts b/tests/govtool-frontend/playwright/lib/pages/dRepDirectoryPage.ts index 10f7e9758..4714b507d 100644 --- a/tests/govtool-frontend/playwright/lib/pages/dRepDirectoryPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/dRepDirectoryPage.ts @@ -48,7 +48,6 @@ export default class DRepDirectoryPage { const delegateBtn = this.page.getByTestId(`${dRepId}-delegate-button`); await expect(delegateBtn).toBeVisible(); await this.page.getByTestId(`${dRepId}-delegate-button`).click(); - await this.searchInput.clear(); } async resetDRepForm() { diff --git a/tests/govtool-frontend/playwright/lib/pages/dRepRegistrationPage.ts b/tests/govtool-frontend/playwright/lib/pages/dRepRegistrationPage.ts index a8920d583..87b0054c9 100644 --- a/tests/govtool-frontend/playwright/lib/pages/dRepRegistrationPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/dRepRegistrationPage.ts @@ -40,6 +40,10 @@ export default class DRepRegistrationPage { @withTxConfirmation async register(dRepInfo: IDRepInfo) { + await this.registerWithoutTxConfirmation(dRepInfo); + } + + async registerWithoutTxConfirmation(dRepInfo: IDRepInfo) { await this.nameInput.fill(dRepInfo.name); if (dRepInfo.email != null) { diff --git a/tests/govtool-frontend/playwright/lib/pages/governanceActionsPage.ts b/tests/govtool-frontend/playwright/lib/pages/governanceActionsPage.ts index 312ecc0ba..159b1545f 100644 --- a/tests/govtool-frontend/playwright/lib/pages/governanceActionsPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/governanceActionsPage.ts @@ -64,7 +64,6 @@ export default class GovernanceActionsPage { } async getAllProposals() { - await this.page.waitForTimeout(2000); return this.page.locator('[data-test-id$="-card"]').all(); } @@ -87,12 +86,12 @@ export default class GovernanceActionsPage { await this.page.getByTestId(`${option}-radio`).check(); } - async validateSort( + async sortAndValidate( sortOption: string, validationFn: (p1: IProposal, p2: IProposal) => boolean, filterKeys = Object.keys(FilterOption) ) { - const responses = await Promise.all( + const responsesPromise = Promise.all( filterKeys.map((filterKey) => this.page.waitForResponse((response) => response @@ -101,39 +100,37 @@ export default class GovernanceActionsPage { ) ) ); - const proposalData = await Promise.all( + + await this.sortProposal(sortOption); + const responses = await responsesPromise; + + let proposalData: IProposal[][] = await Promise.all( responses.map(async (response) => { - return await response.json(); + const { elements } = await response.json(); + return elements.length ? elements : null; }) ); - expect(proposalData.length, "No proposals to sort").toBeGreaterThan(0); + proposalData = proposalData.filter(Boolean); // API validation proposalData.forEach(async (proposal) => { - if (proposal.elements.length <= 1) return; + if (proposal.length <= 1) return; - const proposals = proposal.elements as IProposal[]; + const proposals = proposal; for (let i = 0; i <= proposals.length - 2; i++) { const isValid = validationFn(proposals[i], proposals[i + 1]); - expect(isValid, "API Sorting validation failed").toBe(true); + expect(isValid).toBe(true); } }); - await this.page.waitForTimeout(2000); // Frontend validation - const proposalCards = await Promise.all( - filterKeys.map((key) => - this.page.getByTestId(`govaction-${key}-card`).allInnerTexts() - ) - ); - for (let dIdx = 0; dIdx <= proposalData.length - 1; dIdx++) { - const proposals = proposalData[dIdx].elements as IProposal[]; - for (let i = 0; i <= proposals.length - 1; i++) { - expect( - proposalCards[dIdx][i].includes(proposals[i].txHash), - "Frontend validation failed" - ).toBe(true); + const proposals = proposalData[dIdx] as IProposal[]; + const slides = await this.page + .locator(`[data-testid="govaction-${proposals[0].type}-card"]`) + .all(); + for (let i = 0; i <= slides.length - 1; i++) { + await expect(slides[i]).toContainText(`${proposals[i].txHash}`); } } } diff --git a/tests/govtool-frontend/playwright/lib/services/kuberService.ts b/tests/govtool-frontend/playwright/lib/services/kuberService.ts index 65190100e..51b9cf740 100644 --- a/tests/govtool-frontend/playwright/lib/services/kuberService.ts +++ b/tests/govtool-frontend/playwright/lib/services/kuberService.ts @@ -1,12 +1,10 @@ import { faucetWallet } from "@constants/staticWallets"; -import { ShelleyWallet } from "@helpers/crypto"; -import { KuberValue } from "@types"; +import { KuberValue, StaticWallet } from "@types"; import * as blake from "blakejs"; import environments from "lib/constants/environments"; import { LockInterceptor, LockInterceptorInfo } from "lib/lockInterceptor"; import fetch, { BodyInit, RequestInit } from "node-fetch"; import { cborxDecoder, cborxEncoder } from "../helpers/cborEncodeDecode"; -import convertBufferToHex from "../helpers/convertBufferToHex"; import { Logger } from "./../../../cypress/lib/logger/logger"; type CertificateType = "registerstake" | "registerdrep" | "deregisterdrep"; @@ -82,7 +80,10 @@ class Kuber { const submitTxCallback = async () => { return this.submitTx(signedTx, lockId); }; - return LockInterceptor.intercept(this.walletAddr, submitTxCallback, lockId); + return LockInterceptor.intercept( + this.walletAddr, + submitTxCallback + ); } async submitTx(signedTx: any, lockId?: string) { @@ -110,32 +111,42 @@ class Kuber { } const kuberService = { - initializeWallets: ( - senderAddress: string, - signingKey: string, - wallets: ShelleyWallet[] - ) => { - const kuber = new Kuber(senderAddress, signingKey); + submitTransaction(tx: any) { + return fetch(config.apiUrl + "/api/v1/tx/submit", { + method: "POST", + headers: { + "Content-Type": "application/json", + "api-key": config.apiKey, + }, + + body: JSON.stringify({ + tx: { + description: "", + type: "Tx ConwayEra", + cborHex: tx, + }, + }), + redirect: "follow", + }); + }, + // register stake and outputs 20A + initializeWallets: (wallets: StaticWallet[]) => { + const kuber = new Kuber(faucetWallet.address, faucetWallet.payment.private); const outputs = []; const stakes = []; const certificates = []; for (let i = 0; i < wallets.length; i++) { - const wallet = wallets[i]; - const address = wallet.addressBech32(environments.networkId); outputs.push({ - address: address, - value: 0, + address: wallets[i].address, + value: `${20}A`, }); stakes.push({ type: "PaymentSigningKeyShelley_ed25519", description: "Payment Signing Key", - cborHex: "5820" + convertBufferToHex(wallet.stakeKey.private), + cborHex: "5820" + wallets[i].stake.private, }); certificates.push( - Kuber.generateCert( - "registerstake", - convertBufferToHex(wallet.stakeKey.pkh) - ) + Kuber.generateCert("registerstake", wallets[i].stake.pkh) ); } return kuber.signAndSubmitTx({ @@ -144,25 +155,6 @@ const kuberService = { certificates, }); }, - - submitTransaction(tx: any) { - return fetch(config.apiUrl + "/api/v1/tx/submit", { - method: "POST", - headers: { - "Content-Type": "application/json", - "api-key": config.apiKey, - }, - - body: JSON.stringify({ - tx: { - description: "", - type: "Tx ConwayEra", - cborHex: tx, - }, - }), - redirect: "follow", - }); - }, transferADA: (receiverAddressList: string[], ADA = 20) => { const kuber = new Kuber(faucetWallet.address, faucetWallet.payment.private); const req = { @@ -176,6 +168,31 @@ const kuberService = { return kuber.signAndSubmitTx(req); }, + multipleTransferADA: (outputs: { address: string; value: string }[]) => { + const kuber = new Kuber(faucetWallet.address, faucetWallet.payment.private); + const req = { + outputs, + }; + return kuber.signAndSubmitTx(req); + }, + + multipleDRepRegistration: (wallets: StaticWallet[]) => { + const kuber = new Kuber(faucetWallet.address, faucetWallet.payment.private); + const req = { + certificates: wallets.map((wallet) => + Kuber.generateCert("registerdrep", wallet.stake.pkh) + ), + selections: wallets.map((wallet) => { + return { + type: "PaymentSigningKeyShelley_ed25519", + description: "Stake Signing Key", + cborHex: `5820${wallet.stake.private}`, + }; + }), + }; + return kuber.signAndSubmitTx(req); + }, + dRepRegistration: (stakeSigningKey: string, pkh: string) => { const kuber = new Kuber(faucetWallet.address, faucetWallet.payment.private); const req = { @@ -190,7 +207,6 @@ const kuberService = { }; return kuber.signAndSubmitTx(req); }, - dRepDeRegistration: ( addr: string, signingKey: string, diff --git a/tests/govtool-frontend/playwright/lib/walletManager.ts b/tests/govtool-frontend/playwright/lib/walletManager.ts new file mode 100644 index 000000000..051676b4f --- /dev/null +++ b/tests/govtool-frontend/playwright/lib/walletManager.ts @@ -0,0 +1,73 @@ +import { StaticWallet } from "@types"; +import * as fs from "fs"; +import { LockInterceptor } from "./lockInterceptor"; +const path = require("path"); + +const baseFilePath = path.resolve(__dirname, "./_mock"); + +type Purpose = "registerDRep" | "registeredDRep"; + +/** + * WalletManager class is responsible for managing a list of temporary wallets. + * It ensures that each wallet is used only once across all tests. + */ +class WalletManager { + private static instance: WalletManager; + + public static getInstance(): WalletManager { + if (!WalletManager.instance) { + WalletManager.instance = new WalletManager(); + } + return WalletManager.instance; + } + + async writeWallets(wallets: StaticWallet[], purpose: Purpose) { + await new Promise((resolve, reject) => + fs.writeFile( + `${baseFilePath}/${purpose}Wallets.json`, + JSON.stringify(wallets, null, 2), + (err) => { + if (err) { + reject(err); + } else { + resolve(); + } + } + ) + ); + } + + private async readWallets(purpose: Purpose): Promise { + const data: string = await new Promise((resolve, reject) => + fs.readFile( + `${baseFilePath}/${purpose}Wallets.json`, + "utf8", + (err, data) => { + if (err) { + reject(err); + } else { + resolve(data); + } + } + ) + ); + return JSON.parse(data); + } + + async popWallet(purpose: Purpose): Promise { + const popCb = async () => { + const wallets = await this.readWallets(purpose); + if (wallets.length === 0) { + throw new Error("No more wallets available"); + } + const wallet = wallets.pop(); + await this.writeWallets(wallets, purpose); + + await LockInterceptor.releaseLock("tempWallets"); + return wallet; + }; + + return await LockInterceptor.intercept("tempWallets", popCb); + } +} +export default WalletManager.getInstance(); diff --git a/tests/govtool-frontend/playwright/package.json b/tests/govtool-frontend/playwright/package.json index 4fcd599d0..56d315c53 100644 --- a/tests/govtool-frontend/playwright/package.json +++ b/tests/govtool-frontend/playwright/package.json @@ -25,7 +25,7 @@ "allure:serve": "npx allure serve", "test": "npx playwright test", "format": "prettier . --write", - "generate-wallets": "ts-node ./generate_wallets.ts 8" + "generate-wallets": "ts-node ./generate_wallets.ts 9" }, "dependencies": { "@cardanoapi/cardano-test-wallet": "^1.1.2", diff --git a/tests/govtool-frontend/playwright/playwright.config.ts b/tests/govtool-frontend/playwright/playwright.config.ts index 23722f668..1ab319893 100644 --- a/tests/govtool-frontend/playwright/playwright.config.ts +++ b/tests/govtool-frontend/playwright/playwright.config.ts @@ -1,33 +1,30 @@ import { defineConfig, devices } from "@playwright/test"; import { testPlanFilter } from "allure-playwright/dist/testplan"; -import { config } from "dotenv"; import environments from "lib/constants/environments"; -config(); - /** * Read environment variables from file. * https://github.com/motdotla/dotenv */ -// require('dotenv').config(); /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ + globalSetup: environments.ci ? require.resolve("./global-setup.ts") : "", testDir: "./tests", /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ - forbidOnly: !!process.env.CI, + forbidOnly: !!environments.ci, /* Retry on CI only */ retries: 0, /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? parseInt(process.env.TEST_WORKERS) : undefined, + workers: environments.ci ? parseInt(process.env.TEST_WORKERS) : undefined, /*use Allure Playwright's testPlanFilter() to determine the grep parameter*/ grep: testPlanFilter(), /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: process.env.CI ? [["line"], ["allure-playwright"]] : [["line"]], + reporter: environments.ci ? [["line"], ["allure-playwright"]] : [["line"]], /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ @@ -43,10 +40,6 @@ export default defineConfig({ /* Configure projects for major browsers */ projects: [ - { - name: "faucet setup", - testMatch: "**/faucet.setup.ts", - }, { name: "auth setup", testMatch: "**/auth.setup.ts", @@ -54,45 +47,37 @@ export default defineConfig({ { name: "dRep setup", testMatch: "**/dRep.setup.ts", - dependencies: ["faucet setup"], }, { name: "wallet bootstrap", testMatch: "**/wallet.bootstrap.ts", - dependencies: ["faucet setup"], }, { name: "transaction", use: { ...devices["Desktop Chrome"] }, testMatch: "**/*.tx.spec.ts", - dependencies: process.env.CI ? ["auth setup", "wallet bootstrap"] : [], + dependencies: environments.ci ? ["auth setup", "wallet bootstrap"] : [], }, { name: "loggedin (desktop)", use: { ...devices["Desktop Chrome"] }, testMatch: "**/*.loggedin.spec.ts", - dependencies: process.env.CI ? ["auth setup"] : [], - }, - { - name: "loggedin (mobile)", - use: { ...devices["Pixel 5"] }, - testMatch: "**/*.loggedin.spec.ts", - dependencies: process.env.CI ? ["auth setup"] : [], + dependencies: environments.ci ? ["auth setup"] : [], }, { name: "dRep", use: { ...devices["Desktop Chrome"] }, testMatch: "**/*.dRep.spec.ts", - dependencies: process.env.CI ? ["auth setup", "dRep setup"] : [], + dependencies: environments.ci ? ["auth setup", "dRep setup"] : [], }, { name: "delegation", use: { ...devices["Desktop Chrome"] }, testMatch: "**/*.delegation.spec.ts", - dependencies: process.env.CI + dependencies: environments.ci ? ["auth setup", "dRep setup", "wallet bootstrap"] : [], - teardown: process.env.CI && "cleanup delegation", + teardown: environments.ci && "cleanup delegation", }, { name: "independent (desktop)", @@ -105,13 +90,14 @@ export default defineConfig({ ], }, { - name: "independent (mobile)", + name: "mobile", use: { ...devices["Pixel 5"] }, testIgnore: [ "**/*.loggedin.spec.ts", "**/*.dRep.spec.ts", "**/*.delegation.spec.ts", "**/*.tx.spec.ts", + "**/walletConnect.spec.ts", ], }, { diff --git a/tests/govtool-frontend/playwright/tests/1-wallet-connect/walletConnect.spec.ts b/tests/govtool-frontend/playwright/tests/1-wallet-connect/walletConnect.spec.ts index f23e5947e..e9ea95072 100644 --- a/tests/govtool-frontend/playwright/tests/1-wallet-connect/walletConnect.spec.ts +++ b/tests/govtool-frontend/playwright/tests/1-wallet-connect/walletConnect.spec.ts @@ -37,23 +37,20 @@ test("1C: Should disconnect Wallet When connected", async ({ page }) => { await loginPage.logout(); }); -test("1D. Should check correct network (Testnet/Mainnet) on connection", async ({ - page, -}) => { +test("1D. Should reject wallet connection in mainnet", async ({ page }) => { const wrongNetworkId = 1; // mainnet network await createWallet(page, { networkId: wrongNetworkId, }); - const errors: Array = []; - page.on("pageerror", (error) => { - errors.push(error); - }); + await page.goto("/"); - const loginPage = new LoginPage(page); - await loginPage.login(); + await page.getByTestId("connect-wallet-button").click(); + await page.getByTestId("demos-wallet-button").click(); - expect(errors).not.toHaveLength(0); + await expect(page.getByTestId("wallet-connection-error-modal")).toHaveText( + /You are trying to connect/ + ); }); test("1E. Should hide incompatible wallets when connecting", async ({ diff --git a/tests/govtool-frontend/playwright/tests/2-delegation/delegation.drep.spec.ts b/tests/govtool-frontend/playwright/tests/2-delegation/delegation.drep.spec.ts index 4e446cdc1..47b2a446b 100644 --- a/tests/govtool-frontend/playwright/tests/2-delegation/delegation.drep.spec.ts +++ b/tests/govtool-frontend/playwright/tests/2-delegation/delegation.drep.spec.ts @@ -8,11 +8,11 @@ import { ShelleyWallet } from "@helpers/crypto"; import { isMobile, openDrawer } from "@helpers/mobile"; import { createNewPageWithWallet } from "@helpers/page"; import extractDRepFromWallet from "@helpers/shellyWallet"; -import { transferAdaForWallet } from "@helpers/transaction"; import DRepDetailsPage from "@pages/dRepDetailsPage"; import DRepDirectoryPage from "@pages/dRepDirectoryPage"; import DRepRegistrationPage from "@pages/dRepRegistrationPage"; import { expect } from "@playwright/test"; +import walletManager from "lib/walletManager"; test.beforeEach(async () => { await setAllureEpic("2. Delegation"); @@ -27,9 +27,9 @@ test("2C. Should open wallet connection popup on delegate in disconnected state" } await page.getByTestId("view-drep-directory-button").click(); + await page.getByTestId("search-input").fill(dRep01Wallet.dRepId); await page - .locator('[data-testid$="-connect-to-delegate-button"]') - .first() + .getByTestId(`${dRep01Wallet.dRepId}-connect-to-delegate-button`) .click(); await expect(page.getByTestId("connect-your-wallet-modal")).toBeVisible(); }); @@ -52,11 +52,9 @@ test("2N. Should show DRep information on details page", async ({ page, browser, }, testInfo) => { - test.setTimeout(testInfo.timeout + 2 * environments.txTimeOut); + test.setTimeout(testInfo.timeout + environments.txTimeOut); - const wallet = await ShelleyWallet.generate(); - - await transferAdaForWallet(wallet, 600); + const wallet = await walletManager.popWallet("registerDRep"); const tempDRepAuth = await createTempDRepAuth(page, wallet); const dRepPage = await createNewPageWithWallet(browser, { @@ -68,7 +66,6 @@ test("2N. Should show DRep information on details page", async ({ const dRepRegistrationPage = new DRepRegistrationPage(dRepPage); await dRepRegistrationPage.goto(); - const dRepId = extractDRepFromWallet(wallet); const name = faker.person.firstName(); const email = faker.internet.email({ firstName: name }); const bio = faker.person.bio(); @@ -89,11 +86,11 @@ test("2N. Should show DRep information on details page", async ({ const dRepDirectory = new DRepDirectoryPage(dRepPage); await dRepDirectory.goto(); - await dRepDirectory.searchInput.fill(dRepId); - await dRepPage.getByTestId(`${dRepId}-view-details-button`).click(); + await dRepDirectory.searchInput.fill(wallet.dRepId); + await dRepPage.getByTestId(`${wallet.dRepId}-view-details-button`).click(); // Verification - await expect(dRepPage.getByTestId("copy-drep-id-button")).toHaveText(dRepId); + await expect(dRepPage.getByTestId("copy-drep-id-button")).toHaveText(wallet.dRepId); await expect(dRepPage.getByText("Active", { exact: true })).toBeVisible(); await expect(dRepPage.locator("dl").getByText("₳ 0")).toBeVisible(); await expect(dRepPage.getByText(email, { exact: true })).toBeVisible(); @@ -150,3 +147,29 @@ test.describe("Insufficient funds", () => { await expect(dRepDirectoryPage.delegationErrorModal).toBeVisible(); }); }); + +test("2I. Should check validity of DRep Id", async ({ page }) => { + const dRepDirectory = new DRepDirectoryPage(page); + await dRepDirectory.goto(); + + await dRepDirectory.searchInput.fill(dRep01Wallet.dRepId); + await expect(dRepDirectory.getDRepCard(dRep01Wallet.dRepId)).toHaveText( + dRep01Wallet.dRepId + ); + + const wallet = await ShelleyWallet.generate(); + const invalidDRepId = extractDRepFromWallet(wallet); + + await dRepDirectory.searchInput.fill(invalidDRepId); + await expect(dRepDirectory.getDRepCard(invalidDRepId)).not.toBeVisible(); +}); + +test("2J. Should search by DRep id", async ({ page }) => { + const dRepDirectory = new DRepDirectoryPage(page); + await dRepDirectory.goto(); + + await dRepDirectory.searchInput.fill(dRep01Wallet.dRepId); + await expect(dRepDirectory.getDRepCard(dRep01Wallet.dRepId)).toHaveText( + dRep01Wallet.dRepId + ); +}); diff --git a/tests/govtool-frontend/playwright/tests/2-delegation/delegation.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/2-delegation/delegation.loggedin.spec.ts index c1271c825..04c34f1e5 100644 --- a/tests/govtool-frontend/playwright/tests/2-delegation/delegation.loggedin.spec.ts +++ b/tests/govtool-frontend/playwright/tests/2-delegation/delegation.loggedin.spec.ts @@ -1,9 +1,7 @@ -import { dRep01Wallet, user01Wallet } from "@constants/staticWallets"; +import { user01Wallet } from "@constants/staticWallets"; import { test } from "@fixtures/walletExtension"; import { setAllureEpic } from "@helpers/allure"; -import { ShelleyWallet } from "@helpers/crypto"; import { isMobile } from "@helpers/mobile"; -import extractDRepFromWallet from "@helpers/shellyWallet"; import DRepDirectoryPage from "@pages/dRepDirectoryPage"; import { expect } from "@playwright/test"; @@ -26,22 +24,6 @@ test("2B. Should access DRep Directory page", async ({ page }) => { } }); -test("2I. Should check validity of DRep Id", async ({ page }) => { - const dRepDirectory = new DRepDirectoryPage(page); - await dRepDirectory.goto(); - - await dRepDirectory.searchInput.fill(dRep01Wallet.dRepId); - await expect(dRepDirectory.getDRepCard(dRep01Wallet.dRepId)).toHaveText( - dRep01Wallet.dRepId - ); - - const wallet = await ShelleyWallet.generate(); - const invalidDRepId = extractDRepFromWallet(wallet); - - await dRepDirectory.searchInput.fill(invalidDRepId); - await expect(dRepDirectory.getDRepCard(invalidDRepId)).not.toBeVisible(); -}); - test("2D. Should show delegation options in connected state", async ({ page, }) => { diff --git a/tests/govtool-frontend/playwright/tests/2-delegation/delegation.spec.ts b/tests/govtool-frontend/playwright/tests/2-delegation/delegation.spec.ts index 17cb4136d..6850072df 100644 --- a/tests/govtool-frontend/playwright/tests/2-delegation/delegation.spec.ts +++ b/tests/govtool-frontend/playwright/tests/2-delegation/delegation.spec.ts @@ -1,6 +1,5 @@ -import { dRep01Wallet } from "@constants/staticWallets"; -import DRepDirectoryPage from "@pages/dRepDirectoryPage"; import { setAllureEpic } from "@helpers/allure"; +import DRepDirectoryPage from "@pages/dRepDirectoryPage"; import { expect, test } from "@playwright/test"; import { DRepStatus } from "@types"; @@ -8,44 +7,6 @@ test.beforeEach(async () => { await setAllureEpic("2. Delegation"); }); -test("2J. Should search by DRep id", async ({ page }) => { - const dRepDirectory = new DRepDirectoryPage(page); - await dRepDirectory.goto(); - - await dRepDirectory.searchInput.fill(dRep01Wallet.dRepId); - await expect(dRepDirectory.getDRepCard(dRep01Wallet.dRepId)).toHaveText( - dRep01Wallet.dRepId - ); -}); - -test("2K. Should filter DReps", async ({ page }) => { - const dRepFilterOptions: DRepStatus[] = ["Active", "Inactive", "Retired"]; - - const dRepDirectory = new DRepDirectoryPage(page); - await dRepDirectory.goto(); - - await dRepDirectory.filterBtn.click(); - - // Single filter - for (const option of dRepFilterOptions) { - await dRepDirectory.filterDReps([option]); - await dRepDirectory.validateFilters([option], dRepFilterOptions); - await dRepDirectory.unFilterDReps([option]); - } - - // Multiple filters - const multipleFilterOptionNames = [...dRepFilterOptions]; - while (multipleFilterOptionNames.length > 1) { - await dRepDirectory.filterDReps(multipleFilterOptionNames); - await dRepDirectory.validateFilters( - multipleFilterOptionNames, - dRepFilterOptions - ); - await dRepDirectory.unFilterDReps(multipleFilterOptionNames); - multipleFilterOptionNames.pop(); - } -}); - test("2M. Should sort DReps", async ({ page }) => { test.slow(); @@ -93,3 +54,31 @@ test("2O. Should load more DReps on show more", async ({ page }) => { await expect(dRepDirectory.showMoreBtn).not.toBeVisible(); } }); + +test("2K. Should filter DReps", async ({ page }) => { + const dRepFilterOptions: DRepStatus[] = ["Active", "Inactive", "Retired"]; + + const dRepDirectory = new DRepDirectoryPage(page); + await dRepDirectory.goto(); + + await dRepDirectory.filterBtn.click(); + + // Single filter + for (const option of dRepFilterOptions) { + await dRepDirectory.filterDReps([option]); + await dRepDirectory.validateFilters([option], dRepFilterOptions); + await dRepDirectory.unFilterDReps([option]); + } + + // Multiple filters + const multipleFilterOptionNames = [...dRepFilterOptions]; + while (multipleFilterOptionNames.length > 1) { + await dRepDirectory.filterDReps(multipleFilterOptionNames); + await dRepDirectory.validateFilters( + multipleFilterOptionNames, + dRepFilterOptions + ); + await dRepDirectory.unFilterDReps(multipleFilterOptionNames); + multipleFilterOptionNames.pop(); + } +}); diff --git a/tests/govtool-frontend/playwright/tests/2-delegation/delegationFunctionality.delegation.spec.ts b/tests/govtool-frontend/playwright/tests/2-delegation/delegationFunctionality.delegation.spec.ts index 18a5556b0..60e361241 100644 --- a/tests/govtool-frontend/playwright/tests/2-delegation/delegationFunctionality.delegation.spec.ts +++ b/tests/govtool-frontend/playwright/tests/2-delegation/delegationFunctionality.delegation.spec.ts @@ -4,32 +4,26 @@ import { adaHolder02Wallet, adaHolder03Wallet, adaHolder04Wallet, + adaHolder05Wallet, dRep01Wallet, dRep02Wallet, } from "@constants/staticWallets"; import { createTempDRepAuth } from "@datafactory/createAuth"; import { test } from "@fixtures/walletExtension"; import { setAllureEpic } from "@helpers/allure"; -import { ShelleyWallet } from "@helpers/crypto"; import { createNewPageWithWallet } from "@helpers/page"; -import extractDRepFromWallet from "@helpers/shellyWallet"; -import { - registerDRepForWallet, - registerStakeForWallet, - transferAdaForWallet, - waitForTxConfirmation, -} from "@helpers/transaction"; +import { waitForTxConfirmation } from "@helpers/transaction"; import DRepDirectoryPage from "@pages/dRepDirectoryPage"; import { Page, expect } from "@playwright/test"; import kuberService from "@services/kuberService"; +import { StaticWallet } from "@types"; +import walletManager from "lib/walletManager"; test.beforeEach(async () => { await setAllureEpic("2. Delegation"); }); test.describe("Delegate to others", () => { - test.describe.configure({ mode: "serial" }); - test.use({ storageState: ".auth/adaHolder01.json", wallet: adaHolder01Wallet, @@ -38,7 +32,7 @@ test.describe("Delegate to others", () => { test("2A. Should show delegated DRep Id (on Dashboard, and DRep Directory) after delegation", async ({ page, }, testInfo) => { - test.setTimeout(testInfo.timeout + 2 * environments.txTimeOut); + test.setTimeout(testInfo.timeout + environments.txTimeOut); const dRepId = dRep01Wallet.dRepId; @@ -51,57 +45,50 @@ test.describe("Delegate to others", () => { await expect( page.getByTestId(`${dRepId}-delegate-button')`) ).not.toBeVisible(); - await expect(page.getByText(dRepId)).toHaveCount(1, { timeout: 10_000 }); + await expect(page.getByTestId(`${dRepId}-copy-id-button`)).toHaveCount(1, { + timeout: 20_000, + }); // Verify dRepId in dashboard await page.goto("/dashboard"); await expect(page.getByText(dRepId)).toBeVisible(); }); +}); - test("2W. Should display voting power of DRep", async ({ page, browser }) => { - const dRepPage = await createNewPageWithWallet(browser, { - storageState: ".auth/dRep01.json", - wallet: ShelleyWallet.fromJson(dRep01Wallet), - enableStakeSigning: true, - }); - - const adaHolder01VotingPower = await kuberService.getBalance( - adaHolder01Wallet.address - ); - - await expect( - dRepPage.getByText(`Voting power:₳ ${adaHolder01VotingPower}`) - ).toBeVisible(); - console.log({ adaHolder01VotingPower }); - - await dRepPage.goto("/"); +test.describe("Change delegation", () => { + test.use({ + storageState: ".auth/adaHolder02.json", + wallet: adaHolder02Wallet, }); - test("2F. Should change delegated dRep", async ({ page }, testInfo) => { + test("2F. Should change delegated DRep", async ({ page }, testInfo) => { test.setTimeout(testInfo.timeout + 2 * environments.txTimeOut); - const dRepId = dRep02Wallet.dRepId; + const dRepIdFirst = dRep01Wallet.dRepId; + const dRepIdSecond = dRep02Wallet.dRepId; const dRepDirectoryPage = new DRepDirectoryPage(page); await dRepDirectoryPage.goto(); - await dRepDirectoryPage.delegateToDRep(dRepId); - await expect(page.getByTestId(`${dRepId}-copy-id-button`)).toHaveText( - dRepId + await dRepDirectoryPage.delegateToDRep(dRepIdFirst); + await expect(page.getByTestId(`${dRepIdFirst}-copy-id-button`)).toHaveText( + dRepIdFirst, + { timeout: 20_000 } + ); // verify delegation + + await dRepDirectoryPage.delegateToDRep(dRepIdSecond); + await expect(page.getByTestId(`${dRepIdSecond}-copy-id-button`)).toHaveText( + dRepIdSecond, + { timeout: 20_000 } ); // verify delegation }); }); test.describe("Delegate to myself", () => { let dRepPage: Page; - let wallet: ShelleyWallet; + let wallet: StaticWallet; test.beforeEach(async ({ page, browser }, testInfo) => { - test.setTimeout(testInfo.timeout + 2 * environments.txTimeOut); - - wallet = await ShelleyWallet.generate(); - - await transferAdaForWallet(wallet, 600); - await registerStakeForWallet(wallet); + wallet = await walletManager.popWallet("registerDRep"); const dRepAuth = await createTempDRepAuth(page, wallet); dRepPage = await createNewPageWithWallet(browser, { @@ -112,9 +99,9 @@ test.describe("Delegate to myself", () => { }); test("2E. Should register as Sole voter", async ({ page }, testInfo) => { - test.setTimeout(testInfo.timeout + 2 * environments.txTimeOut); + test.setTimeout(testInfo.timeout + environments.txTimeOut); - const dRepId = extractDRepFromWallet(wallet); + const dRepId = wallet.dRepId; await dRepPage.goto("/"); await dRepPage.getByTestId("register-as-sole-voter-button").click(); @@ -126,6 +113,7 @@ test.describe("Delegate to myself", () => { await waitForTxConfirmation(dRepPage); // Checks in dashboard + // BUG await expect(page.getByText(dRepId)).toHaveText(dRepId); // Checks in dRep directory @@ -137,7 +125,9 @@ test.describe("Delegate to myself", () => { ); }); - test("2S. Should retire as a Direct Voter on delegating to another DRep", async () => { + test("2S. Should retire as a Direct Voter on delegating to another DRep", async ({}, testInfo) => { + test.setTimeout(testInfo.timeout + environments.txTimeOut); + await dRepPage.goto("/"); await dRepPage.getByTestId("register-as-sole-voter-button").click(); await dRepPage.getByTestId("continue-button").click(); @@ -162,8 +152,8 @@ test.describe("Delegate to myself", () => { test.describe("Multiple delegations", () => { test.use({ - storageState: ".auth/adaHolder02.json", - wallet: adaHolder02Wallet, + storageState: ".auth/adaHolder05.json", + wallet: adaHolder05Wallet, }); test("2R. Should display a modal indicating waiting for previous transaction when delegating if the previous transaction is not completed", async ({ @@ -190,133 +180,54 @@ test.describe("Multiple delegations", () => { }); test.describe("Abstain delegation", () => { - test.describe.configure({ mode: "serial" }); - - let dRepWallet: ShelleyWallet; - - test.beforeAll(async () => { - dRepWallet = await ShelleyWallet.generate(); + test.use({ + storageState: ".auth/adaHolder03.json", + wallet: adaHolder03Wallet, }); - test("2U_1. Register DRep and Delegate", async ({ + test("2U. Should show delegated voting power to Abstain", async ({ page, - browser, }, testInfo) => { - test.setTimeout(testInfo.timeout + 2 * environments.txTimeOut); + test.setTimeout(testInfo.timeout + environments.txTimeOut); - await registerDRepForWallet(dRepWallet); - - const adaHolderPage = await createNewPageWithWallet(browser, { - storageState: ".auth/adaHolder03.json", - wallet: ShelleyWallet.fromJson(adaHolder03Wallet), - enableStakeSigning: true, - }); - const dRepDirectoryPage = new DRepDirectoryPage(adaHolderPage); + const dRepDirectoryPage = new DRepDirectoryPage(page); await dRepDirectoryPage.goto(); - const dRepId = extractDRepFromWallet(dRepWallet); - await dRepDirectoryPage.delegateToDRep(dRepId); - console.debug(`Delegated to ${dRepId}`); - }); - - test("2U_2. Should delegate my own voting power to Abstain as registered DRep with delegated voting power", async ({ - page, - browser, - }, testInfo) => { - test.setTimeout(testInfo.timeout + 2 * environments.txTimeOut); - - await transferAdaForWallet(dRepWallet); - await registerStakeForWallet(dRepWallet); - - const dRepId = extractDRepFromWallet(dRepWallet); - console.debug(`Using ${dRepId}`); - const tempDRepAuth = await createTempDRepAuth(page, dRepWallet); - const dRepPage = await createNewPageWithWallet(browser, { - storageState: tempDRepAuth, - wallet: dRepWallet, - enableStakeSigning: true, - }); - - const dRepDirectoryPage = new DRepDirectoryPage(dRepPage); - await dRepDirectoryPage.goto(); await dRepDirectoryPage.automaticDelegationOptionsDropdown.click(); - await dRepPage - .getByTestId("abstain-from-every-vote-delegate-button") - .click(); - await waitForTxConfirmation(dRepPage); + await page.getByTestId("abstain-from-every-vote-delegate-button").click(); + await waitForTxConfirmation(page); + + const balance = await kuberService.getBalance(adaHolder03Wallet.address); - const balance = await kuberService.getBalance( - dRepWallet.addressBech32(environments.networkId) - ); await expect( - dRepPage.getByText(`You have delegated ₳${balance}`) + page.getByText(`You have delegated ₳${balance}`) ).toBeVisible(); - console.log({ balance }); }); }); test.describe("No confidence delegation", () => { - test.describe.configure({ mode: "serial" }); - - let dRepWallet: ShelleyWallet; - - test.beforeAll(async () => { - dRepWallet = await ShelleyWallet.generate(); + test.use({ + storageState: ".auth/adaHolder04.json", + wallet: adaHolder04Wallet, }); - test("2V_1. Register DRep and Delegate", async ({ + test("2V. Should show delegated voting power to No confidence", async ({ page, - browser, }, testInfo) => { - test.setTimeout(testInfo.timeout + 2 * environments.txTimeOut); + test.setTimeout(testInfo.timeout + environments.txTimeOut); - await registerDRepForWallet(dRepWallet); - - const adaHolderPage = await createNewPageWithWallet(browser, { - storageState: ".auth/adaHolder04.json", - wallet: ShelleyWallet.fromJson(adaHolder04Wallet), - enableStakeSigning: true, - }); - const dRepDirectoryPage = new DRepDirectoryPage(adaHolderPage); + const dRepDirectoryPage = new DRepDirectoryPage(page); await dRepDirectoryPage.goto(); - const dRepId = extractDRepFromWallet(dRepWallet); - await dRepDirectoryPage.delegateToDRep(dRepId); - console.debug(`Delegated to ${dRepId}`); - }); - - test("2V_2. Should delegate my own voting power to Abstain as registered DRep with delegated voting power", async ({ - page, - browser, - }, testInfo) => { - test.setTimeout(testInfo.timeout + 2 * environments.txTimeOut); - - await transferAdaForWallet(dRepWallet); - await registerStakeForWallet(dRepWallet); - - const dRepId = extractDRepFromWallet(dRepWallet); - console.debug(`Using ${dRepId}`); - const tempDRepAuth = await createTempDRepAuth(page, dRepWallet); - const dRepPage = await createNewPageWithWallet(browser, { - storageState: tempDRepAuth, - wallet: dRepWallet, - enableStakeSigning: true, - }); - - const dRepDirectoryPage = new DRepDirectoryPage(dRepPage); - await dRepDirectoryPage.goto(); await dRepDirectoryPage.automaticDelegationOptionsDropdown.click(); - await dRepPage + await page .getByTestId("signal-no-confidence-on-every-vote-delegate-button") .click(); - await waitForTxConfirmation(dRepPage); + await waitForTxConfirmation(page); - const balance = await kuberService.getBalance( - dRepWallet.addressBech32(environments.networkId) - ); + const balance = await kuberService.getBalance(adaHolder04Wallet.address); await expect( - dRepPage.getByText(`You have delegated ₳${balance}`) + page.getByText(`You have delegated ₳${balance}`) ).toBeVisible(); - console.log({ balance }); }); }); diff --git a/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.dRep.spec.ts b/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.dRep.spec.ts index 515179e7a..d42a722a5 100644 --- a/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.dRep.spec.ts +++ b/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.dRep.spec.ts @@ -4,17 +4,14 @@ import { createTempDRepAuth } from "@datafactory/createAuth"; import { faker } from "@faker-js/faker"; import { test } from "@fixtures/walletExtension"; import { setAllureEpic } from "@helpers/allure"; -import { ShelleyWallet } from "@helpers/crypto"; +import { downloadMetadata } from "@helpers/metadata"; import { createNewPageWithWallet } from "@helpers/page"; -import { - registerDRepForWallet, - transferAdaForWallet, - waitForTxConfirmation, -} from "@helpers/transaction"; +import { waitForTxConfirmation } from "@helpers/transaction"; import DRepRegistrationPage from "@pages/dRepRegistrationPage"; import GovernanceActionsPage from "@pages/governanceActionsPage"; -import { expect } from "@playwright/test"; -import * as crypto from "crypto"; +import { Download, expect } from "@playwright/test"; +import metadataBucketService from "@services/metadataBucketService"; +import walletManager from "lib/walletManager"; test.beforeEach(async () => { await setAllureEpic("3. DRep registration"); @@ -32,15 +29,34 @@ test.describe("Logged in DReps", () => { ); // BUG: testId -> dRep-id-display-dashboard (It is taking sidebar dRep-id) }); - test.use({ storageState: ".auth/dRep01.json", wallet: dRep01Wallet }); + test("3H. Should Update DRep data", async ({ page }, testInfo) => { + test.setTimeout(testInfo.timeout + environments.txTimeOut); + + await page.goto("/"); + + await page.getByTestId("view-drep-details-button").click(); + await page.getByTestId("edit-drep-data-button").click(); + + const newDRepName = faker.internet.userName(); + await page.getByPlaceholder("ex. JohnDRep").fill(newDRepName); + await page.getByTestId("continue-button").click(); + await page.getByRole("checkbox").click(); + await page.getByTestId("continue-button").click(); - // Skipped: No option to update metadata - test("3H. Should be able to update metadata ", async ({ page }) => { - test.skip(); - page.getByTestId("change-metadata-button").click(); - page.getByTestId("url-input").fill("https://google.com"); - page.getByTestId("hash-input").fill(crypto.randomBytes(32).toString("hex")); - await expect(page.getByTestId("confirm-modal-button")).toBeVisible(); + page.getByRole("button", { name: `${newDRepName}.jsonld` }).click(); + const download: Download = await page.waitForEvent("download"); + const dRepMetadata = await downloadMetadata(download); + + const url = await metadataBucketService.uploadMetadata( + dRepMetadata.name, + dRepMetadata.data + ); + + await page.getByPlaceholder("URL").fill(url); + await page.getByTestId("continue-button").click(); // BUG -> incorrect test id + await page.getByTestId("confirm-modal-button").click(); + + await waitForTxConfirmation(page); }); }); @@ -48,11 +64,8 @@ test.describe("Temporary DReps", () => { test("3G. Should show confirmation message with link to view transaction, when DRep registration txn is submitted", async ({ page, browser, - }, testInfo) => { - test.setTimeout(testInfo.timeout + environments.txTimeOut); - - const wallet = await ShelleyWallet.generate(); - await transferAdaForWallet(wallet, 600); + }) => { + const wallet = await walletManager.popWallet("registerDRep"); const tempDRepAuth = await createTempDRepAuth(page, wallet); const dRepPage = await createNewPageWithWallet(browser, { @@ -71,14 +84,8 @@ test.describe("Temporary DReps", () => { ).toBeVisible(); }); - test("3I. Should verify retire as DRep", async ({ - page, - browser, - }, testInfo) => { - test.setTimeout(testInfo.timeout + environments.txTimeOut); - - const wallet = await ShelleyWallet.generate(); - await registerDRepForWallet(wallet); + test("3I. Should verify retire as DRep", async ({ page, browser }) => { + const wallet = await walletManager.popWallet("registeredDRep"); const tempDRepAuth = await createTempDRepAuth(page, wallet); const dRepPage = await createNewPageWithWallet(browser, { @@ -100,12 +107,9 @@ test.describe("Temporary DReps", () => { page, browser, }, testInfo) => { - test.setTimeout(testInfo.timeout + 3 * environments.txTimeOut); - - const wallet = await ShelleyWallet.generate(); - await registerDRepForWallet(wallet); + test.setTimeout(testInfo.timeout + environments.txTimeOut); - await transferAdaForWallet(wallet); + const wallet = await walletManager.popWallet("registeredDRep"); const dRepAuth = await createTempDRepAuth(page, wallet); const dRepPage = await createNewPageWithWallet(browser, { @@ -122,6 +126,7 @@ test.describe("Temporary DReps", () => { ).toBeVisible(); dRepPage.getByTestId("confirm-modal-button").click(); await waitForTxConfirmation(dRepPage); + await expect(dRepPage.getByText("Voting power:₳")).not.toBeVisible(); const governanceActionsPage = new GovernanceActionsPage(dRepPage); await governanceActionsPage.goto(); @@ -136,9 +141,7 @@ test.describe("Temporary DReps", () => { }, testInfo) => { test.setTimeout(testInfo.timeout + environments.txTimeOut); - const wallet = await ShelleyWallet.generate(); - - await transferAdaForWallet(wallet, 600); + const wallet = await walletManager.popWallet("registerDRep"); const dRepAuth = await createTempDRepAuth(page, wallet); const dRepPage = await createNewPageWithWallet(browser, { diff --git a/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.loggedin.spec.ts index 8d6a74b70..8b85c0943 100644 --- a/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.loggedin.spec.ts +++ b/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.loggedin.spec.ts @@ -2,6 +2,7 @@ import { user01Wallet } from "@constants/staticWallets"; import { faker } from "@faker-js/faker"; import { test } from "@fixtures/walletExtension"; import { setAllureEpic } from "@helpers/allure"; +import { invalid as mockInvalid } from "@mock/index"; import DRepRegistrationPage from "@pages/dRepRegistrationPage"; import { expect } from "@playwright/test"; @@ -34,6 +35,8 @@ test("3D. Verify DRep registration form", async ({ page }) => { }); test("3E. Should accept valid data in DRep form", async ({ page }) => { + test.slow(); + const dRepRegistrationPage = new DRepRegistrationPage(page); await dRepRegistrationPage.goto(); @@ -55,48 +58,17 @@ test("3E. Should accept valid data in DRep form", async ({ page }) => { }); test("3L. Should reject invalid data in DRep form", async ({ page }) => { + test.slow(); + const dRepRegistrationPage = new DRepRegistrationPage(page); await dRepRegistrationPage.goto(); - function generateInvalidEmail() { - const choice = faker.number.int({ min: 1, max: 3 }); - - if (choice === 1) { - return faker.lorem.word() + faker.number + "@invalid.com"; - } else if (choice == 2) { - return faker.lorem.word() + "@"; - } - return faker.lorem.word() + "@gmail_com"; - } - function generateInvalidUrl() { - const choice = faker.number.int({ min: 1, max: 3 }); - - if (choice === 1) { - return faker.internet.url().replace("https://", "http://"); - } else if (choice === 2) { - return faker.lorem.word() + ".invalid"; - } - return faker.lorem.word() + ".@com"; - } - function generateInvalidName() { - const choice = faker.number.int({ min: 1, max: 3 }); - if (choice === 1) { - // space invalid - return faker.lorem.word() + " " + faker.lorem.word(); - } else if (choice === 2) { - // maximum 80 words invalid - return faker.lorem.paragraphs().replace(/\s+/g, ""); - } - // empty invalid - return " "; - } - for (let i = 0; i < 100; i++) { await dRepRegistrationPage.inValidateForm( - generateInvalidName(), - generateInvalidEmail(), + mockInvalid.name(), + mockInvalid.email(), faker.lorem.paragraph(40), - generateInvalidUrl() + mockInvalid.url() ); } }); @@ -107,10 +79,7 @@ test("3F. Should create proper DRep registration request, when registered with d const dRepRegistrationPage = new DRepRegistrationPage(page); await dRepRegistrationPage.goto(); - await dRepRegistrationPage.register({ name: "Test" }).catch((err) => { - // Fails because real tx is not submitted - }); - + await dRepRegistrationPage.registerWithoutTxConfirmation({ name: "Test" }); await expect( page.getByTestId("registration-transaction-error-modal") ).toBeVisible(); diff --git a/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.dRep.spec.ts b/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.dRep.spec.ts index 10102d754..f58843ab8 100644 --- a/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.dRep.spec.ts +++ b/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.dRep.spec.ts @@ -1,27 +1,22 @@ -import environments from "@constants/environments"; import { dRep01Wallet } from "@constants/staticWallets"; import { createTempDRepAuth } from "@datafactory/createAuth"; import { faker } from "@faker-js/faker"; import { test } from "@fixtures/walletExtension"; import { setAllureEpic } from "@helpers/allure"; import { lovelaceToAda } from "@helpers/cardano"; -import { ShelleyWallet } from "@helpers/crypto"; import { createNewPageWithWallet } from "@helpers/page"; -import { - registerDRepForWallet, - transferAdaForWallet, -} from "@helpers/transaction"; import GovernanceActionsPage from "@pages/governanceActionsPage"; import { Page, expect } from "@playwright/test"; import { FilterOption, IProposal } from "@types"; +import walletManager from "lib/walletManager"; + +test.beforeEach(async () => { + await setAllureEpic("4. Proposal visibility"); +}); test.describe("Logged in DRep", () => { test.use({ storageState: ".auth/dRep01.json", wallet: dRep01Wallet }); - test.beforeEach(async () => { - await setAllureEpic("4. Proposal visibility"); - }); - test("4E. Should display DRep's voting power in governance actions page", async ({ page, }) => { @@ -36,30 +31,13 @@ test.describe("Logged in DRep", () => { page.getByText(`₳ ${lovelaceToAda(votingPower)}`) ).toBeVisible(); }); - - test("4F. Should Disable DRep functionality upon wallet disconnection on governance page", async ({ - page, - }) => { - const governanceActionsPage = new GovernanceActionsPage(page); - await governanceActionsPage.goto(); - - await page.getByTestId("disconnect-button").click(); - - const govActionDetailsPage = - await governanceActionsPage.viewFirstProposal(); - await expect(govActionDetailsPage.voteBtn).not.toBeVisible(); - }); }); test.describe("Temporary DReps", async () => { let dRepPage: Page; - test.beforeEach(async ({ page, browser }, testInfo) => { - test.setTimeout(testInfo.timeout + 2 * environments.txTimeOut); - - const wallet = await ShelleyWallet.generate(); - await registerDRepForWallet(wallet); - await transferAdaForWallet(wallet, 40); + test.beforeEach(async ({ page, browser }) => { + const wallet = await walletManager.popWallet("registeredDRep"); const tempDRepAuth = await createTempDRepAuth(page, wallet); @@ -82,58 +60,75 @@ test.describe("Temporary DReps", async () => { }); }); -test("4G. Should display correct vote counts on governance details page for DRep", async ({ - page, -}) => { - const responsesPromise = Object.keys(FilterOption).map((filterKey) => - page.waitForResponse((response) => - response.url().includes(`&type[]=${FilterOption[filterKey]}`) - ) - ); +test.describe("Check vote count", () => { + test.use({ storageState: ".auth/dRep01.json", wallet: dRep01Wallet }); - const governanceActionsPage = new GovernanceActionsPage(page); - await governanceActionsPage.goto(); - const responses = await Promise.all(responsesPromise); - const proposals: IProposal[] = ( - await Promise.all( - responses.map(async (response) => { - const data = await response.json(); - return data.elements; - }) - ) - ).flat(); - - expect(proposals.length, "No proposals found!").toBeGreaterThan(0); - - const proposalToCheck = proposals[0]; - const govActionDetailsPage = - await governanceActionsPage.viewProposal(proposalToCheck); - await govActionDetailsPage.showVotesBtn.click(); - - await expect( - page - .getByText("yes₳") - .getByText(`₳ ${lovelaceToAda(proposalToCheck.yesVotes)}`) - ).toBeVisible(); - await expect( - page - .getByText("abstain₳") - .getByText(`₳ ${lovelaceToAda(proposalToCheck.abstainVotes)}`) - ).toBeVisible(); - await expect( - page - .getByText("no₳") - .getByText(`₳ ${lovelaceToAda(proposalToCheck.noVotes)}`) - ).toBeVisible(); + test("4G. Should display correct vote counts on governance details page for DRep", async ({ + page, + }) => { + const responsesPromise = Object.keys(FilterOption).map((filterKey) => + page.waitForResponse((response) => + response.url().includes(`&type[]=${FilterOption[filterKey]}`) + ) + ); + + const governanceActionsPage = new GovernanceActionsPage(page); + await governanceActionsPage.goto(); + + const responses = await Promise.all(responsesPromise); + const proposals: IProposal[] = ( + await Promise.all( + responses.map(async (response) => { + const data = await response.json(); + return data.elements; + }) + ) + ).flat(); + + expect(proposals.length, "No proposals found!").toBeGreaterThan(0); + + const proposalToCheck = proposals[0]; + const govActionDetailsPage = + await governanceActionsPage.viewProposal(proposalToCheck); + await govActionDetailsPage.showVotesBtn.click(); + + await expect( + page + .getByText("yes₳") + .getByText(`₳ ${lovelaceToAda(proposalToCheck.yesVotes)}`) + ).toBeVisible(); + await expect( + page + .getByText("abstain₳") + .getByText(`₳ ${lovelaceToAda(proposalToCheck.abstainVotes)}`) + ).toBeVisible(); + await expect( + page + .getByText("no₳") + .getByText(`₳ ${lovelaceToAda(proposalToCheck.noVotes)}`) + ).toBeVisible(); + }); }); -test("4F. Should Disable DRep functionality upon wallet disconnection on governance page", async ({ +test("4F. Should Disable DRep functionality upon wallet disconnection on governance actions page", async ({ page, + browser, }) => { - const governanceActionsPage = new GovernanceActionsPage(page); + const wallet = await walletManager.popWallet("registeredDRep"); + + const tempDRepAuth = await createTempDRepAuth(page, wallet); + + const dRepPage = await createNewPageWithWallet(browser, { + storageState: tempDRepAuth, + wallet, + }); + + const governanceActionsPage = new GovernanceActionsPage(dRepPage); await governanceActionsPage.goto(); - await page.getByTestId("disconnect-button").click(); + await dRepPage.getByTestId("disconnect-button").click(); + + await expect(dRepPage).toHaveURL("/governance_actions"); const govActionDetailsPage = await governanceActionsPage.viewFirstProposal(); await expect(govActionDetailsPage.voteBtn).not.toBeVisible(); diff --git a/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.loggedin.spec.ts index 4501dab6e..a2e109869 100644 --- a/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.loggedin.spec.ts +++ b/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.loggedin.spec.ts @@ -88,20 +88,17 @@ test("4C.2: Should sort Governance Action Type on governance actions page", asyn await govActionsPage.sortBtn.click(); - govActionsPage.sortProposal(SortOption.SoonToExpire); - await govActionsPage.validateSort( + await govActionsPage.sortAndValidate( SortOption.SoonToExpire, (p1, p2) => p1.expiryDate <= p2.expiryDate ); - govActionsPage.sortProposal(SortOption.NewestFirst); - await govActionsPage.validateSort( + await govActionsPage.sortAndValidate( SortOption.NewestFirst, (p1, p2) => p1.createdDate >= p2.createdDate ); - govActionsPage.sortProposal(SortOption.HighestYesVotes); - await govActionsPage.validateSort( + await govActionsPage.sortAndValidate( SortOption.HighestYesVotes, (p1, p2) => p1.yesVotes >= p2.yesVotes ); @@ -115,13 +112,11 @@ test("4D: Should filter and sort Governance Action Type on governance actions pa const govActionsPage = new GovernanceActionsPage(page); await govActionsPage.goto(); - await govActionsPage.sortBtn.click(); - await govActionsPage.sortProposal(SortOption.SoonToExpire); - await govActionsPage.filterBtn.click(); govActionsPage.filterProposalByNames([filterOptionNames[0]]); - await govActionsPage.validateSort( + await govActionsPage.sortBtn.click(); + await govActionsPage.sortAndValidate( SortOption.SoonToExpire, (p1, p2) => p1.expiryDate <= p2.expiryDate, [removeAllSpaces(filterOptionNames[0])] diff --git a/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.spec.ts b/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.spec.ts index cb5c0f2e1..56dd8e9ff 100644 --- a/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.spec.ts +++ b/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.spec.ts @@ -1,6 +1,8 @@ import { setAllureEpic } from "@helpers/allure"; +import { lovelaceToAda } from "@helpers/cardano"; import GovernanceActionsPage from "@pages/governanceActionsPage"; import { expect, test } from "@playwright/test"; +import { FilterOption, IProposal } from "@types"; test.beforeEach(async () => { await setAllureEpic("4. Proposal visibility"); @@ -24,3 +26,46 @@ test("4B.2: Should restrict voting for users who are not registered as DReps (wi const govActionDetailsPage = await govActionsPage.viewFirstProposal(); await expect(govActionDetailsPage.voteBtn).not.toBeVisible(); }); + +test("4K. Should display correct vote counts on governance details page for disconnect state", async ({ + page, +}) => { + const responsesPromise = Object.keys(FilterOption).map((filterKey) => + page.waitForResponse((response) => + response.url().includes(`&type[]=${FilterOption[filterKey]}`) + ) + ); + + const governanceActionsPage = new GovernanceActionsPage(page); + await governanceActionsPage.goto(); + const responses = await Promise.all(responsesPromise); + const proposals: IProposal[] = ( + await Promise.all( + responses.map(async (response) => { + const data = await response.json(); + return data.elements; + }) + ) + ).flat(); + + expect(proposals.length, "No proposals found!").toBeGreaterThan(0); + + const proposalToCheck = proposals[0]; + await governanceActionsPage.viewProposal(proposalToCheck); + + await expect( + page + .getByText("yes₳") + .getByText(`₳ ${lovelaceToAda(proposalToCheck.yesVotes)}`) + ).toBeVisible(); + await expect( + page + .getByText("abstain₳") + .getByText(`₳ ${lovelaceToAda(proposalToCheck.abstainVotes)}`) + ).toBeVisible(); + await expect( + page + .getByText("no₳") + .getByText(`₳ ${lovelaceToAda(proposalToCheck.noVotes)}`) + ).toBeVisible(); +}); diff --git a/tests/govtool-frontend/playwright/tests/5-proposal-functionality/proposalFunctionality.dRep.spec.ts b/tests/govtool-frontend/playwright/tests/5-proposal-functionality/proposalFunctionality.dRep.spec.ts index 9cd656642..262321a72 100644 --- a/tests/govtool-frontend/playwright/tests/5-proposal-functionality/proposalFunctionality.dRep.spec.ts +++ b/tests/govtool-frontend/playwright/tests/5-proposal-functionality/proposalFunctionality.dRep.spec.ts @@ -3,17 +3,13 @@ import { dRep01Wallet } from "@constants/staticWallets"; import { createTempDRepAuth } from "@datafactory/createAuth"; import { test } from "@fixtures/walletExtension"; import { setAllureEpic } from "@helpers/allure"; -import { ShelleyWallet } from "@helpers/crypto"; import { createNewPageWithWallet } from "@helpers/page"; -import { - registerDRepForWallet, - transferAdaForWallet, - waitForTxConfirmation, -} from "@helpers/transaction"; +import { waitForTxConfirmation } from "@helpers/transaction"; import GovernanceActionDetailsPage from "@pages/governanceActionDetailsPage"; import GovernanceActionsPage from "@pages/governanceActionsPage"; import { expect } from "@playwright/test"; import kuberService from "@services/kuberService"; +import walletManager from "lib/walletManager"; test.beforeEach(async () => { await setAllureEpic("5. Proposal functionality"); @@ -115,12 +111,8 @@ test.describe("Proposal checks", () => { test.describe("Perform voting", () => { let govActionDetailsPage: GovernanceActionDetailsPage; - test.beforeEach(async ({ page, browser }, testInfo) => { - test.setTimeout(testInfo.timeout + 2 * environments.txTimeOut); - - const wallet = await ShelleyWallet.generate(); - await registerDRepForWallet(wallet); - await transferAdaForWallet(wallet, 40); + test.beforeEach(async ({ page, browser }) => { + const wallet = await walletManager.popWallet("registeredDRep"); const tempDRepAuth = await createTempDRepAuth(page, wallet); @@ -188,11 +180,9 @@ test.describe("Check voting power", () => { page, browser, }, testInfo) => { - test.setTimeout(testInfo.timeout + 2 * environments.txTimeOut); + test.setTimeout(testInfo.timeout + environments.txTimeOut); - const wallet = await ShelleyWallet.generate(); - await registerDRepForWallet(wallet); - await transferAdaForWallet(wallet, 40); + const wallet = await walletManager.popWallet("registeredDRep"); const tempDRepAuth = await createTempDRepAuth(page, wallet); @@ -211,9 +201,7 @@ test.describe("Check voting power", () => { dRepPage.getByTestId("confirm-modal-button").click(); await waitForTxConfirmation(dRepPage); - const balance = await kuberService.getBalance( - wallet.addressBech32(environments.networkId) - ); + const balance = await kuberService.getBalance(wallet.address); expect(balance, "Retirement deposit not returned").toBeGreaterThan(500); }); }); diff --git a/tests/govtool-frontend/playwright/tests/6-miscellaneous/miscellaneous.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/6-miscellaneous/miscellaneous.loggedin.spec.ts deleted file mode 100644 index a1cf6ee2f..000000000 --- a/tests/govtool-frontend/playwright/tests/6-miscellaneous/miscellaneous.loggedin.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { user01Wallet } from "@constants/staticWallets"; -import { test } from "@fixtures/walletExtension"; -import DelegationPage from "@pages/dRepDirectoryPage"; -import { setAllureEpic } from "@helpers/allure"; -import DRepRegistrationPage from "@pages/dRepRegistrationPage"; -import { expect } from "@playwright/test"; - -test.use({ storageState: ".auth/user01.json", wallet: user01Wallet }); - -test.beforeEach(async () => { - await setAllureEpic("6. Miscellaneous"); -}); -// Skipped: No dRepId to validate -test("6B. Provides error for invalid format", async ({ page }) => { - test.skip(); - // invalid dRep delegation - const delegationPage = new DelegationPage(page); - await delegationPage.goto(); - await delegationPage.delegateToDRep("Random values"); - await expect(delegationPage.delegationErrorModal).toBeVisible(); - - // invalid dRep registration - const dRepRegistrationPage = new DRepRegistrationPage(page); - await dRepRegistrationPage.goto(); - - // await dRepRegistrationPage.urlInput.fill("abc"); - // await expect(dRepRegistrationPage.urlInputError).toBeVisible(); - - // await dRepRegistrationPage.hashInput.fill("abc"); - // await expect(dRepRegistrationPage.hashInputError).toBeVisible(); -}); - -test("6D: Proper label and recognition of the testnet network", async ({ - page, -}) => { - await page.goto("/"); - - await expect(page.getByText("testnet")).toBeVisible(); -}); diff --git a/tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmissionFunctionality.tx.spec.ts b/tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmissionFunctionality.tx.spec.ts deleted file mode 100644 index 0af28bc9a..000000000 --- a/tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmissionFunctionality.tx.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import environments from "@constants/environments"; -import { createTempUserAuth } from "@datafactory/createAuth"; -import { test } from "@fixtures/walletExtension"; -import { setAllureEpic } from "@helpers/allure"; -import { ShelleyWallet } from "@helpers/crypto"; -import { createNewPageWithWallet } from "@helpers/page"; -import ProposalSubmissionPage from "@pages/proposalSubmissionPage"; -import { expect } from "@playwright/test"; -import { IProposalForm, ProposalType } from "@types"; - -test.beforeEach(async ({ browser, page }, testInfo) => { - await setAllureEpic("7. Proposal submission"); -}); - -test.describe("Proposal submission check", () => { - Object.values(ProposalType).map((type: ProposalType, index) => { - test(`7G.${index + 1}: Should open wallet connection popup, when registered with proper ${type.toLowerCase()} data`, async ({ - page, - browser, - }, testInfo) => { - test.setTimeout(testInfo.timeout + environments.txTimeOut); - - const wallet = await ShelleyWallet.generate(); - const tempUserAuth = await createTempUserAuth(page, wallet); - const governancePage = await createNewPageWithWallet(browser, { - storageState: tempUserAuth, - wallet, - enableStakeSigning: true, - }); - - const proposalSubmissionPage = new ProposalSubmissionPage(governancePage); - - await proposalSubmissionPage.goto(); - - await governancePage.getByTestId(`${type}-radio`).click(); - await proposalSubmissionPage.continueBtn.click(); - - const proposal: IProposalForm = - proposalSubmissionPage.generateValidProposalFormFields( - type, - wallet.rewardAddressBech32(environments.networkId) - ); - await proposalSubmissionPage.register({ ...proposal }); - await expect( - proposalSubmissionPage.registrationErrorModal.getByText( - "UTxO Balance Insufficient" - ) - ).toBeVisible(); - }); - }); -}); diff --git a/tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmissionVisibility.spec.ts b/tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmissionVisibility.loggedin.spec.ts similarity index 100% rename from tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmissionVisibility.spec.ts rename to tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmissionVisibility.loggedin.spec.ts diff --git a/tests/govtool-frontend/playwright/tests/auth.setup.ts b/tests/govtool-frontend/playwright/tests/auth.setup.ts index 6df7aad0f..e62d7fa92 100644 --- a/tests/govtool-frontend/playwright/tests/auth.setup.ts +++ b/tests/govtool-frontend/playwright/tests/auth.setup.ts @@ -5,6 +5,7 @@ import { adaHolder02Wallet, adaHolder03Wallet, adaHolder04Wallet, + adaHolder05Wallet, dRep01Wallet, user01Wallet, } from "@constants/staticWallets"; @@ -18,6 +19,7 @@ const adaHolder01AuthFile = ".auth/adaHolder01.json"; const adaHolder02AuthFile = ".auth/adaHolder02.json"; const adaHolder03AuthFile = ".auth/adaHolder03.json"; const adaHolder04AuthFile = ".auth/adaHolder04.json"; +const adaHolder05AuthFile = ".auth/adaHolder05.json"; const user01AuthFile = ".auth/user01.json"; @@ -85,3 +87,13 @@ setup("Create AdaHolder 04 auth", async ({ page, context }) => { await context.storageState({ path: adaHolder04AuthFile }); }); + +setup("Create AdaHolder 05 auth", async ({ page, context }) => { + await importWallet(page, adaHolder05Wallet); + + const loginPage = new LoginPage(page); + await loginPage.login(); + await loginPage.isLoggedIn(); + + await context.storageState({ path: adaHolder05AuthFile }); +}); diff --git a/tests/govtool-frontend/playwright/tests/dRep.setup.ts b/tests/govtool-frontend/playwright/tests/dRep.setup.ts index f00ac7919..6c600d025 100644 --- a/tests/govtool-frontend/playwright/tests/dRep.setup.ts +++ b/tests/govtool-frontend/playwright/tests/dRep.setup.ts @@ -1,27 +1,30 @@ import environments from "@constants/environments"; import { dRepWallets } from "@constants/staticWallets"; +import { setAllureEpic, setAllureStory } from "@helpers/allure"; import { pollTransaction } from "@helpers/transaction"; import { expect, test as setup } from "@playwright/test"; import kuberService from "@services/kuberService"; -import fetch = require("node-fetch"); setup.describe.configure({ timeout: environments.txTimeOut }); -dRepWallets.forEach((wallet) => { - setup(`Register DRep of wallet: ${wallet.address}`, async () => { - try { - const res = await kuberService.dRepRegistration( - wallet.stake.private, - wallet.stake.pkh - ); +setup.beforeEach(async () => { + await setAllureEpic("Setup"); + await setAllureStory("Register DRep"); +}); + +setup("Register DRep of static wallets", async () => { + try { + const res = await kuberService.multipleDRepRegistration(dRepWallets); - await pollTransaction(res.txId, res.lockInfo); - } catch (err) { - if (err.status === 400) { - expect(true, "DRep already registered").toBeTruthy(); - } else { - throw err; - } + await pollTransaction(res.txId, res.lockInfo); + } catch (err) { + if ( + err.status === 400 && + err.message.includes("ConwayDRepAlreadyRegistered") + ) { + expect(true, "DRep already registered").toBeTruthy(); + } else { + throw err; } - }); + } }); diff --git a/tests/govtool-frontend/playwright/tests/faucet.setup.ts b/tests/govtool-frontend/playwright/tests/faucet.setup.ts deleted file mode 100644 index eca65a562..000000000 --- a/tests/govtool-frontend/playwright/tests/faucet.setup.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { faucetWallet } from "@constants/staticWallets"; -import { setAllureEpic, setAllureStory } from "@helpers/allure"; -import { pollTransaction } from "@helpers/transaction"; -import { test as setup } from "@playwright/test"; -import { loadAmountFromFaucet } from "@services/faucetService"; -import kuberService from "@services/kuberService"; -import environments from "lib/constants/environments"; - -setup.describe.configure({ mode: "serial", timeout: environments.txTimeOut }); - -setup.beforeEach(async () => { - await setAllureEpic("Setup"); - await setAllureStory("Fund"); -}); - -setup("Fund faucet wallet", async () => { - const balance = await kuberService.getBalance(faucetWallet.address); - if (balance > 10000) return; - - const res = await loadAmountFromFaucet(faucetWallet.address); - await pollTransaction(res.txid); -}); diff --git a/tests/govtool-frontend/playwright/tests/wallet.bootstrap.ts b/tests/govtool-frontend/playwright/tests/wallet.bootstrap.ts index 2bcc49c80..acd11a7d4 100644 --- a/tests/govtool-frontend/playwright/tests/wallet.bootstrap.ts +++ b/tests/govtool-frontend/playwright/tests/wallet.bootstrap.ts @@ -1,5 +1,5 @@ import { adaHolderWallets, dRepWallets } from "@constants/staticWallets"; -import { setAllureStory, setAllureEpic } from "@helpers/allure"; +import { setAllureEpic } from "@helpers/allure"; import { pollTransaction } from "@helpers/transaction"; import { expect, test as setup } from "@playwright/test"; import kuberService from "@services/kuberService"; @@ -11,30 +11,16 @@ setup.beforeEach(async () => { await setAllureEpic("Setup"); }); -setup("Fund static wallets", async () => { - await setAllureStory("Fund"); - const addresses = [...adaHolderWallets, ...dRepWallets].map((e) => e.address); - const res = await kuberService.transferADA(addresses); - await pollTransaction(res.txId); -}); - -for (const wallet of [...adaHolderWallets, ...dRepWallets]) { - setup(`Register stake of static wallet: ${wallet.address}`, async () => { - await setAllureStory("Register stake"); - try { - const { txId, lockInfo } = await kuberService.registerStake( - wallet.stake.private, - wallet.stake.pkh, - wallet.payment.private, - wallet.address - ); - await pollTransaction(txId, lockInfo); - } catch (err) { - if (err.status === 400) { - expect(true, "Stake already registered").toBeTruthy(); - } else { - throw Error(err); - } +setup("Initialize static wallets", async () => { + try { + const wallets = [...adaHolderWallets, ...dRepWallets]; + const res = await kuberService.initializeWallets(wallets); + await pollTransaction(res.txId); + } catch (err) { + if (err.status === 400 && err.message.includes("StakeKeyRegisteredDELEG")) { + expect(true, "Wallets already initialized").toBeTruthy(); + } else { + throw Error(err); } - }); -} + } +});