mirror of
https://github.com/VladSez/easy-invoice-pdf
synced 2026-04-21 13:37:40 +00:00
* feat: Add language attribute to date input fields in invoice form components - Include `lang="en"` attribute in date input fields of `InvoiceForm` and `GeneralInformation` components for improved accessibility and localization support. * fix: Update language attribute for date input fields in invoice form components * refactor: Improve layout and organization of invoice components - Remove unnecessary margin from the main container in the Home component. - Wrap the share invoice button and PDF download link in a fragment for better structure. - Adjust margins for the ProjectInfo and action button container for improved spacing. - Update the InvoicePDFViewer height to use full height for better responsiveness. - Remove the deprecated RegenerateInvoiceButton component to streamline the codebase. - Update the InvoiceClientPage to accept handleShareInvoice prop for better functionality. - Clean up unused language attributes in date input fields across invoice form components. * feat: Integrate Playwright for end-to-end testing and enhance invoice form components - Add Playwright configuration and dependencies for E2E testing. - Create GitHub Actions workflow for automated E2E tests on deployment. - Implement initial E2E tests for the Invoice Generator Page, verifying UI elements and form functionality. - Refactor invoice form components to include data-testid attributes for better testability. - Update .gitignore to exclude Playwright-related files and directories. * chore: Update GitHub Actions workflow for E2E testing and enhance test coverage - Upgrade pnpm version from 8 to 10 in the E2E workflow for improved package management. - Add new test case to verify header buttons and links on the Invoice Generator Page, ensuring UI elements are displayed correctly and have the expected attributes. * chore: Enhance ESLint configuration for Playwright integration - Add Playwright ESLint plugin to package.json for improved E2E testing support. - Update .eslintrc.json to include overrides for E2E test files. - Clean up GitHub Actions workflow by removing unnecessary pnpm version specification. * chore: Update Playwright configuration and improve test assertions - Increase timeout for expect assertions and test execution from 15 seconds to 30 seconds for better stability in E2E tests. - Comment out mobile viewport tests to streamline configuration and focus on desktop testing. * chore: Update configuration and refactor invoice form components - Add compiler options to remove console logs in production and enhance logging for fetch requests in next.config.mjs. - Update package.json to include new type definitions for ua-parser-js and add ua-parser-js as a dependency. - Refactor invoice form components to remove form prefix IDs, simplifying data-testid attributes for better testability. - Introduce DeviceContext for managing device type state and improve responsiveness in invoice form components. - Implement server-side device detection using user agent parsing for better rendering on mobile and desktop views. - Update media query hooks to streamline device type checks across components. * chore: Update Playwright configuration and enhance invoice form tests - Reduce timeout for expect assertions from 30 seconds to 15 seconds for improved test performance. - Add new test for handling currency switching in the Invoice Generator Page, verifying correct currency display and calculations. - Refactor buyer and seller information components to include tooltip messages and improve accessibility with aria attributes. - Update BuyerDialog and BuyerManagement components to enhance user experience with better visibility and edit functionality for buyer details. * chore: Update Playwright installation command in GitHub Actions workflow - Modify Playwright installation command to remove explicit browser specification, allowing for default browser installation with dependencies. * chore: Update GitHub Actions E2E workflow for Playwright report handling - Change condition for uploading Playwright report to ensure it uploads regardless of test outcome. - Reduce retention days for uploaded reports from 5 to 3 for better resource management. * chore: Update Playwright installation command in GitHub Actions workflow - Specify installation of Chromium and WebKit browsers along with dependencies for enhanced testing capabilities. * chore: Enhance E2E tests for seller and buyer management functionality - Add tests to verify the deletion process for sellers and buyers, including confirmation dialogs and success messages. - Ensure localStorage data is correctly saved and parsed for both seller and buyer information. - Introduce default data constants for sellers and buyers to streamline test setup. - Improve accessibility by adding screen reader text for delete buttons in the seller management component. * chore: Pin versions of GitHub Actions in E2E workflow for stability - Update actions/checkout, pnpm/action-setup, actions/setup-node, and actions/upload-artifact to specific versions for improved reliability and security. - Comment added to clarify the rationale for using pinned versions. * chore: Add E2E test for accordion items visibility and localStorage state management - Implement test to verify that accordion items are visible, collapsible, and their state is correctly saved in localStorage. - Ensure state persistence across page reloads and validate updated states after toggling sections. - Introduce ACCORDION_STATE_LOCAL_STORAGE_KEY and AccordionState type for better type safety and clarity. * chore: Update Playwright configuration and add comprehensive E2E tests for seller and buyer management - Increase timeout for expect assertions and test execution from 30 seconds to 60 seconds for improved stability in E2E tests. - Introduce new E2E tests for seller and buyer management, covering creation, editing, and deletion processes, including confirmation dialogs and success messages. - Ensure localStorage data is correctly saved and parsed for both seller and buyer information. - Implement detailed validation for form fields and visibility toggles in seller and buyer management dialogs. - Enhance accessibility by adding screen reader text for buttons and tooltips in the management components. * chore: Refactor Playwright configuration and enhance invoice item validation tests - Introduce a constant for timeout values in Playwright configuration for consistency and maintainability. - Add comprehensive validation tests for amount, net price, and VAT fields in the invoice items section, ensuring proper error messages for invalid inputs. - Update expected error messages in the schema to match the new formatting for better clarity. - Improve test structure by utilizing descriptive variable names and modularizing input handling for better readability. * chore: add pdf e2e tests - Add `pdf-parse` and its type definitions to package.json for PDF handling capabilities. - Increase Playwright timeout from 30 seconds to 60 seconds for improved test stability. - Introduce comprehensive E2E tests for PDF generation, verifying content in both English and Polish. - Implement cleanup procedures for test downloads to ensure a clean testing environment. - Validate invoice data updates in the generated PDF, ensuring accurate content reflects user inputs. * chore: add eslint, knip, lint-staged * chore: run prettier * chore: minor improvements * chore: add more test and improved e2e config * minor fixes * minor fixes * chore: add new test
309 lines
9.9 KiB
TypeScript
309 lines
9.9 KiB
TypeScript
import { DEFAULT_BUYER_DATA, type BuyerData } from "@/app/schema";
|
|
import { expect, test } from "@playwright/test";
|
|
|
|
test.describe("Buyer management", () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto("/");
|
|
});
|
|
|
|
test("create/edit buyer", async ({ page }) => {
|
|
// Open buyer management dialog
|
|
await page.getByRole("button", { name: "New Buyer" }).click();
|
|
|
|
// Fill in all buyer details
|
|
const testData = {
|
|
name: "New Test Client",
|
|
address: "456 Client Avenue\nClient City, 54321\nClient Country",
|
|
|
|
vatNoFieldIsVisible: true,
|
|
vatNo: "987654321",
|
|
|
|
email: "client@example.com",
|
|
} as const satisfies BuyerData;
|
|
|
|
const manageBuyerDialog = page.getByTestId(`manage-buyer-dialog`);
|
|
|
|
// Fill in form fields
|
|
await manageBuyerDialog
|
|
.getByRole("textbox", { name: "Name" })
|
|
.fill(testData.name);
|
|
await manageBuyerDialog
|
|
.getByRole("textbox", { name: "Address" })
|
|
.fill(testData.address);
|
|
await manageBuyerDialog
|
|
.getByRole("textbox", { name: "VAT Number" })
|
|
.fill(testData.vatNo);
|
|
await manageBuyerDialog
|
|
.getByRole("textbox", { name: "Email" })
|
|
.fill(testData.email);
|
|
|
|
// Verify VAT visibility switch is checked by default
|
|
await expect(
|
|
manageBuyerDialog.getByRole("switch", { name: "Show in PDF" }).nth(0)
|
|
).toBeChecked();
|
|
|
|
// Toggle VAT visibility switch
|
|
await manageBuyerDialog
|
|
.getByRole("switch", { name: "Show in PDF" })
|
|
.nth(0)
|
|
.click(); // Toggle VAT Number visibility
|
|
|
|
// Verify "Apply to Current Invoice" switch is checked by default
|
|
await expect(
|
|
manageBuyerDialog.getByRole("switch", {
|
|
name: "Apply to Current Invoice",
|
|
})
|
|
).toBeChecked();
|
|
|
|
// Cancel button is shown
|
|
await expect(
|
|
manageBuyerDialog.getByRole("button", { name: "Cancel" })
|
|
).toBeVisible();
|
|
|
|
// Save buyer
|
|
await manageBuyerDialog.getByRole("button", { name: "Save Buyer" }).click();
|
|
|
|
// Verify success toast message is visible
|
|
await expect(
|
|
page.getByText("Buyer added successfully", { exact: true })
|
|
).toBeVisible();
|
|
|
|
// Verify buyer data is actually saved in localStorage
|
|
const storedData = (await page.evaluate(() => {
|
|
return localStorage.getItem("EASY_INVOICE_PDF_BUYERS");
|
|
})) as string;
|
|
expect(storedData).toBeTruthy();
|
|
|
|
const parsedData = JSON.parse(storedData) as BuyerData[];
|
|
|
|
expect(parsedData[0]).toMatchObject({
|
|
name: testData.name,
|
|
address: testData.address,
|
|
|
|
vatNo: testData.vatNo,
|
|
vatNoFieldIsVisible: false,
|
|
|
|
email: testData.email,
|
|
} satisfies BuyerData);
|
|
|
|
// Verify all saved details in the Buyer Information section form
|
|
const buyerForm = page.getByTestId(`buyer-information-section`);
|
|
|
|
// Try to find desktop tooltip icon first
|
|
const desktopTooltipExists =
|
|
(await buyerForm
|
|
.getByTestId("form-section-tooltip-info-icon-desktop")
|
|
.count()) > 0;
|
|
|
|
// If desktop tooltip exists, hover over it; otherwise find and click mobile tooltip
|
|
// eslint-disable-next-line playwright/no-conditional-in-test
|
|
if (desktopTooltipExists) {
|
|
// Get desktop tooltip icons and hover over the first one because we use tooltip
|
|
const desktopTooltips = buyerForm.getByTestId(
|
|
"form-section-tooltip-info-icon-desktop"
|
|
);
|
|
await desktopTooltips.first().hover();
|
|
} else {
|
|
// Get mobile tooltip icons and click the first one because we use popover
|
|
const mobileTooltips = buyerForm.getByTestId(
|
|
"form-section-tooltip-info-icon-mobile"
|
|
);
|
|
await mobileTooltips.first().click();
|
|
}
|
|
|
|
// Check that HTML title attributes contain the tooltip message on input fields
|
|
const nameInput = buyerForm.getByRole("textbox", { name: "Name" });
|
|
await expect(nameInput).toHaveAttribute(
|
|
"title",
|
|
"Buyer details are locked. Click the edit buyer button to modify."
|
|
);
|
|
|
|
// Buyer Name
|
|
await expect(nameInput).toHaveAttribute("aria-readonly", "true");
|
|
await expect(nameInput).toHaveValue(testData.name);
|
|
|
|
// Buyer Address
|
|
await expect(
|
|
buyerForm.getByRole("textbox", { name: "Address" })
|
|
).toHaveAttribute("aria-readonly", "true");
|
|
await expect(
|
|
buyerForm.getByRole("textbox", { name: "Address" })
|
|
).toHaveValue(testData.address);
|
|
|
|
// Buyer VAT Number
|
|
await expect(
|
|
buyerForm.getByRole("textbox", { name: "VAT Number" })
|
|
).toHaveAttribute("aria-readonly", "true");
|
|
await expect(
|
|
buyerForm.getByRole("textbox", { name: "VAT Number" })
|
|
).toHaveValue(testData.vatNo);
|
|
|
|
const vatNumberSwitch = buyerForm.getByTestId(`buyerVatNoFieldIsVisible`);
|
|
// Verify VAT Number switch is not checked as we toggled it off
|
|
await expect(vatNumberSwitch).not.toBeChecked();
|
|
await expect(vatNumberSwitch).toBeDisabled();
|
|
|
|
// Buyer Email
|
|
await expect(
|
|
buyerForm.getByRole("textbox", { name: "Email" })
|
|
).toHaveAttribute("aria-readonly", "true");
|
|
await expect(buyerForm.getByRole("textbox", { name: "Email" })).toHaveValue(
|
|
testData.email
|
|
);
|
|
|
|
// Verify the buyer appears in the dropdown
|
|
await expect(
|
|
buyerForm.getByRole("combobox", { name: "Select Buyer" })
|
|
).toContainText(testData.name);
|
|
|
|
// Test edit functionality
|
|
await buyerForm.getByRole("button", { name: "Edit buyer" }).click();
|
|
|
|
// Verify all fields are populated in edit dialog
|
|
await expect(
|
|
manageBuyerDialog.getByRole("textbox", { name: "Name" })
|
|
).toHaveValue(testData.name);
|
|
await expect(
|
|
manageBuyerDialog.getByRole("textbox", { name: "Address" })
|
|
).toHaveValue(testData.address);
|
|
await expect(
|
|
manageBuyerDialog.getByRole("textbox", { name: "VAT Number" })
|
|
).toHaveValue(testData.vatNo);
|
|
await expect(
|
|
manageBuyerDialog.getByRole("textbox", { name: "Email" })
|
|
).toHaveValue(testData.email);
|
|
|
|
// Verify visibility switch state persisted in edit dialog
|
|
await expect(
|
|
manageBuyerDialog.getByRole("switch", { name: "Show in PDF" }).nth(0)
|
|
).not.toBeChecked();
|
|
|
|
// Update some data in edit mode
|
|
const updatedName = "Updated Client Corp";
|
|
await manageBuyerDialog
|
|
.getByRole("textbox", { name: "Name" })
|
|
.fill(updatedName);
|
|
|
|
// Re-enable VAT visibility
|
|
await manageBuyerDialog
|
|
.getByRole("switch", { name: "Show in PDF" })
|
|
.nth(0)
|
|
.click();
|
|
|
|
// Save updated buyer
|
|
await manageBuyerDialog.getByRole("button", { name: "Save Buyer" }).click();
|
|
|
|
// Verify success toast for update
|
|
await expect(
|
|
page.getByText("Buyer updated successfully", { exact: true })
|
|
).toBeVisible();
|
|
|
|
// Verify updated information is displayed
|
|
await expect(buyerForm.getByRole("textbox", { name: "Name" })).toHaveValue(
|
|
updatedName
|
|
);
|
|
|
|
// Verify VAT visibility is now enabled
|
|
await expect(
|
|
buyerForm.getByTestId(`buyerVatNoFieldIsVisible`)
|
|
).toBeChecked();
|
|
});
|
|
|
|
test("delete buyer", async ({ page }) => {
|
|
// First add a buyer
|
|
await page.getByRole("button", { name: "New Buyer" }).click();
|
|
|
|
const testData = {
|
|
name: "Test Delete Buyer",
|
|
address: "456 Delete Avenue",
|
|
email: "delete@buyer.com",
|
|
|
|
vatNoFieldIsVisible: true,
|
|
vatNo: "123456789",
|
|
} as const satisfies BuyerData;
|
|
|
|
const manageBuyerDialog = page.getByTestId(`manage-buyer-dialog`);
|
|
|
|
// Fill in basic buyer details
|
|
await manageBuyerDialog
|
|
.getByRole("textbox", { name: "Name" })
|
|
.fill(testData.name);
|
|
await manageBuyerDialog
|
|
.getByRole("textbox", { name: "Address" })
|
|
.fill(testData.address);
|
|
await manageBuyerDialog
|
|
.getByRole("textbox", { name: "Email" })
|
|
.fill(testData.email);
|
|
|
|
// Save buyer
|
|
await manageBuyerDialog.getByRole("button", { name: "Save Buyer" }).click();
|
|
|
|
// Verify buyer was added
|
|
const buyerForm = page.getByTestId(`buyer-information-section`);
|
|
await expect(
|
|
buyerForm.getByRole("combobox", { name: "Select Buyer" })
|
|
).toContainText(testData.name);
|
|
|
|
// Click delete button
|
|
await buyerForm.getByRole("button", { name: "Delete buyer" }).click();
|
|
|
|
// Verify delete confirmation dialog appears
|
|
await expect(page.getByRole("alertdialog")).toBeVisible();
|
|
await expect(
|
|
page.getByText(
|
|
`Are you sure you want to delete "${testData.name}" buyer?`
|
|
)
|
|
).toBeVisible();
|
|
|
|
// Cancel button is shown
|
|
await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible();
|
|
|
|
// Click cancel button
|
|
await page.getByRole("button", { name: "Cancel" }).click();
|
|
|
|
// Verify dialog is closed
|
|
await expect(page.getByRole("alertdialog")).toBeHidden();
|
|
|
|
// Click delete button once again to open the dialog
|
|
await buyerForm.getByRole("button", { name: "Delete buyer" }).click();
|
|
|
|
// Verify delete confirmation dialog appears
|
|
await expect(page.getByRole("alertdialog")).toBeVisible();
|
|
await expect(
|
|
page.getByText(
|
|
`Are you sure you want to delete "${testData.name}" buyer?`
|
|
)
|
|
).toBeVisible();
|
|
|
|
// Confirm deletion
|
|
await page.getByRole("button", { name: "Delete" }).click();
|
|
|
|
// Verify success message
|
|
await expect(
|
|
page.getByText("Buyer deleted successfully", { exact: true })
|
|
).toBeVisible();
|
|
|
|
// Verify buyer is removed from dropdown
|
|
// because we have only one buyer, dropdown will be completely hidden
|
|
await expect(
|
|
buyerForm.getByRole("combobox", { name: "Select Buyer" })
|
|
).toBeHidden();
|
|
|
|
// Verify form is reset to default values
|
|
await expect(buyerForm.getByRole("textbox", { name: "Name" })).toHaveValue(
|
|
DEFAULT_BUYER_DATA.name
|
|
);
|
|
|
|
await expect(
|
|
buyerForm.getByRole("textbox", { name: "Address" })
|
|
).toHaveValue(DEFAULT_BUYER_DATA.address);
|
|
|
|
await expect(buyerForm.getByRole("textbox", { name: "Email" })).toHaveValue(
|
|
DEFAULT_BUYER_DATA.email
|
|
);
|
|
|
|
await expect(
|
|
buyerForm.getByRole("textbox", { name: "VAT Number" })
|
|
).toHaveValue(DEFAULT_BUYER_DATA.vatNo);
|
|
});
|
|
});
|