From 89e128c1b9f8464a9b94bb3414696ddaf2d335bd Mon Sep 17 00:00:00 2001 From: Sital999 Date: Sun, 17 Nov 2024 16:27:26 +0545 Subject: [PATCH] Test: Implement 1-2A Update user profile A CO can update individual fields in the information held on a given user of the CVT --- integration_test/lib/fixtures/poll.ts | 2 +- .../lib/pages/representativesPage.ts | 66 +++++++---- integration_test/tests/0-common/comon.spec.ts | 49 ++++---- .../conventionOrganizer.spec.ts | 105 +++++++++++++----- .../coordinator/manageActivePowerTable.tsx | 2 + 5 files changed, 146 insertions(+), 78 deletions(-) diff --git a/integration_test/lib/fixtures/poll.ts b/integration_test/lib/fixtures/poll.ts index a54ded2..e73246a 100644 --- a/integration_test/lib/fixtures/poll.ts +++ b/integration_test/lib/fixtures/poll.ts @@ -62,7 +62,7 @@ export const test = base.extend({ await userPollPage.goto(pollId); // cast vote await userPage.getByTestId(votes[index]).click(); - await userPage.getByText('Vote recorded').isVisible() + await userPage.getByText('Vote recorded').isVisible(); await userPage.close(); } ) diff --git a/integration_test/lib/pages/representativesPage.ts b/integration_test/lib/pages/representativesPage.ts index 8635043..62fed7f 100644 --- a/integration_test/lib/pages/representativesPage.ts +++ b/integration_test/lib/pages/representativesPage.ts @@ -1,8 +1,5 @@ import { Page, expect } from '@playwright/test'; -import { faker } from '@faker-js/faker'; -const updateDelegateEmail = faker.person.fullName + '@email.com'; -const updatedAlternateEmail = faker.person.fullName + '@email.com'; const representativeUpdatedToast = 'User info updated!'; export default class RepresentativesPage { readonly updateDelegateBtn = this.page.getByTestId( @@ -30,26 +27,40 @@ export default class RepresentativesPage { await this.page.goto('/representatives/manage'); } - async updateDelegateProfile(): Promise { + async updateUserProfile( + name: string, + email: string, + stake_address: string + ): Promise { + // await this.goto(); + // await this.page + // .locator('[data-testid^="edit-representative-info-"]') + // .first() + // .click(); + // await this.page.getByRole('textbox').nth(0).fill(name); + // await this.page.getByRole('textbox').nth(1).fill(email); + // await this.page + // .locator('[data-testid^="save-representative-info-"]') + // .first() + // .click(); await this.goto(); await this.updateDelegateBtn.click(); - await this.page.getByRole('textbox').nth(1).fill(updateDelegateEmail); + await this.page.getByRole('textbox').nth(0).fill(name); + await this.page.getByRole('textbox').nth(1).fill(email); + await this.page.getByRole('textbox').nth(2).fill(stake_address); await this.saveDelegateInfoBtn.click({ force: true }); } - async isRepresentativeUpdated(): Promise { + async isRepresentativeUpdated(infos: Array): Promise { await expect(this.page.getByText(representativeUpdatedToast)).toBeVisible(); - } - - async updateAlternateProfile(): Promise { - await this.goto(); - await this.updateAlternateBtn.click(); - await this.page.getByRole('textbox').nth(1).fill(updatedAlternateEmail); - await this.saveAlternateBtn.click({ force: true }); + await Promise.all( + infos.map( + async (info) => await expect(this.page.getByText(info)).toBeVisible() + ) + ); } async switchVotingPower(): Promise { - await this.goto(); await this.transferVotingPowerBtn.click(); const currentActiveVoter = await this.page .getByRole('combobox') @@ -70,18 +81,25 @@ export default class RepresentativesPage { .locator('[data-id="1"]') .locator('[data-field="active_voter_id"]') .innerText(); - const changedRow = await this.page + await this.page .locator('[data-id="1"]') .filter({ has: this.page.getByTestId('edit-active-voter-1') }) - .allInnerTexts(); - const changedRowData = changedRow[0].split('\n\n'); - const activeVoter = - activeVoterRole === 'Delegate' ? changedRowData[1] : changedRowData[2]; - await this.page.goto('/'); - const activeVoterNamme = await this.page + .locator(`[data-testid^="${activeVoterRole.toLowerCase()}-name-"]`) + .click({ force: true }); + await expect(this.page.locator('.MuiChip-root').first()).toHaveText( + 'Active Voter' + ); + } + + async getRepresentativeId(isDelegate: boolean = false): Promise { + await expect(this.page.getByRole('row').first()).toBeVisible(); + const representativeTestId = await this.page .locator('[data-id="1"]') - .locator('[data-field="active_voter"]') - .innerText(); - expect(activeVoterNamme).toBe(activeVoter); + .filter({ has: this.page.getByTestId('edit-active-voter-1') }) + .locator( + `[data-testid^="${isDelegate ? 'delegate' : 'alternate'}-name-"]` + ) + .getAttribute('data-testid'); + return representativeTestId.split('-').pop(); } } diff --git a/integration_test/tests/0-common/comon.spec.ts b/integration_test/tests/0-common/comon.spec.ts index 5ebc345..667bb9f 100644 --- a/integration_test/tests/0-common/comon.spec.ts +++ b/integration_test/tests/0-common/comon.spec.ts @@ -58,7 +58,7 @@ test.describe('Polls', () => { test.describe('Polls', () => { test.use({ pollType: 'VotedPoll', - }); + }); /** * Description: After a poll is closed the results of the poll should be displayed* @@ -98,13 +98,12 @@ test.describe('Polls', () => { await expect(noCount).toHaveText('1'); await expect(abstainCount).toHaveText('1'); }); - }); test.describe('User profile', () => { test.use({ pollType: 'VotedPoll', - }); + }); /** * Description: By going to the profile page of a delegate or alternate I can review their voting record @@ -118,25 +117,28 @@ test.describe('User profile', () => { test('0-2A-1. Given Delegate or alternate profile page, can view voting hsitory', async ({ page, pollId, - browser + browser, }) => { - await page.goto('/polls/'+pollId); - + await page.goto('/polls/' + pollId); - const buttons = await page.locator('[data-testid^="representative-vote-"]').all(); + const buttons = await page + .locator('[data-testid^="representative-vote-"]') + .all(); if (buttons.length === 0) { throw new Error('No representative vote buttons found'); } - const pages:Page[]=[]; + const pages: Page[] = []; for (const button of buttons) { const testId = await button.getAttribute('data-testid'); console.log(`Opening new tab for button with test id: ${testId}`); - + // Get the href attribute or construct the URL for navigation const href = await button.getAttribute('href'); if (!href) { - throw new Error(`Button with test id: ${testId} does not have an href attribute`); + throw new Error( + `Button with test id: ${testId} does not have an href attribute` + ); } // Open a new tab @@ -146,14 +148,14 @@ test.describe('User profile', () => { pages.push(newPage); } - await Promise.all(pages.map(async (voterPage)=>{ - const votingTable= voterPage.getByTestId('voting-history-table'); - await votingTable.getByTestId('user-votes-'+pollId).isVisible(); - })); - + await Promise.all( + pages.map(async (voterPage) => { + const votingTable = voterPage.getByTestId('voting-history-table'); + await votingTable.getByTestId('user-votes-' + pollId).isVisible(); + }) + ); }); - /** * Description: By going to the profile page of a delegate or alternate I can review their voting record * @@ -167,28 +169,27 @@ test.describe('User profile', () => { test('0-2A-1. Can navigate to user profile from delegate/alternate listing page', async ({ page, }) => { - throw new Error('Not Implemented'); }); - test('0-2A-1. Can navigate to user profile from voter view in poll results page', async ({ page, pollId, }) => { await page.goto('polls/' + pollId); - + // Locate the buttons - const buttons = await page.locator('[data-testid^="representative-vote-"]').all(); - + const buttons = await page + .locator('[data-testid^="representative-vote-"]') + .all(); + if (buttons.length === 0) { throw new Error('No representative vote buttons found'); } - + // Click the first button await buttons[0].click(); - - await page.waitForURL(/\/representatives\/\d+$/); + await page.waitForURL(/\/representatives\/\d+$/); }); }); diff --git a/integration_test/tests/1-convention-organizers/conventionOrganizer.spec.ts b/integration_test/tests/1-convention-organizers/conventionOrganizer.spec.ts index 0e06c58..75a794c 100644 --- a/integration_test/tests/1-convention-organizers/conventionOrganizer.spec.ts +++ b/integration_test/tests/1-convention-organizers/conventionOrganizer.spec.ts @@ -31,6 +31,7 @@ test.describe('Delete Poll', async () => { await homePage.deleteOpenPollCards(); pollId = await homePage.createPoll(); }); + test('1-1F-1. Given connected as CO, can delete a pending poll', async ({ page, }) => { @@ -204,6 +205,8 @@ test.describe('Create Poll', () => { }); test.describe('User Control', () => { + test.use({ pollType: 'CreatePoll' }); + /** * Description: A CO can update individual fields in the information held on a given user of the CVT * @@ -214,29 +217,21 @@ test.describe('User Control', () => { */ test('1-2A. Given connected as CO can update all fields of user', async ({ page, - browser, - }) => { - throw new Error('Not Implemented'); - }); - - /** - * Description: If a delegate is unable to vote then their alternate needs to be given voting rights, and if they become able to vote again, then the voting rights need to be able to be returned. - * - * User Story: As a CO, I want to be able to choose either the delegate or the alternate from any given workshop to have the right to vote on behalf of the workshop participants so that every workshop has the opportunity to cast exactly one vote in each poll - * - * Acceptance Criteria: Given that I am a CO on the page listing all the delegates and alternates, when I toggle one of them to be the voter from a given workshop that one can vote, the other one from the workshop is not able to vote. - */ - test('1-2B-1. Given connected as CO can switch delegate user to alternate or vice-versa', async ({ - page, + pollId, }) => { - const representativePage = new RepresentativesPage(page); - await representativePage.switchVotingPower(); - await expect(page.getByText('Active voter updated!')).toBeVisible(); - // TODO: go to listing page and confirm that the role is change. - // again switch and test. + const name = faker.person.fullName(); + const email = name.split(' ')[0] + '@email.com'; + const stake_address = faker.person.jobArea(); + const represntativePage = new RepresentativesPage(page); + await represntativePage.updateUserProfile(name, email, stake_address); + await represntativePage.isRepresentativeUpdated([ + name, + email, + stake_address, + ]); }); - test('1-2B-2. Should have corresponding workspace delegate and alternate in a same row', async ({ + test('1-2B. Should have corresponding workspace delegate and alternate in a same row', async ({ page, }) => { await page.goto('/'); @@ -271,26 +266,78 @@ test.describe('User Control', () => { ) ); }); - // As a convention organiser, I want to be able to update the profile information of a delegate (or alternate) to correct any error or omission. - test('1-2D. Convention organisers can update delegate profile information ', async ({ + + test('1-2D. Should not be able to switch active voting power between delegate and alternate during voting', async ({ page, + pollId, }) => { - const represntativePage = new RepresentativesPage(page); - await represntativePage.updateDelegateProfile(); - await represntativePage.isRepresentativeUpdated(); - await represntativePage.updateAlternateProfile(); - await represntativePage.isRepresentativeUpdated(); + test.slow(); + const pollPage = new PollPage(page); + await pollPage.goto(pollId); + await pollPage.beginVoteBtn.click(); + + // before switching power while poll is opened for voting + const representativePage = new RepresentativesPage(page); + await representativePage.goto(); + await expect(page.getByRole('row').first()).toBeVisible(); + const previousActiveVoterId = await page + .locator('[data-id="1"]') + .locator('[data-field="active_voter_cell"]') + .getAttribute('data-testid'); + + await representativePage.switchVotingPower(); + await page.waitForTimeout(500); + + // // after trying to switch power while poll is opened for voting + await expect(page.getByRole('status')).toHaveText( + 'You cannot change the active voter while a Poll is actively voting.' + ); + const currentActiveVoterId = await page + .locator('[data-id="1"]') + .locator('[data-field="active_voter_cell"]') + .getAttribute('data-testid'); + + expect(previousActiveVoterId).toBe(currentActiveVoterId); }); }); +/** + * Description: If a delegate is unable to vote then their alternate needs to be given voting rights, and if they become able to vote again, then the voting rights need to be able to be returned. + * + * User Story: As a CO, I want to be able to choose either the delegate or the alternate from any given workshop to have the right to vote on behalf of the workshop participants so that every workshop has the opportunity to cast exactly one vote in each poll + * + * Acceptance Criteria: Given that I am a CO on the page listing all the delegates and alternates, when I toggle one of them to be the voter from a given workshop that one can vote, the other one from the workshop is not able to vote. + */ + test.describe('Voting Power', () => { + test.beforeEach(async ({ page }) => { + const homePage = new HomePage(page); + await homePage.goto(); + await homePage.deleteOpenPollCards(); + }); test('1-3A. Should be able to switch active voting power between delegate and alternate.', async ({ page, }) => { const representativePage = new RepresentativesPage(page); + await representativePage.goto(); + const previousActiveVoterId = await page + .locator('[data-id="1"]') + .locator('[data-field="active_voter_cell"]') + .getAttribute('data-testid'); await representativePage.switchVotingPower(); + await expect(page.getByText('Active voter updated!')).toBeVisible(); - await representativePage.assertSwitchedVotingPower(); - await representativePage.switchVotingPower(); + const representativesIds = await Promise.all([ + representativePage.getRepresentativeId(true), + representativePage.getRepresentativeId(), + ]); + const currentActiveVoterId = await page + .locator('[data-id="1"]') + .locator('[data-field="active_voter_cell"]') + .getAttribute('data-testid'); + + expect(representativesIds).toContain(previousActiveVoterId); + expect(representativesIds).toContain(currentActiveVoterId); + expect(currentActiveVoterId).not.toBe(previousActiveVoterId); }); }); diff --git a/src/components/coordinator/manageActivePowerTable.tsx b/src/components/coordinator/manageActivePowerTable.tsx index f49d6b6..2a88e55 100644 --- a/src/components/coordinator/manageActivePowerTable.tsx +++ b/src/components/coordinator/manageActivePowerTable.tsx @@ -212,6 +212,8 @@ export function ManageActivePowerTable(): JSX.Element { flexDirection="row" alignItems="center" height="100%" + data-testid={`${activeVoterId}`} + data-field="active_voter_cell" > {activeVoterId == delegateId