From afd8bf854a679c4579171e5b1c9257cf8574065d Mon Sep 17 00:00:00 2001 From: Sudip Bhattarai Date: Tue, 3 Dec 2024 00:19:12 +0545 Subject: [PATCH] Implement full audit of on-chain tx --- .../lib/constants/environments.ts | 3 +- integration_test/lib/fixtures/importWallet.ts | 15 +- integration_test/lib/helpers/blockfrost.ts | 130 +++++++++++++ integration_test/lib/helpers/poll.ts | 68 +++++++ integration_test/lib/helpers/txUtil.ts | 20 +- integration_test/lib/pages/loginPage.ts | 6 +- integration_test/playwright.config.ts | 8 + .../conventionOrganizer.spec.ts | 50 +---- .../tests/6-on-chain-tests.spec.ts | 175 ++++++++++++++++++ integration_test/tests/voted-poll-setup.ts | 33 +--- 10 files changed, 423 insertions(+), 85 deletions(-) create mode 100644 integration_test/lib/helpers/blockfrost.ts create mode 100644 integration_test/lib/helpers/poll.ts create mode 100644 integration_test/tests/6-on-chain-tests.spec.ts diff --git a/integration_test/lib/constants/environments.ts b/integration_test/lib/constants/environments.ts index 6e47e6f..c98622a 100644 --- a/integration_test/lib/constants/environments.ts +++ b/integration_test/lib/constants/environments.ts @@ -1,13 +1,14 @@ import { config } from 'dotenv'; config(); const environments = { - txTimeOut: 3 * 40 * 1000, + txTimeOut: 4 * 40 * 1000, baseUrl: process.env.HOST_URL || 'http://localhost:3000', kuber: { apiUrl: process.env.KUBER_API_URL || 'https://preview.kuber.cardanoapi.io', apiKey: process.env.KUBER_API_KEY || '', }, + blockfrostApiKey: process.env.BLOCKFROST_API_KEY, networkId: process.env.NETWORK_ID || '0', ci: process.env.CI, }; diff --git a/integration_test/lib/fixtures/importWallet.ts b/integration_test/lib/fixtures/importWallet.ts index f218060..73d40d5 100644 --- a/integration_test/lib/fixtures/importWallet.ts +++ b/integration_test/lib/fixtures/importWallet.ts @@ -1,6 +1,8 @@ import { CardanoTestWalletJson } from '@cardanoapi/cardano-test-wallet/types'; -import { Page } from '@playwright/test'; +import { Browser, BrowserContext, Page } from '@playwright/test'; import { StaticWallet } from '@types'; +import loadEternlExtension from './loadExtension'; +import LoginPage from '@pages/loginPage'; export async function injectWalletExtension( page: Page, @@ -22,4 +24,15 @@ export async function injectWalletExtension( }, wallet); } + +export async function pageWithInjectedWallet(context:BrowserContext|Browser, wallet:StaticWallet): Promise { + const page = await context.newPage(); + await loadEternlExtension(page); + + await injectWalletExtension(page, wallet); + + const loginPage = new LoginPage(page); + await loginPage.login(); + return page; +} export const importWallet = injectWalletExtension \ No newline at end of file diff --git a/integration_test/lib/helpers/blockfrost.ts b/integration_test/lib/helpers/blockfrost.ts new file mode 100644 index 0000000..d9dade4 --- /dev/null +++ b/integration_test/lib/helpers/blockfrost.ts @@ -0,0 +1,130 @@ +import environments from "@constants/environments"; + +//@ts-ignore +const blockfrostApiUrl = 'https://cardano-'+(environments.networkId == 0? 'preview':'mainnet')+'.blockfrost.io/api/v0/txs/'; +const blockfrostApiKey = environments.blockfrostApiKey; + +async function fetchVoteMetadata (txHash:string): Promise { + const url = `${blockfrostApiUrl}${txHash}/metadata` + console.log("Fetching:",url) + const response = await fetch(url, { + method: 'GET', + headers: { + 'project_id': blockfrostApiKey, + }, + }); + const txData: any[] = await response.json(); + const label27=txData.filter(x=>x.label==='27')[0].json_metadata + return label27; +}; + +export async function fetchPollSummaryMetadata(txHash: string):Promise{ + return await fetchVoteMetadata(txHash) +} +interface OnChainVoteData { + [delegate: string]: string[]; +} + +export type Vote = VotePayload & { + voterName: string + cosesign1: string + cosekey: string +} + +export interface VotePayload{ + pollName: string, + constutionHash: string, + constutionLink: string, + voterStake: string, + vote: 'yes'| 'no' | 'abstain' + challenge: string +} +export async function fetchVoteTxMetadata(txHash: string):Promise{ + const voteData: OnChainVoteData[] = await fetchVoteMetadata(txHash) + return voteData.map(v=>{ + const voterName=Object.keys(v) + const voteData =v[voterName[0]] + const voteStr=voteData.join('') + const result =extractVoteData(voteStr) + result.voterName=voterName[0] + return result + + }) +} + +function extractVoteData(data:string): Vote { + // Regular Expressions for matching different parts + const pollNameRegex = /Poll: ([^,]+)/; + const constitutionHashRegex = /Hashed Constitution Text: ([^,]+)/; + const constitutionLinkRegex = /Link to Constitution Text: ([^,]+)/; + const voteRegex = /Vote: (\w+)/; + const walletRegex = /Wallet: ([^,]+)/; + const cosign1Regex = /Signature: ([^,]+)/; + const cosekeyRegex = /Public Key: ([^,]+)/; + const challengeRegex = /Challenge: ([^,]+)/; + // Match each one + const pollNameMatch = data.match(pollNameRegex); + const constitutionHashMatch = data.match(constitutionHashRegex); + const constitutionLinkMatch = data.match(constitutionLinkRegex); + const voteMatch = data.match(voteRegex); + const walletMatch = data.match(walletRegex); + const cosign1Match = data.match(cosign1Regex); + const cosekeyMatch = data.match(cosekeyRegex); + const challengeMatch = data.match(challengeRegex) + + const validateVote = (vote:string): 'yes'|'no'|'abstain' => { + const validVotes = ['yes', 'no', 'abstain']; + return validVotes.includes(vote) ? vote as any : 'abstain'; + }; + + return { + pollName: pollNameMatch ? pollNameMatch[1] : '', + voterName: walletMatch ? walletMatch[1] : '', + constutionHash: constitutionHashMatch ? constitutionHashMatch[1] : '', + constutionLink: constitutionLinkMatch ? constitutionLinkMatch[1] : '', + voterStake: walletMatch ? walletMatch[1] : '', + challenge: challengeMatch? challengeMatch[1]: '', + vote: voteMatch ? validateVote(voteMatch[1]) : 'abstain', + cosesign1: cosign1Match ? cosign1Match[1] : '', + cosekey: cosekeyMatch ? cosekeyMatch[1] : '' + }; +} + + ///Wallet: stake_test1uqp28kgg6cafhzuf74eyh8trpa4fh5w50776wk5xh5npv8gx0zh92, + //Poll: Rustic Plastic Chips, Hashed Constitution Text: 16885f03dd0368dbcaaf80ea0ebc5c2e81b46dd082ec628662d69968af096f8d, + // Link to Constitution Text: https://proud-publication.info/, Vote: no, + // Timestamp: 12/2/2024, 11:14:32 PM, Challenge: fa2c6340ddfbb809735bd51217f0cbcbd3bd2b487654549f054e74ac5e8c9d87 + export function decodeOnChainPayload(data: string): VotePayload { + // Regular Expressions for matching different parts of the data + const pollNameRegex = /Poll: ([^,]+)/; + const constitutionHashRegex = /Hashed Constitution Text: ([^,]+)/; + const constitutionLinkRegex = /Link to Constitution Text: ([^,]+)/; + const voteRegex = /Vote: (\w+)/; + const walletRegex = /Wallet: ([^,]+)/; + const challengeRegex = /Challenge: ([^,]+)/; + + // Match each part of the data + const pollNameMatch = data.match(pollNameRegex); + const constitutionHashMatch = data.match(constitutionHashRegex); + const constitutionLinkMatch = data.match(constitutionLinkRegex); + const voteMatch = data.match(voteRegex); + const walletMatch = data.match(walletRegex); + const challengeMatch = data.match(challengeRegex); + + // Validate the vote (same as the one in the extractVoteData function) + const validateVote = (vote: string): 'yes' | 'no' | 'abstain' => { + const validVotes = ['yes', 'no', 'abstain']; + return validVotes.includes(vote) ? vote as any : 'abstain'; + }; + + // Return the parsed data in a structured format + return { + pollName: pollNameMatch ? pollNameMatch[1] : '', + constutionHash: constitutionHashMatch ? constitutionHashMatch[1] : '', + constutionLink: constitutionLinkMatch ? constitutionLinkMatch[1] : '', + voterStake: walletMatch ? walletMatch[1] : '', // In this case, it seems to be the wallet address again + challenge: challengeMatch ? challengeMatch[1] : '', + vote: voteMatch ? validateVote(voteMatch[1]) : 'abstain', + }; + } + diff --git a/integration_test/lib/helpers/poll.ts b/integration_test/lib/helpers/poll.ts new file mode 100644 index 0000000..8c55868 --- /dev/null +++ b/integration_test/lib/helpers/poll.ts @@ -0,0 +1,68 @@ +import { alternateWallets, delegateWallets, organizerWallets } from "@constants/staticWallets"; +import { pageWithInjectedWallet } from "@fixtures/importWallet"; +import HomePage from "@pages/homePage"; +import { StaticWallet } from "@types"; +import { nAtaTime, sleep } from "./txUtil"; +import PollPage from "@pages/pollPage"; +import Logger from "./logger"; +import { Browser, expect, Page } from "@playwright/test"; +export type VotedPoll = { + pollId: number, + votes: Record +} +export async function createFullyVotedPoll (browser: Browser,voteCount?:number) : Promise { + const getPage=async (w:StaticWallet)=>await pageWithInjectedWallet(browser,w) + + + const organizerPages=await Promise.all([organizerWallets[0]].map(getPage)); + + const organizerHomePage = new HomePage(organizerPages[0]); + const deleted = await organizerHomePage.deleteOpenPollCards(); + if(deleted){ + await organizerHomePage.goto() + } + const pollId = await organizerHomePage.createPoll() + await organizerHomePage.beginVoteBtn.click(); + + const voteButtons = [ + 'vote-yes-button', + 'vote-no-button', + 'vote-abstain-button', + ]; + const votes=['yes','no','abstain'] + const castedVotes={} + + + await nAtaTime(voteCount? delegateWallets.slice(0,voteCount):delegateWallets,async (wallet,index)=>{ + const context = await browser.newContext() + + let page = await pageWithInjectedWallet(context,wallet) + + let pollPage=new PollPage(page) + await pollPage.goto(pollId) + + const isActive = await pollPage.voteYesBtn.waitFor({state: "visible",timeout: 30000}).then(()=>true).catch(()=>false) + + if(!isActive){ + Logger.info("User is not active voter: "+wallet.stakeAddress) + await page.close() + wallet=alternateWallets[index] + page = await pageWithInjectedWallet(context,alternateWallets[index]) + pollPage=new PollPage(page) + await pollPage.goto(pollId) + } + + const randomVote = Math.floor(Math.random() * 3); + await page.getByTestId(voteButtons[randomVote]).click(); + castedVotes[wallet.stakeAddress] = votes[randomVote]; + await expect(page.getByText('Vote recorded!'),"Expected Vote to be recorded for user: "+wallet.stakeAddress).toBeVisible({timeout:20000}) + await page.close() + await context.close() + },6); + + const organizerPollPage=new PollPage(organizerPages[0]) + await organizerPollPage.endVoting() + await sleep(2000) + return {pollId,votes:castedVotes,organizerPollPage:organizerPages[0]} + +} \ No newline at end of file diff --git a/integration_test/lib/helpers/txUtil.ts b/integration_test/lib/helpers/txUtil.ts index 9fd4fcc..8e3c0d9 100644 --- a/integration_test/lib/helpers/txUtil.ts +++ b/integration_test/lib/helpers/txUtil.ts @@ -78,4 +78,22 @@ export async function waitForTxSubmit( export function sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); -} \ No newline at end of file +} + +export async function nAtaTime(array: T[], f: (item: T,index?:number) => Promise,n:number =10): Promise { + const chunkSize = n; + let results: U[] = []; + + // Process the array in chunks of `n` + for (let i = 0; i < array.length; i += chunkSize) { + const chunk = array.slice(i, i + chunkSize); + + // Wait for all async operations on this chunk to finish and collect the results + const chunkResults = await Promise.all(chunk.map((item,index) => f(item,index))); + + // Push the results of the current chunk to the results array + results.push(...chunkResults); + } + + return results; +} diff --git a/integration_test/lib/pages/loginPage.ts b/integration_test/lib/pages/loginPage.ts index 22b2b2e..91f5c50 100644 --- a/integration_test/lib/pages/loginPage.ts +++ b/integration_test/lib/pages/loginPage.ts @@ -16,7 +16,8 @@ export default class LoginPage { await this.connectWalletBtn.first().click(); await this.eternlWalletBtn.click({ force: true }); - await expect(this.page.getByTestId('connected-user-name')).toBeVisible({timeout: 30000}) + // this is taking absurdly long time when testing with 10 parallel users + await expect(this.page.getByTestId('connected-user-name')).toBeVisible({timeout: 40000}) } async logout(): Promise { @@ -25,7 +26,6 @@ export default class LoginPage { } async isLoggedIn(): Promise { - await this.connectWalletBtn.first().click(); - await expect(this.disconnectWalletBtn).toBeVisible(); + await expect(this.page.getByTestId('connected-user-name')).toBeVisible({timeout: 40000}) } } diff --git a/integration_test/playwright.config.ts b/integration_test/playwright.config.ts index 8f1409b..06ec281 100644 --- a/integration_test/playwright.config.ts +++ b/integration_test/playwright.config.ts @@ -76,6 +76,14 @@ export default defineConfig({ testMatch: '**/*independent.spec.ts', use: { ...devices['Galaxy Tab S4'] }, }, + + { + name: 'on-chain', + testMatch: 'y-on-chain-tests.spec.ts', + use: { ...devices['Desktop Chrome'] }, + dependencies: environments.ci ? ['auth setup'] : [], + }, + /* Test against mobile viewports. */ // { diff --git a/integration_test/tests/1-convention-organizers/conventionOrganizer.spec.ts b/integration_test/tests/1-convention-organizers/conventionOrganizer.spec.ts index 9287be4..a08c6f1 100644 --- a/integration_test/tests/1-convention-organizers/conventionOrganizer.spec.ts +++ b/integration_test/tests/1-convention-organizers/conventionOrganizer.spec.ts @@ -12,7 +12,7 @@ import { newDelegate2Page, newDelegatePage, } from '@helpers/page'; -import { pollTransaction, sleep, waitForTxSubmit } from '@helpers/txUtil'; + test.beforeEach(async () => { await setAllureEpic('1. Convention Organizers'); @@ -306,54 +306,6 @@ test.describe('Create Poll', () => { }); }); -test.describe('Onchain Poll', () => { - test.use({ - pollType: 'VotedPoll', - }); // - /** - * Description: The transaction that contains the aggregated results on-chain must also contain all of the transaction IDs of the vote transactions. - - User story: As an Observer I want to have access to all the vote transaction IDs in one transaction, so that I only need to be given the reference to one transaction ID to adit the vote on-chain. - - Acceptance Criteria: Given that I am an observer, when I look up the transaction ID of the results summary transaction on-chain, then I will see all the transaction IDs of the votes for this poll. - */ 2; - - test('1-1H . Given CO, can submit poll results onchain', async ({ - page, - pollId, - }) => { - test.slow() - const pollPage = new PollPage(page); - pollPage.goto(pollId); - const waiter = waitForTxSubmit(page) - await pollPage.uploadVoteOnchainBtn.waitFor({ state: 'visible' }); - await sleep(1000) - - // Click the button and start transaction submission - await pollPage.uploadVoteOnchainBtn.click(); - console.log("Upload votes button clicked!!") - const votesTxId = await waiter - const summaryTxWaiter = waitForTxSubmit(page) - await pollTransaction(votesTxId) - - - const summaryTxId = await summaryTxWaiter - await pollTransaction(summaryTxId); - - // click on viewVote Onchain Button and check the url in new tab - - const newPagePopup = page.waitForEvent('popup') - await pollPage.viewTxOnchainBtn.waitFor({ state: 'visible' }); - await pollPage.viewTxOnchainBtn.click() - const newPage = await newPagePopup - - await newPage.waitForLoadState('domcontentloaded'); - const expectedUrl = `/transaction/${summaryTxId}`; - expect(newPage.url()).toContain(expectedUrl); - - }); -}); - test.describe('User Control', () => { test.use({ pollType: 'CreatePoll' }); diff --git a/integration_test/tests/6-on-chain-tests.spec.ts b/integration_test/tests/6-on-chain-tests.spec.ts new file mode 100644 index 0000000..99e8b9e --- /dev/null +++ b/integration_test/tests/6-on-chain-tests.spec.ts @@ -0,0 +1,175 @@ +import environments from "@constants/environments"; +import { createAuth } from "@helpers/auth"; + +import { fetchVoteTxMetadata,fetchPollSummaryMetadata, Vote, decodeOnChainPayload } from "@helpers/blockfrost"; +import { newOrganizer1Page } from "@helpers/page"; +import { createFullyVotedPoll, VotedPoll } from "@helpers/poll"; +import { pollTransaction, sleep, waitForTxSubmit } from "@helpers/txUtil"; +import HomePage from "@pages/homePage"; +import LoginPage from "@pages/loginPage"; +import PollPage from "@pages/pollPage"; +import { expect, Page, test } from "@playwright/test"; +import { bech32, blake, cborBackend, CoseSign1, Ed25519Key, loadCrypto } from "libcardano"; + +test.describe('Onchain Poll', () => { + + /** + * Description: The transaction that contains the aggregated results on-chain must also contain all of the transaction IDs of the vote transactions. + + User story: As an Observer I want to have access to all the vote transaction IDs in one transaction, so that I only need to be given the reference to one transaction ID to adit the vote on-chain. + + Acceptance Criteria: Given that I am an observer, when I look up the transaction ID of the results summary transaction on-chain, then I will see all the transaction IDs of the votes for this poll. + */ 2; + let pollDetail: VotedPoll| undefined = undefined + let organizerPollPage:Page|undefined = undefined + test('6-1A . Given CO, can submit poll results onchain', async ({ + browser + }) => { + const voteCount=62 + const totalTxCount=Math.ceil(voteCount/9) +1 + test.setTimeout(900000)// set it to 15 minutes. + const fullyVotedPoll = await createFullyVotedPoll(browser,voteCount) + pollDetail=fullyVotedPoll; + organizerPollPage=fullyVotedPoll.organizerPollPage; + const page = fullyVotedPoll.organizerPollPage; + const pollPage = new PollPage(page); + + + + let submitWaiter= waitForTxSubmit(page) + // await pollPage.uploadVoteOnchainBtn.waitFor({ state: 'visible' }); + // Click the button and start transaction submission + await pollPage.uploadVoteOnchainBtn.click({force: true,timeout: 30000}); + + console.log("Upload votes button clicked!!") + let txs:string[]=[] + let currentTxId: string|undefined + let i = totalTxCount; + + do{ + currentTxId=await submitWaiter + txs.push(currentTxId) + if(i>1){ + submitWaiter=waitForTxSubmit(page) // start submit waiter in background. + } + await pollTransaction(currentTxId) + }while(--i >0) + await page.reload({waitUntil: 'networkidle'}) + + const summaryTxLink = page.locator('[data-testid="summary-tx-link"]'); + await expect(summaryTxLink).toBeVisible() + const voteTxLink = page.locator('[data-testid^="vote-record-tx-link-1"]'); + await expect(voteTxLink).toBeVisible() + + + // check for summary link + + let newPagePopup=page.waitForEvent('popup') + await summaryTxLink.click() + let newPage=await newPagePopup + + await newPage.waitForLoadState('domcontentloaded'); + let expectedUrl = `/transaction/${txs[txs.length-1]}`; + expect(newPage.url()).toContain(expectedUrl); + + + // check for votes link + newPagePopup=page.waitForEvent('popup') + await voteTxLink.click() + newPage=await newPagePopup + + await newPage.waitForLoadState('domcontentloaded'); + expectedUrl = `/transaction/${txs[0]}`; + expect(newPage.url()).toContain(expectedUrl); + + }); + let onChainVotes:Vote[]=[] + test('6-1B. Given votes are uploaded on-chain, all votes are present.', async ({ + browser, + }) => { + if(!pollDetail && environments.ci){ + test.skip() + return; + } + const page = organizerPollPage || await newOrganizer1Page(browser) + const pollPage=new PollPage(page); + await pollPage.goto(pollDetail?.pollId || 2629); + const summaryTxLink = page.locator('[data-testid="summary-tx-link"]'); + await expect(summaryTxLink).toBeVisible() + const summaryTxHref = await summaryTxLink.getAttribute('href'); + const extractedSummaryTxHash = summaryTxHref.split('/').pop().split('?')[0]; // Get the hash part from the URL + + + // Extract vote transaction hashes + await expect(page.getByTestId('vote-record-tx-link-1')).toBeVisible() + const voteTxLinks = page.locator('[data-testid^="vote-record-tx-link-"]'); + const voteTxHashes = []; + + // Loop through each vote transaction link to extract the hashes + for (let i = 0; i < await voteTxLinks.count(); i++) { + const voteTxLink = voteTxLinks.nth(i); + const voteTxHref = await voteTxLink.getAttribute('href'); + const extractedVoteTxHash = voteTxHref.split('/').pop().split('?')[0]; // Get the hash part from the URL + voteTxHashes.push(extractedVoteTxHash); + } + + // Validate metadata for summary transaction + const summaryMetadata:string[] = await fetchPollSummaryMetadata(extractedSummaryTxHash); + const voteTxSet=new Set(summaryMetadata) + expect(summaryMetadata.length == voteTxHashes.length,`OnChainVote Txs=${summaryMetadata.length} pageVoteTxs=${voteTxHashes.length}: Expected to be equal`).toBeTruthy(); // Ensure metadata exists + + // Validate metadata for each vote transaction + for (let txHash of voteTxHashes) { + expect(voteTxSet.has(txHash),"txHash: "+txHash+" present on-chain but not present on pollPage").toBeTruthy() + const voteMetadata = await fetchVoteTxMetadata(txHash); + onChainVotes.push(...voteMetadata) + } + + // same no of casted and on-chain votes. + expect(Object.keys(pollDetail.votes).length).toBe(onChainVotes.length) + + // Assert the count of on-chain and page vote shown + const onChainYes=onChainVotes.filter(v=>v.vote == 'yes').length + const onChainNo=onChainVotes.filter(v=>v.vote == 'no').length + const onChainAbstain=onChainVotes.filter(v=>v.vote == 'abstain').length + expect(await page.getByTestId('yes-count').innerHTML()).toBe(onChainYes.toString()) + expect(await page.getByTestId('no-count').innerHTML()).toBe(onChainNo.toString()) + expect(await page.getByTestId('abstain-count').innerHTML()).toBe(onChainAbstain.toString()) + + }); + + test('6-1C. Given votes are uploaded on-chain, vote data is correct', async () => { + if(!onChainVotes){ + test.skip() + } + await loadCrypto() + + for (let vote of onChainVotes ){ + const cosign1=CoseSign1.fromBytes(Buffer.from(vote.cosesign1,'hex')) + const signedMessage=cosign1.payload.toString() + + const signedVote =decodeOnChainPayload(signedMessage) + + // check that the fields shown and data actually signed are same. + expect(vote.challenge).toBe(signedVote.challenge) + expect(vote.vote).toBe(signedVote.vote) + expect(vote.constutionHash).toBe(signedVote.constutionHash) + expect(vote.constutionLink).toBe(signedVote.constutionLink) + expect(vote.voterStake).toBe(signedVote.voterStake) + expect(vote.pollName).toBe(signedVote.pollName) + + // check that the data in metadata and the actual vote casted is same. + expect(vote.vote).toBe(pollDetail.votes[vote.voterStake]) + + // Cryptographic verification of votes. + const userStakeAddress=bech32.decode(vote.voterStake).data + const userStakeKeyHash=userStakeAddress.subarray(1) + const signerPubKey : Buffer= cborBackend.decode(Buffer.from(vote.cosekey,'hex')).get(-2) + const signerPubKeyHash = blake.hash28(signerPubKey) + const isSignatureValid = await cosign1.verify(await Ed25519Key.fromPublicKey(signerPubKey)) + + expect(isSignatureValid).toBeTruthy() + expect(Buffer.compare(signerPubKeyHash,userStakeKeyHash),"Expected siged pub key to be of voter").toBe(0) + } + }); +}); \ No newline at end of file diff --git a/integration_test/tests/voted-poll-setup.ts b/integration_test/tests/voted-poll-setup.ts index 0d364d2..597eaef 100644 --- a/integration_test/tests/voted-poll-setup.ts +++ b/integration_test/tests/voted-poll-setup.ts @@ -3,10 +3,11 @@ import { delegateWallets, organizerWallets, } from '@constants/staticWallets'; -import { importWallet, injectWalletExtension } from '@fixtures/importWallet'; +import { importWallet, injectWalletExtension, pageWithInjectedWallet } from '@fixtures/importWallet'; import loadEternlExtension from '@fixtures/loadExtension'; import { setAllureEpic, setAllureStory } from '@helpers/allure'; import Logger from '@helpers/logger'; +import { nAtaTime } from '@helpers/txUtil'; import HomePage from '@pages/homePage'; import LoginPage from '@pages/loginPage'; import PollPage from '@pages/pollPage'; @@ -24,16 +25,7 @@ function sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } -async function pageWithInjectedWallet(browser:Browser, wallet:StaticWallet): Promise { - const page = await browser.newPage(); - await loadEternlExtension(page); - await injectWalletExtension(page, wallet); - - const loginPage = new LoginPage(page); - await loginPage.login(); - return page; -} test(`Create Voted Poll`, async ({browser }) => { test.setTimeout(300000) // let's set it to 5 minute const getPage=async (w:StaticWallet)=>await pageWithInjectedWallet(browser,w) @@ -75,26 +67,7 @@ test(`Create Voted Poll`, async ({browser }) => { await page.getByTestId(votes[randomVote]).click(); await expect(page.getByText('Vote recorded!'),"Expected Vote to be recorded for user: "+wallet.stakeAddress).toBeVisible({timeout:10000}) await page.close() - }); + },10); const organizerPollPage=new PollPage(organizerPages[0]) await organizerPollPage.endVoting() }); - -async function nAtaTime(array: T[], f: (item: T,index?:number) => Promise,n:number =10): Promise { - const chunkSize = n; - let results: U[] = []; - - // Process the array in chunks of `n` - for (let i = 0; i < array.length; i += chunkSize) { - const chunk = array.slice(i, i + chunkSize); - - // Wait for all async operations on this chunk to finish and collect the results - const chunkResults = await Promise.all(chunk.map((item,index) => f(item,index))); - - // Push the results of the current chunk to the results array - results.push(...chunkResults); - } - - return results; - } - \ No newline at end of file