Skip to content

Commit

Permalink
Merge branch 'main' into feature/accessible-graphs
Browse files Browse the repository at this point in the history
  • Loading branch information
jer3k authored Apr 17, 2024
2 parents bf111f0 + 0e1ed0a commit 9a97563
Show file tree
Hide file tree
Showing 31 changed files with 647 additions and 220 deletions.
1 change: 1 addition & 0 deletions .github/workflows/cd-to-test-on-workflow-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jobs:
secrets: inherit
with:
backend-external-url: https://pay-transparency-test-backend-external.apps.silver.devops.gov.bc.ca/api
environment: test
zap:
name: ZAP Scan
needs: [deploys]
Expand Down
72 changes: 61 additions & 11 deletions frontend/e2e/pages/dashboard.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { expect } from '@playwright/test';
import { Locator, Page, expect } from '@playwright/test';
import { PTPage, User } from './page';
import { PagePaths } from '../utils';
import {
IEmployeeCountRange,
INaicsCode,
IReportDetails,
} from './generate-report';
import { waitForCodes } from '../utils/report';

export class DashboardPage extends PTPage {
static path = PagePaths.DASHBOARD;
Expand All @@ -9,17 +15,21 @@ export class DashboardPage extends PTPage {
async setup() {
await super.setup();
this.generateReportButton = await this.instance.getByRole('link', {
name: /Generate Pay Transparency Report/i,
name: /Upload your CSV here/i,
});
}

async gotoGenerateReport() {
expect(this.generateReportButton).toBeVisible();
await this.generateReportButton.click();
await this.instance.waitForURL(PagePaths.GENERATE_REPORT);
await expect(
this.instance.getByText('Disclaimer: This tool relies on the employer supplying accurate and complete payroll data in order to calculate pay gaps.'),
).toBeVisible();
await waitForCodes(this.instance, async () => {
expect(this.generateReportButton).toBeVisible();
await this.generateReportButton.click();
await this.instance.waitForURL(PagePaths.GENERATE_REPORT);
await expect(
this.instance.getByText(
'Disclaimer: This tool relies on the employer supplying accurate and complete payroll data in order to calculate pay gaps.',
),
).toBeVisible();
});
}

async gotoReport(id: string) {
Expand All @@ -38,13 +48,46 @@ export class DashboardPage extends PTPage {
await expect(viewReportButton).toBeVisible();
}

async gotoEditReport(id: string) {
async canEditReport(id: string) {
const editReportButton = await this.instance.getByTestId(
`edit-report-${id}`,
);
expect(editReportButton).toBeVisible();
await editReportButton.click();
await expect(editReportButton).toBeVisible();

return editReportButton;
}

async gotoEditReport(reportId: string, button: Locator) {
const getReportDetailsRequest = this.instance.waitForResponse(
(res) =>
res.url().includes(`/api/v1/report/${reportId}`) &&
res.status() === 200,
);
const getEmployeeCountRangesRequest = this.instance.waitForResponse(
(res) =>
res.url().includes('/api/v1/codes/employee-count-ranges') &&
res.status() === 200,
);
const getNaicsCodesRequest = this.instance.waitForResponse(
(res) =>
res.url().includes('/api/v1/codes/naics-codes') && res.status() === 200,
);

expect(button).toBeVisible();
await button.click();
await this.instance.waitForURL(PagePaths.GENERATE_REPORT);
const getReportDetailsResponse = await getReportDetailsRequest;
const reportDetails: IReportDetails = await getReportDetailsResponse.json();
const getEmployeeCountRangesResponse = await getEmployeeCountRangesRequest;
const employeeCountRanges: IEmployeeCountRange[] =
await getEmployeeCountRangesResponse.json();
const getNaicsCodesResponse = await getNaicsCodesRequest;
const naicsCodes: INaicsCode[] = await getNaicsCodesResponse.json();

PTPage.employeeCountRanges = employeeCountRanges;
PTPage.naicsCodes = naicsCodes;

return { reportDetails, naicsCodes, employeeCountRanges };
}

async verifyUser(user: User): Promise<void> {
Expand All @@ -54,4 +97,11 @@ export class DashboardPage extends PTPage {
await expect(welcome).toBeVisible();
await super.verifyUser(user);
}

static async visit(page: Page): Promise<DashboardPage> {
await page.goto(PagePaths.DASHBOARD);
const dashboard = new DashboardPage(page);
await dashboard.setup();
return dashboard;
}
}
191 changes: 179 additions & 12 deletions frontend/e2e/pages/generate-report.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,36 @@
import { Locator, expect } from '@playwright/test';
import { Locator, Response, expect } from '@playwright/test';
import { PTPage, User } from './page';
import path from 'path';
import flatten from 'lodash/flatten';
import { LocalDate } from '@js-joda/core';
import { validateSubmitErrors, waitForApiResponses } from '../utils/report';
import { PagePaths } from '../utils';

export interface IReportDetails {
report_id: string;
user_comment: string;
employee_count_range_id: string;
naics_code: string;
report_start_date: string;
report_end_date: string;
reporting_year: string;
report_status: string;
revision: string;
data_constraints: string;
is_unlocked: boolean;
create_date: string;
company_id: string;
}

export interface INaicsCode {
naics_code: string;
naics_label: string;
}

export interface IEmployeeCountRange {
employee_count_range_id: string;
employee_count_range: string;
}

interface IFormValues {
naicsCode: string;
Expand All @@ -20,6 +49,14 @@ interface IUploadFileErrors {
export class GenerateReportPage extends PTPage {
public naicsInput: Locator;
public employeeCountInput: Locator;
public reportingYearInput: Locator;
public startMonthInput: Locator;
public startYearInput: Locator;
public endMonthInput: Locator;
public endYearInput: Locator;
public commentsInput: Locator;
public dataConstraintsInput: Locator;
public submitButton: Locator;

async setup() {
await super.setup();
Expand All @@ -28,11 +65,23 @@ export class GenerateReportPage extends PTPage {
this.employeeCountInput = await this.instance.locator(
'#employeeCountRange',
);
this.reportingYearInput = await this.instance.locator('#reportYear');
this.startMonthInput = await this.instance.locator('#startMonth');
this.startYearInput = await this.instance.locator('#startYear');
this.endMonthInput = await this.instance.locator('#endMonth');
this.endYearInput = await this.instance.locator('#endYear');

this.commentsInput = await this.instance.locator('#comments');
this.dataConstraintsInput = await this.instance.locator('#dataConstraints');
this.submitButton = await this.instance.getByRole('button', {
name: 'Submit',
});
}

async setNaicsCode(label: string) {
await this.naicsInput.click();
const code = await this.instance.getByRole('option', { name: label });
await this.instance.waitForTimeout(1000);
const code = await this.instance.getByRole('option', {name: label});
expect(code).toBeVisible();
await code.click();
}
Expand Down Expand Up @@ -85,22 +134,140 @@ export class GenerateReportPage extends PTPage {

async fillOutForm(values: IFormValues) {
// 2: Fill out the form in the generate report form page
await this.setup();
await this.setNaicsCode(values.naicsCode);
await this.setEmployeeCount(values.employeeCountRange);

const comments = await this.instance.locator('#comments');
await comments.fill(values.comments);
const dataConstraints = await this.instance.locator('#dataConstraints');
await dataConstraints.fill(values.dataConstraints);
await this.commentsInput.fill(values.comments);
await this.dataConstraintsInput.fill(values.dataConstraints);

await this.selectFile(values.fileName);
await this.instance.waitForSelector('i.fa-xmark')
await this.instance.waitForSelector('i.fa-xmark');
}

async checkDefaultFormValues(report: IReportDetails) {
const naicsCode = PTPage.naicsCodes.find(
(nc) => nc.naics_code === report.naics_code,
);
const employeeCountRange = PTPage.employeeCountRanges.find(
(nc) => nc.employee_count_range_id === report.employee_count_range_id,
);
await expect(
await this.instance.getByText(
`${naicsCode?.naics_code} - ${naicsCode?.naics_label}`,
),
).toBeVisible();

const radioButton = await this.instance.getByLabel(
employeeCountRange!.employee_count_range,
);
await expect(await radioButton.isChecked()).toBeTruthy();

await expect(this.reportingYearInput).toBeVisible();
await expect(this.reportingYearInput).toHaveValue(report.reporting_year);
await expect(this.reportingYearInput).toBeDisabled();

await this.checkDate(
report.report_start_date,
this.startMonthInput,
this.startYearInput,
);
await this.checkDate(
report.report_end_date,
this.endMonthInput,
this.endYearInput,
);
if (report.user_comment) {
await expect(this.commentsInput).toHaveValue(report.user_comment);
} else {
await expect(this.commentsInput).toBeEmpty();
}

if (report.data_constraints) {
await expect(this.dataConstraintsInput).toHaveValue(
report.data_constraints,
);
} else {
await expect(this.dataConstraintsInput).toBeEmpty();
}
}

async checkDate(value: string, monthLocator: Locator, yearLocator: Locator) {
const date = LocalDate.parse(value);
await expect(monthLocator).toBeVisible();
await expect(monthLocator).toHaveValue(`${date.monthValue()}`);
await expect(yearLocator).toBeVisible();
await expect(yearLocator).toHaveValue(`${date.year()}`);
}

async submitForm(responseChecker?: (res: Response) => boolean) {
await this.submitButton.scrollIntoViewIfNeeded();
if (responseChecker) {
const { report } = await waitForApiResponses(
{
report: this.instance.waitForResponse(responseChecker),
},
async () => {
await this.submitButton.click();
},
);
return report;
} else {
await this.submitButton.click();
}
}

async submitInvalidFormAndValidateErrors() {
await this.submitForm();
// Check form errors
await this.checkErrors();
// Check API errors
await validateSubmitErrors(this);
}

async submitValidFormAndGotoDraftPage() {
await this.naicsInput.scrollIntoViewIfNeeded();
await this.fillOutForm({
naicsCode: '11 - Agriculture, forestry, fishing and hunting',
employeeCountRange: '50-299',
comments: 'Example test comment',
dataConstraints: 'Example data constraint text',
fileName: 'CsvGood.csv',
});

const validUploadResponse = await this.submitForm(
(res) =>
res.url().includes('/api/v1/file-upload') && res.status() === 200,
);

await this.instance.waitForURL(PagePaths.DRAFT_REPORT);

return validUploadResponse;
}

async submitForm() {
const button = await this.instance.getByRole('button', { name: 'Submit' });
await button.scrollIntoViewIfNeeded();
await button.click();
async editReportAndSubmit(reportDetails: IReportDetails) {
// edit form and submit form
const naicsCode = PTPage.naicsCodes.find(
(n) => n.naics_code !== reportDetails.naics_code,
);
await this.naicsInput.click();
await this.setNaicsCode(
`${naicsCode!.naics_code} - ${naicsCode!.naics_label}`,
);
const employeeCountRange = PTPage.employeeCountRanges.find(
(n) =>
n.employee_count_range_id !== reportDetails.employee_count_range_id,
);

await this.setEmployeeCount(employeeCountRange!.employee_count_range);
const comment = 'new comment edit';
await this.commentsInput.fill(comment);
const dataConstraint = 'new data constraint edit';
await this.dataConstraintsInput.fill(dataConstraint);

await this.selectFile('CsvGood.csv');
return this.submitForm(
(res) =>
res.url().includes('/api/v1/file-upload') && res.status() === 200,
);
}
}
8 changes: 7 additions & 1 deletion frontend/e2e/pages/page.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Locator, Page, expect } from '@playwright/test';
import { PagePaths } from '../utils';
import { IEmployeeCountRange, INaicsCode } from './generate-report';

export type User = {
displayName: string;
Expand All @@ -14,12 +15,17 @@ export type User = {

export class PTPage {
public accountButton: Locator;
constructor(public readonly instance: Page) {}
public static naicsCodes: INaicsCode[] = [];
public static employeeCountRanges: IEmployeeCountRange[] = [];
constructor(public readonly instance: Page, public user = undefined) {}

async setup() {
this.accountButton = await this.instance.getByTestId(
'header-account-button',
);
if (this.user) {
await this.verifyUser(this.user);
}
}

async verifyUser(user: User) {
Expand Down
Loading

0 comments on commit 9a97563

Please sign in to comment.