Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: duplicates in portalicious #6522

Merged
merged 3 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading