Skip to content

Commit

Permalink
feat: geo 870 e2e admin report search and locking end to end test (#801)
Browse files Browse the repository at this point in the history
  • Loading branch information
goemen authored Oct 8, 2024
1 parent 6998e7e commit 9a5760a
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 16 deletions.
17 changes: 16 additions & 1 deletion admin-frontend/e2e/pages/admin-portal-page.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Page, expect } from '@playwright/test';
import { User } from '../auth.setup';
import { PagePaths } from '../utils';
import { DateTimeFormatter, ZonedDateTime, ZoneId } from '@js-joda/core';
import { Locale } from '@js-joda/locale_en';

export class AdminPortalPage {
constructor(public readonly page: Page) {}
Expand All @@ -27,7 +29,7 @@ export class AdminPortalPage {
await expect(
this.page.getByRole('link', { name: 'Announcements' }),
).toBeVisible();
// TODO: bring it back after the
// TODO: bring it back after the
// await expect(
// this.page.getByRole('link', { name: 'User Management' }),
// ).toBeVisible();
Expand All @@ -53,4 +55,17 @@ export class AdminPortalPage {
await this.page.waitForTimeout(1000);
await this.page.waitForURL(PagePaths.LOGOUT);
}

formatDate(
inDateStr: string,
inFormatter = DateTimeFormatter.ISO_DATE_TIME,
outFormatter = DateTimeFormatter.ofPattern('MMM d, yyyy').withLocale(
Locale.CANADA,
),
) {
const date = ZonedDateTime.parse(inDateStr, inFormatter);
const localTz = ZoneId.systemDefault();
const dateInLocalTz = date.withZoneSameInstant(localTz);
return outFormatter.format(dateInLocalTz);
}
}
15 changes: 1 addition & 14 deletions admin-frontend/e2e/pages/announcements/announcements-page.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { expect, Locator, Page } from 'playwright/test';
import { PagePaths } from '../../utils';
import { AdminPortalPage } from '../admin-portal-page';
import { DateTimeFormatter, ZonedDateTime, ZoneId } from '@js-joda/core';
import { Locale } from '@js-joda/locale_en';
import { AnnouncementStatus } from '../../types';

export class AnnouncementsPage extends AdminPortalPage {
Expand Down Expand Up @@ -202,16 +200,5 @@ export class AnnouncementsPage extends AdminPortalPage {
return getAnnouncementResponse;
}

private formatDate(
inDateStr: string,
inFormatter = DateTimeFormatter.ISO_DATE_TIME,
outFormatter = DateTimeFormatter.ofPattern('MMM d, yyyy').withLocale(
Locale.CANADA,
),
) {
const date = ZonedDateTime.parse(inDateStr, inFormatter);
const localTz = ZoneId.systemDefault();
const dateInLocalTz = date.withZoneSameInstant(localTz);
return outFormatter.format(dateInLocalTz);
}

}
191 changes: 191 additions & 0 deletions admin-frontend/e2e/pages/reports/search-reports-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { expect, Locator, Page } from 'playwright/test';
import { AdminPortalPage } from '../admin-portal-page';
import { PagePaths } from '../../utils';
import { groupBy } from 'lodash';

export class SearchReportsPage extends AdminPortalPage {
static PATH = PagePaths.REPORTS;
searchInput: Locator;
searchButton: Locator;
filterButton: Locator;

static async visit(
page: Page,
): Promise<{ searchReportsPage: SearchReportsPage; reports: any[] }> {
const searchResponse = SearchReportsPage.waitForSearchResults(page);
await page.goto(SearchReportsPage.PATH);
const searchReportsPage = new SearchReportsPage(page);
await searchReportsPage.setup();
const response = await searchResponse;
const { reports } = await response.json();
return { searchReportsPage, reports };
}

async setup(): Promise<void> {
super.setup();
this.searchInput = await this.page.getByLabel('Search by company name');
this.searchButton = await this.page.getByRole('button', { name: 'Search' });
this.filterButton = await this.page.getByRole('button', {
name: 'Filter',
});
await this.expectElementToBeVisible(this.searchInput);
await this.expectElementToBeVisible(this.searchButton);
await this.expectElementToBeVisible(this.filterButton);
}

async searchReports(companyName: string) {
await this.searchInput.fill(companyName);
return this.clickSearchButton();
}

async filterReports(is_unlocked: boolean) {
await this.filterButton.click();
const reportYearInput = await this.page.getByLabel('Report Year');
const statusButton = await this.page.getByLabel('Locked/Unlocked');

await expect(reportYearInput).toBeVisible();
await expect(statusButton).toBeVisible();

await reportYearInput.click({ force: true, button: 'right' });
const currentYear = new Date().getFullYear();
await this.page.waitForTimeout(2000);
const option = await this.page.getByLabel(`Year: ${currentYear}`);
await option.click();
const lockStatus = is_unlocked ? 'Unlocked' : 'Locked';
await this.page.waitForTimeout(2000);
await statusButton.click({ force: true, button: 'right' });
const lockOption = await this.page.getByText(lockStatus, { exact: true });
await lockOption.click();

const filterResponse = SearchReportsPage.waitForSearchResults(this.page);
const applyButton = await this.page.getByRole('button', { name: 'Apply' });
await applyButton.click();
const response = await filterResponse;
const { reports } = await response.json();

return reports;
}

async clickSearchButton() {
const searchResponse = SearchReportsPage.waitForSearchResults(this.page);
await this.searchButton.click();
const response = await searchResponse;
const { reports } = await response.json();
return reports;
}

async searchAndVerifyReports(companyName: string, validatedLength = true) {
const reports = await this.searchReports(companyName);
if (!validatedLength) {
expect(reports.length).toBeGreaterThan(0);
} else {
expect(reports.length).toBe(1);
}
const report = reports[0];
const { company_name } = report.pay_transparency_company;
await this.expectElementToBeVisible(
await this.page.getByText(company_name).first(),
);
await this.expectElementToBeVisible(
await this.page.getByText(this.formatDate(report.create_date)).first(),
);
await this.expectElementToBeVisible(
await this.page.getByText(report.naics_code).first(),
);
const employeeCount = report.employee_count_range.employee_count_range;
await this.expectElementToBeVisible(
await this.page.getByText(employeeCount).first(),
);
await this.expectElementToBeVisible(
await this.page.getByText(report.reporting_year, { exact: true }).first(),
);

await this.expectElementToBeVisible(await this.getOpenReportButton());
await this.expectElementToBeVisible(await this.getLockReportButton());
await this.expectElementToBeVisible(await this.getReportHistoryButton());

return report;
}

async toggleReportLockAndverify(report) {
const lockButton = await this.getLockReportButton();
const lockResponse = this.waitForReportLock(report.report_id);
await lockButton.click();
const confirmButton = await this.page.getByRole('button', {
name: report.is_unlocked ? 'Yes, lock' : 'Yes, unlock',
});
await confirmButton.click();
const response = await lockResponse;
const patchedReport = await response.json();
expect(patchedReport.is_unlocked).toBe(!report.is_unlocked);
}

private async getOpenReportButton() {
const button = await this.page.getByRole('button', { name: 'Open report' });
await this.expectElementToBeVisible(button.first());
return button.first();
}

private async getLockReportButton() {
const button = await this.page.getByRole('button', { name: 'Lock report' });
await this.expectElementToBeVisible(button.first());
return button.first();
}

private async getReportHistoryButton() {
const button = await this.page.getByRole('button', {
name: 'Admin action history',
});
await this.expectElementToBeVisible(button.first());
return button.first();
}

static waitForSearchResults(page) {
return page.waitForResponse((res) => {
return (
res.url().includes('/admin-api/v1/reports') && res.status() === 200
);
});
}

private waitForReportLock(reportId) {
console.log(`/admin-api/v1/reports/${reportId}`);
return this.page.waitForResponse((res) => {
return (
res.url().includes(`/admin-api/v1/reports/${reportId}`) &&
res.status() === 200 &&
res.request().method() === 'PATCH'
);
});
}

private waitForHistory(reportId) {
return this.page.waitForResponse((res) => {
return (
res
.url()
.includes(`/admin-api/v1/reports/${reportId}/admin-action-history`) &&
res.status() === 200
);
});
}

private async expectElementToBeVisible(element: Locator) {
await expect(element).toBeVisible();
}

static getCompanyNameWithOneReport(reports, isUnlocked = true) {
const groups = groupBy(reports, 'pay_transparency_company.company_name');
console.log(
Object.keys(groups).find(
(key) =>
groups[key].length === 1 && groups[key][0].is_unlocked === isUnlocked,
),
);
const companyName = Object.keys(groups).find(
(key) =>
groups[key].length === 1 && groups[key][0].is_unlocked === isUnlocked,
);
return companyName;
}
}
33 changes: 33 additions & 0 deletions admin-frontend/e2e/reports.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { expect, test } from '@playwright/test';
import { SearchReportsPage } from './pages/reports/search-reports-page';

test.describe('Reports', () => {
test('search reports', async ({ page }) => {
const { searchReportsPage: reportsPage, reports } =
await SearchReportsPage.visit(page);
const title = reports[0].pay_transparency_company.company_name;
await reportsPage.searchAndVerifyReports(title, false);
});

test.describe.serial('lock and unlock report', async () => {
test('lock', async ({ page }) => {
const { searchReportsPage: reportsPage } =
await SearchReportsPage.visit(page);
const reports = await reportsPage.filterReports(true);
expect(reports.length).toBeGreaterThan(0);
const title = reports[0].pay_transparency_company.company_name;
const report = await reportsPage.searchAndVerifyReports(title);
await reportsPage.toggleReportLockAndverify(report);
});

test('unlock', async ({ page }) => {
const { searchReportsPage: reportsPage } =
await SearchReportsPage.visit(page);
const reports = await reportsPage.filterReports(false);
expect(reports.length).toBeGreaterThan(0);
const title = reports[0].pay_transparency_company.company_name;
const report = await reportsPage.searchAndVerifyReports(title);
await reportsPage.toggleReportLockAndverify(report);
});
});
});
10 changes: 9 additions & 1 deletion admin-frontend/src/components/ReportSearchFilters.vue
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,19 @@
<v-col sm="4" md="2" lg="2" xl="1" class="d-flex flex-column">
<h5>Year</h5>
<v-select
id="report-year"
v-model="selectedReportYear"
:items="reportYearOptions"
variant="solo"
density="compact"
aria-label="Report Year"
>
<template #item="{ props, item }">
<v-list-item v-bind="props" :title="item.raw ? item.raw : 'All'">
<v-list-item
:aria-label="'Year: ' + item.raw"
v-bind="props"
:title="item.raw ? item.raw : 'All'"
>
<template #append="{ isActive }">
<v-icon v-if="isActive" icon="mdi-check"></v-icon>
</template>
Expand All @@ -140,10 +146,12 @@
<v-col sm="4" md="3" lg="2" xl="1" class="d-flex flex-column">
<h5>Locked/Unlocked</h5>
<v-select
id="unlocked-status"
v-model="selectedLockedValues"
:items="lockedOptions"
variant="solo"
density="compact"
aria-label="Locked/Unlocked"
>
<template #item="{ props, item }">
<v-list-item v-bind="props" :title="item.raw ? item.raw : 'All'">
Expand Down

0 comments on commit 9a5760a

Please sign in to comment.