Skip to content

Commit

Permalink
chore: add E2E tests for duplicate registrations (#6552)
Browse files Browse the repository at this point in the history
* chore: add E2E tests for duplicate registrations

* PR review
  • Loading branch information
aberonni committed Feb 27, 2025
1 parent 76d9edf commit 2d02e23
Show file tree
Hide file tree
Showing 13 changed files with 493 additions and 9 deletions.
2 changes: 1 addition & 1 deletion e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ npm test -- --headed
Or, for [Portalicious](../interfaces/Portalicious/)-specific tests:

```shell
npm run test:portalicious
npm run test:portalicious
```

### Using the VS Code-extension
Expand Down
7 changes: 4 additions & 3 deletions e2e/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion e2e/portalicious/pages/BasePage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class BasePage {
readonly formError: Locator;
readonly toast: Locator;
readonly chooseFileButton: Locator;
readonly dialog: Locator;

constructor(page: Page) {
this.page = page;
Expand All @@ -32,6 +33,7 @@ class BasePage {
this.chooseFileButton = this.page.getByRole('button', {
name: 'Choose file',
});
this.dialog = this.page.getByRole('alertdialog');
}

async openSidebar() {
Expand All @@ -43,7 +45,9 @@ class BasePage {
await this.sidebar.getByRole('link', { name: pageName }).click();
}

async navigateToProgramPage(pageName: string) {
async navigateToProgramPage(
pageName: 'Registrations' | 'Payments' | 'Monitoring' | 'Team',
) {
await this.projectHeader.getByRole('tab', { name: pageName }).click();
}

Expand Down
7 changes: 3 additions & 4 deletions e2e/portalicious/pages/RegistrationActivityLogPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ import { expect } from '@playwright/test';
import { Page } from 'playwright';

import TableComponent from '@121-e2e/portalicious/components/TableComponent';
import BasePage from '@121-e2e/portalicious/pages/BasePage';

class RegistrationActivityLogPage extends BasePage {
readonly page: Page;
import RegistrationBasePage from './RegistrationBasePage';

class RegistrationActivityLogPage extends RegistrationBasePage {
readonly table: TableComponent;

constructor(page: Page) {
super(page);
this.page = page;
this.table = new TableComponent(page);
}

Expand Down
36 changes: 36 additions & 0 deletions e2e/portalicious/pages/RegistrationBasePage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Locator, Page } from 'playwright';
import { expect } from 'playwright/test';

import BasePage from './BasePage';

abstract class RegistrationBasePage extends BasePage {
readonly duplicateChip: Locator;
readonly duplicatesBanner: Locator;

constructor(page: Page) {
super(page);
this.duplicateChip = this.page.getByTestId('duplicate-chip');
this.duplicatesBanner = this.page.getByTestId('duplicates-banner');
}

async goToRegistrationPage(
page: 'Activity log' | 'Personal information' | 'Debit cards',
) {
await this.page.getByRole('tab', { name: page }).click();
}

async assertDuplicateWith({ duplicateName }: { duplicateName: string }) {
await this.assertDuplicateStatus({ status: 'Duplicate' });

await expect(this.duplicatesBanner).toBeVisible();
await expect(this.duplicatesBanner).toContainText('Duplicated with:');
await expect(this.duplicatesBanner).toContainText(duplicateName);
}

async assertDuplicateStatus({ status }: { status: string }) {
await expect(this.duplicateChip).toBeVisible();
await expect(this.duplicateChip).toContainText(status);
}
}

export default RegistrationBasePage;
40 changes: 40 additions & 0 deletions e2e/portalicious/pages/RegistrationPersonalInformationPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Locator, Page } from 'playwright';
import { expect } from 'playwright/test';

import RegistrationBasePage from './RegistrationBasePage';

class RegistrationPersonalInformationPage extends RegistrationBasePage {
readonly editInformationButton: Locator;
readonly editInformationReasonField: Locator;

constructor(page: Page) {
super(page);
this.editInformationButton = this.page.getByRole('button', {
name: 'Edit information',
});
this.editInformationReasonField = this.page.getByLabel(
'Write a reason for the update',
);
}

async editRegistration({
field,
value,
reason = 'E2E test',
}: {
field: string;
value: string;
reason?: string;
}) {
await this.editInformationButton.click();
await this.page.getByLabel(field).fill(value);
await this.page.getByRole('button', { name: 'Save' }).click();
await this.editInformationReasonField.fill(reason);
await this.dialog.getByRole('button', { name: 'Save' }).click();

// this re-appears after the save has been successful
await expect(this.editInformationButton).toBeVisible();
}
}

export default RegistrationPersonalInformationPage;
7 changes: 7 additions & 0 deletions e2e/portalicious/pages/RegistrationsPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import * as XLSX from 'xlsx';
import TableComponent from '@121-e2e/portalicious/components/TableComponent';
import BasePage from '@121-e2e/portalicious/pages/BasePage';

import { expectedSortedArraysToEqual } from '../utils';

const expectedColumnsSelectedRegistrationsExport = [
'referenceId',
'id',
Expand Down Expand Up @@ -505,6 +507,11 @@ class RegistrationsPage extends BasePage {
validateExactRowCount,
);
}

async assertDuplicateColumnValues(expectedValues: string[]) {
const duplicateColumnValues = await this.table.getTextArrayFromColumn(5);
expectedSortedArraysToEqual(duplicateColumnValues, expectedValues);
}
}

export default RegistrationsPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { test } from '@playwright/test';

import { SeedScript } from '@121-service/src/scripts/enum/seed-script.enum';
import { seedIncludedRegistrations } from '@121-service/test/helpers/registration.helper';
import {
getAccessToken,
resetDB,
} from '@121-service/test/helpers/utility.helper';
import { registrationsPV } from '@121-service/test/registrations/pagination/pagination-data';

import HomePage from '@121-e2e/portalicious/pages/HomePage';
import LoginPage from '@121-e2e/portalicious/pages/LoginPage';
import RegistrationsPage from '@121-e2e/portalicious/pages/RegistrationsPage';

test.beforeEach(async ({ page }) => {
await resetDB(SeedScript.nlrcMultiple);
const programIdPV = 2;

const accessToken = await getAccessToken();
await seedIncludedRegistrations(registrationsPV, programIdPV, accessToken);

// Login
const loginPage = new LoginPage(page);
await page.goto('/');
await loginPage.login(
process.env.USERCONFIG_121_SERVICE_EMAIL_ADMIN,
process.env.USERCONFIG_121_SERVICE_PASSWORD_ADMIN,
);
});

test('[33854] Validate that duplicate badges are present in the UI', async ({
page,
}) => {
const homePage = new HomePage(page);
const registrations = new RegistrationsPage(page);

const projectTitle = 'NLRC Direct Digital Aid Program (PV)';

await test.step('Select program', async () => {
await homePage.selectProgram(projectTitle);
});

await test.step('Wait for registrations to load', async () => {
const allRegistrationsCount = registrationsPV.length;
await registrations.waitForLoaded(allRegistrationsCount);
});

await test.step('Verify contents of duplicate column', async () => {
await registrations.assertDuplicateColumnValues([
'Unique',
'Duplicate',
'Duplicate',
'Unique',
]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { expect, test } from '@playwright/test';

import { SeedScript } from '@121-service/src/scripts/enum/seed-script.enum';
import { seedIncludedRegistrations } from '@121-service/test/helpers/registration.helper';
import {
getAccessToken,
resetDB,
} from '@121-service/test/helpers/utility.helper';
import { registrationsPV } from '@121-service/test/registrations/pagination/pagination-data';

import HomePage from '@121-e2e/portalicious/pages/HomePage';
import LoginPage from '@121-e2e/portalicious/pages/LoginPage';
import RegistrationActivityLogPage from '@121-e2e/portalicious/pages/RegistrationActivityLogPage';
import RegistrationsPage from '@121-e2e/portalicious/pages/RegistrationsPage';

test.beforeEach(async ({ page }) => {
const programIdPV = 2;
await resetDB(SeedScript.nlrcMultiple);

const accessToken = await getAccessToken();
await seedIncludedRegistrations(registrationsPV, programIdPV, accessToken);

// Login
const loginPage = new LoginPage(page);
await page.goto('/');
await loginPage.login(
process.env.USERCONFIG_121_SERVICE_EMAIL_ADMIN,
process.env.USERCONFIG_121_SERVICE_PASSWORD_ADMIN,
);
});

test('[33855] Validate that "Duplicate" banner is displayed in overview of duplicated registrations', async ({
page,
}) => {
const homePage = new HomePage(page);
const registrations = new RegistrationsPage(page);
const registrationActivityLogPage = new RegistrationActivityLogPage(page);

const projectTitle = 'NLRC Direct Digital Aid Program (PV)';

const duplicateRegistrationA = registrationsPV[1]; // 'Jan Janssen'
const duplicateRegistrationB = registrationsPV[2]; // 'Joost Herlembach'
const uniqueRegistration = registrationsPV[0]; // 'Gemma Houtenbos'

await test.step('Select program', async () => {
await homePage.selectProgram(projectTitle);
});

await test.step('Wait for registrations to load', async () => {
const allRegistrationsCount = registrationsPV.length;
await registrations.waitForLoaded(allRegistrationsCount);
});

await test.step('Open registration page', async () => {
await registrations.goToRegistrationByName({
registrationName: duplicateRegistrationA.fullName,
});
});

await test.step('View banner with duplicate', async () => {
await registrationActivityLogPage.assertDuplicateWith({
duplicateName: duplicateRegistrationB.fullName,
});
});

await test.step('Verify link to duplicate works', async () => {
const duplicateBLink =
await registrationActivityLogPage.duplicatesBanner.getByRole('link', {
name: duplicateRegistrationB.fullName,
});

await expect(duplicateBLink).toBeVisible();
await duplicateBLink.click();
});

await test.step('Verify new tab is opened and contains link to orignial duplicate', async () => {
await page.waitForTimeout(2000); //waitForNavigation and waitForLoadState do not work in this case

const pages = await page.context().pages();

await expect(pages).toHaveLength(2);

const registrationActivityLogPageForDuplicateB =
new RegistrationActivityLogPage(pages[1]);

await registrationActivityLogPageForDuplicateB.assertDuplicateWith({
duplicateName: duplicateRegistrationA.fullName,
});
});

await test.step('Navigate back to registrations table', async () => {
await page.bringToFront();
await page.goBack();
});

await test.step('Open registration page for unique registration', async () => {
await registrations.goToRegistrationByName({
registrationName: uniqueRegistration.fullName,
});
});

await test.step('Verify no banner is displayed for unique registration', async () => {
await expect(
registrationActivityLogPage.duplicatesBanner,
).not.toBeVisible();

await registrationActivityLogPage.assertDuplicateStatus({
status: 'Unique',
});
});
});
Loading

0 comments on commit 2d02e23

Please sign in to comment.