feature/IO-3587-Commision-Cut - Additional Testing / Test Harness improvements

This commit is contained in:
Dave
2026-03-18 16:50:57 -04:00
parent 782fa8a1c7
commit 3f03157834
13 changed files with 2227 additions and 45 deletions

View File

@@ -0,0 +1,140 @@
/* eslint-disable */
import { expect, test } from "@playwright/test";
import { acceptEulaIfPresent, login } from "./utils/login";
async function openCommissionCutHarness(page) {
await page.goto("/manage/_test?fixture=commission-cut");
await acceptEulaIfPresent(page);
await expect(page.getByRole("heading", { name: "Commission Cut Test Harness" })).toBeVisible();
}
test.describe("Commission-based cut", () => {
test.skip(!process.env.TEST_USERNAME || !process.env.TEST_PASSWORD, "Requires TEST_USERNAME and TEST_PASSWORD.");
test("renders payout previews and completes Pay All from the commission-cut harness", async ({ page }) => {
let calculateLaborCalls = 0;
let payAllCalls = 0;
await login(page, {
email: process.env.TEST_USERNAME,
password: process.env.TEST_PASSWORD
});
await page.route("**/payroll/calculatelabor", async (route) => {
calculateLaborCalls += 1;
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify([
{
employeeid: "emp-1",
mod_lbr_ty: "LAA",
expectedHours: 4,
claimedHours: 1
},
{
employeeid: "emp-2",
mod_lbr_ty: "LAB",
expectedHours: 2,
claimedHours: 1
}
])
});
});
await page.route("**/payroll/payall", async (route) => {
payAllCalls += 1;
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify([{ id: "tt-1" }])
});
});
await openCommissionCutHarness(page);
await expect(page.getByText("Claim Task Preview")).toBeVisible();
await expect(page.getByRole("cell", { name: "Commission" })).toBeVisible();
await expect(page.getByRole("cell", { name: "Hourly" })).toBeVisible();
await expect(
page.getByText(
"There are currently 1.25 hours of repair lines that are unassigned. These hours are not including in the above calculations and must be paid manually."
)
).toBeVisible();
await expect(page.getByRole("button", { name: "Pay All" })).toBeVisible();
await page.getByRole("button", { name: "Pay All" }).click();
await expect.poll(() => calculateLaborCalls).toBeGreaterThan(0);
await expect.poll(() => payAllCalls).toBe(1);
await expect(page.getByText("All hours paid out successfully.")).toBeVisible();
});
test("shows the backend error when Pay All is rejected", async ({ page }) => {
await login(page, {
email: process.env.TEST_USERNAME,
password: process.env.TEST_PASSWORD
});
await page.route("**/payroll/calculatelabor", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify([
{
employeeid: "emp-1",
mod_lbr_ty: "LAA",
expectedHours: 4,
claimedHours: 1
}
])
});
});
await page.route("**/payroll/payall", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: false,
error: "Not all hours have been assigned."
})
});
});
await openCommissionCutHarness(page);
await page.getByRole("button", { name: "Pay All" }).click();
await expect(page.getByText("Error flagging hours. Not all hours have been assigned.")).toBeVisible();
});
test("shows a negative labor difference when previously claimed hours exceed the current expected hours", async ({
page
}) => {
await login(page, {
email: process.env.TEST_USERNAME,
password: process.env.TEST_PASSWORD
});
await page.route("**/payroll/calculatelabor", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify([
{
employeeid: "emp-1",
mod_lbr_ty: "LAA",
expectedHours: 2,
claimedHours: 5
}
])
});
});
await openCommissionCutHarness(page);
await expect(page.locator("strong").filter({ hasText: "-3" }).first()).toBeVisible();
});
});

View File

@@ -1,5 +1,48 @@
import { expect } from "@playwright/test";
const formatToday = () => {
const today = new Date();
const month = String(today.getMonth() + 1).padStart(2, "0");
const day = String(today.getDate()).padStart(2, "0");
const year = today.getFullYear();
return `${month}/${day}/${year}`;
};
export async function acceptEulaIfPresent(page) {
const eulaDialog = page.getByRole("dialog", { name: "Terms and Conditions" });
const eulaVisible =
(await eulaDialog.isVisible().catch(() => false)) ||
(await eulaDialog
.waitFor({
state: "visible",
timeout: 5000
})
.then(() => true)
.catch(() => false));
if (!eulaVisible) {
return;
}
const markdownCard = page.locator(".eula-markdown-card");
await markdownCard.evaluate((element) => {
element.scrollTop = element.scrollHeight;
element.dispatchEvent(new Event("scroll", { bubbles: true }));
});
await page.getByRole("textbox", { name: "First Name" }).fill("Codex");
await page.getByRole("textbox", { name: "Last Name" }).fill("Tester");
await page.getByRole("textbox", { name: "Legal Business Name" }).fill("Codex QA");
await page.getByRole("textbox", { name: "Date Accepted" }).fill(formatToday());
await page.getByRole("checkbox", { name: "I accept the terms and conditions of this agreement." }).check();
const acceptButton = page.getByRole("button", { name: "Accept EULA" });
await expect(acceptButton).toBeEnabled({ timeout: 10000 });
await acceptButton.click();
await expect(eulaDialog).not.toBeVisible({ timeout: 10000 });
}
export async function login(page, { email, password }) {
// Navigate to the login page
await page.goto("/"); // Adjust if your login route differs (e.g., '/login')
@@ -16,6 +59,8 @@ export async function login(page, { email, password }) {
// Wait for navigation or success indicator (e.g., redirect to /manage/)
await page.waitForURL(/\/manage\//, { timeout: 10000 }); // Adjust based on redirect
await acceptEulaIfPresent(page);
// Verify successful login (e.g., check for a dashboard element)
await expect(page.locator("text=Manage")).toBeVisible(); // Adjust to your apps post-login UI
}

View File

@@ -1,5 +1,45 @@
import { afterEach } from "vitest";
import { afterEach, vi } from "vitest";
import { cleanup } from "@testing-library/react";
import "@testing-library/jest-dom";
if (!window.matchMedia) {
Object.defineProperty(window, "matchMedia", {
writable: true,
value: vi.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(),
removeListener: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn()
}))
});
}
if (!window.ResizeObserver) {
window.ResizeObserver = class ResizeObserver {
observe() {}
unobserve() {}
disconnect() {}
};
}
if (!window.IntersectionObserver) {
window.IntersectionObserver = class IntersectionObserver {
observe() {}
unobserve() {}
disconnect() {}
};
}
if (!window.scrollTo) {
window.scrollTo = vi.fn();
}
if (!HTMLElement.prototype.scrollIntoView) {
HTMLElement.prototype.scrollIntoView = vi.fn();
}
afterEach(() => cleanup());