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
474 lines
15 KiB
TypeScript
474 lines
15 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import dayjs from "dayjs";
|
|
import fs from "fs";
|
|
import path from "path";
|
|
import pdf from "pdf-parse";
|
|
|
|
const TEST_DOWNLOADS_DIR = "test-downloads";
|
|
|
|
/**
|
|
* We can't test the PDF preview because it's not supported in Playwright.
|
|
* https://github.com/microsoft/playwright/issues/7822
|
|
*
|
|
* we download pdf file and parse the content to verify the invoice data
|
|
*/
|
|
test.describe("PDF Preview", () => {
|
|
test.beforeAll(async () => {
|
|
// Ensure test-downloads directory exists
|
|
try {
|
|
await fs.promises.mkdir(TEST_DOWNLOADS_DIR);
|
|
} catch (error) {
|
|
// Handle specific error cases if needed
|
|
if ((error as NodeJS.ErrnoException).code !== "EEXIST") {
|
|
throw error;
|
|
}
|
|
}
|
|
});
|
|
|
|
test.afterEach(async () => {
|
|
// Clean up all files in test-downloads directory
|
|
try {
|
|
const files = await fs.promises.readdir(TEST_DOWNLOADS_DIR);
|
|
|
|
await Promise.all(
|
|
files.map((file) =>
|
|
fs.promises.unlink(path.join(TEST_DOWNLOADS_DIR, file))
|
|
)
|
|
);
|
|
} catch (error) {
|
|
// Handle specific error cases if needed
|
|
// ENOENT means "no such file or directory" - ignore this expected error
|
|
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
throw error;
|
|
}
|
|
}
|
|
});
|
|
|
|
test.afterAll(async () => {
|
|
// Remove the test-downloads directory itself
|
|
try {
|
|
await fs.promises.rmdir(TEST_DOWNLOADS_DIR);
|
|
} catch (error) {
|
|
// Handle specific error cases if needed
|
|
// ENOENT means "no such file or directory" - ignore this expected error
|
|
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
throw error;
|
|
}
|
|
}
|
|
});
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto("/");
|
|
});
|
|
|
|
test("downloads PDF in English and verifies content", async ({ page }) => {
|
|
// Set up download handler
|
|
const downloadPromise = page.waitForEvent("download");
|
|
|
|
const downloadButton = page.getByRole("link", {
|
|
name: "Download PDF in English",
|
|
});
|
|
// Wait for download button to be visible and enabled
|
|
await expect(downloadButton).toBeVisible();
|
|
await expect(downloadButton).toBeEnabled();
|
|
|
|
// Click the download button
|
|
await downloadButton.click();
|
|
|
|
// Wait for the download to start
|
|
const download = await downloadPromise;
|
|
|
|
// Get the suggested filename
|
|
const suggestedFilename = download.suggestedFilename();
|
|
|
|
// Save the file to a temporary location
|
|
const tmpPath = path.join(TEST_DOWNLOADS_DIR, suggestedFilename);
|
|
await download.saveAs(tmpPath);
|
|
|
|
// Read and verify PDF content using pdf-parse
|
|
const dataBuffer = await fs.promises.readFile(tmpPath);
|
|
const pdfData = await pdf(dataBuffer);
|
|
|
|
// Verify PDF content
|
|
expect(pdfData.text).toContain("Invoice No. of:");
|
|
expect(pdfData.text).toContain("Date of issue:");
|
|
|
|
expect(pdfData.text).toContain("Seller");
|
|
expect(pdfData.text).toContain("Buyer");
|
|
|
|
const lastDayOfCurrentMonth = dayjs().endOf("month").format("YYYY-MM-DD");
|
|
|
|
expect(pdfData.text).toContain(
|
|
`Date of sales/of executing the service: ${lastDayOfCurrentMonth}`
|
|
);
|
|
expect(pdfData.text).toContain(`To pay: 0.00 EUR
|
|
Paid: 0.00 EUR
|
|
Left to pay: 0.00 EUR
|
|
Amount in words: zero EUR 00/100`);
|
|
|
|
expect(pdfData.text).toContain(`Reverse charge
|
|
Created with https://easyinvoicepdf.com`);
|
|
});
|
|
|
|
test("downloads PDF in Polish and verifies translated content", async ({
|
|
page,
|
|
}) => {
|
|
// Switch to Polish
|
|
await page
|
|
.getByRole("combobox", { name: "Invoice PDF Language" })
|
|
.selectOption("pl");
|
|
|
|
// we wait until this button is visible and enabled, that means that the PDF preview has been regenerated
|
|
const downloadButton = page.getByRole("link", {
|
|
name: "Download PDF in Polish",
|
|
});
|
|
|
|
await expect(downloadButton).toBeVisible();
|
|
await expect(downloadButton).toBeEnabled();
|
|
|
|
// Set up download handler
|
|
const downloadPromise = page.waitForEvent("download");
|
|
|
|
// Click the download button
|
|
await downloadButton.click();
|
|
|
|
// Wait for the download to start
|
|
const download = await downloadPromise;
|
|
|
|
// Get the suggested filename
|
|
const suggestedFilename = download.suggestedFilename();
|
|
|
|
// Save the file to a temporary location
|
|
const tmpPath = path.join(TEST_DOWNLOADS_DIR, suggestedFilename);
|
|
await download.saveAs(tmpPath);
|
|
|
|
// Read and verify PDF content using pdf-parse
|
|
const dataBuffer = await fs.promises.readFile(tmpPath);
|
|
const pdfData = await pdf(dataBuffer);
|
|
|
|
// Verify PDF content
|
|
expect(pdfData.text).toContain("Faktura nr");
|
|
expect(pdfData.text).toContain("Data wystawienia");
|
|
|
|
expect(pdfData.text).toContain("Sprzedawca");
|
|
expect(pdfData.text).toContain("Nabywca");
|
|
|
|
expect(pdfData.text).toContain("Data wystawienia:");
|
|
|
|
const lastDayOfCurrentMonth = dayjs().endOf("month").format("YYYY-MM-DD");
|
|
expect(pdfData.text).toContain(
|
|
`Data sprzedaży / wykonania usługi: ${lastDayOfCurrentMonth}`
|
|
);
|
|
|
|
expect(pdfData.text).toContain(`Razem do zapłaty: 0.00 EUR
|
|
Wpłacono: 0.00 EUR
|
|
Pozostało do zapłaty: 0.00 EUR
|
|
Kwota słownie: zero EUR 00/100`);
|
|
|
|
expect(pdfData.text).toContain("Created with https://easyinvoicepdf.com");
|
|
});
|
|
|
|
test("update pdf when invoice data changes", async ({ page }) => {
|
|
// Switch to another currency
|
|
await page.getByRole("combobox", { name: "Currency" }).selectOption("GBP");
|
|
|
|
// Switch to another date format
|
|
await page
|
|
.getByRole("combobox", { name: "Date format" })
|
|
.selectOption("MMMM D, YYYY");
|
|
|
|
await page
|
|
.getByRole("textbox", { name: "Invoice Type" })
|
|
.fill("HELLO FROM PLAYWRIGHT TEST!");
|
|
|
|
const sellerSection = page.getByTestId(`seller-information-section`);
|
|
|
|
// Name field
|
|
await sellerSection
|
|
.getByRole("textbox", { name: "Name" })
|
|
.fill("PLAYWRIGHT SELLER TEST");
|
|
|
|
// Toggle VAT Number visibility off
|
|
await sellerSection
|
|
.getByRole("switch", { name: /Show in PDF/i })
|
|
.nth(0)
|
|
.click();
|
|
|
|
// Toggle Account Number visibility off
|
|
await sellerSection
|
|
.getByRole("switch", { name: /Show in PDF/i })
|
|
.nth(1)
|
|
.click();
|
|
|
|
// Toggle SWIFT visibility off
|
|
await sellerSection
|
|
.getByRole("switch", { name: /Show in PDF/i })
|
|
.nth(2)
|
|
.click();
|
|
|
|
const buyerSection = page.getByTestId(`buyer-information-section`);
|
|
|
|
// Name field
|
|
await buyerSection
|
|
.getByRole("textbox", { name: "Name" })
|
|
.fill("PLAYWRIGHT BUYER TEST");
|
|
|
|
// // Address field
|
|
await buyerSection
|
|
.getByRole("textbox", { name: "Address" })
|
|
.fill("PLAYWRIGHT BUYER ADDRESS TEST");
|
|
|
|
// // Email field
|
|
await buyerSection
|
|
.getByRole("textbox", { name: "Email" })
|
|
.fill("TEST_BUYER_EMAIL@mail.com");
|
|
|
|
const invoiceSection = page.getByTestId(`invoice-items-section`);
|
|
|
|
// Amount field
|
|
await invoiceSection.getByRole("spinbutton", { name: "Amount" }).fill("3");
|
|
|
|
// Net price field
|
|
await invoiceSection
|
|
.getByRole("spinbutton", { name: "Net price" })
|
|
.fill("1000");
|
|
|
|
// Toggle VAT Table Summary visibility off
|
|
await page
|
|
.getByRole("switch", { name: `Show "VAT Table Summary" in the PDF` })
|
|
.click();
|
|
|
|
// Wait for PDF preview to regenerate after language change (debounce timeout)
|
|
// eslint-disable-next-line playwright/no-wait-for-timeout
|
|
await page.waitForTimeout(600);
|
|
|
|
// Set up download handler
|
|
const downloadPromise = page.waitForEvent("download");
|
|
|
|
const downloadButton = page.getByRole("link", {
|
|
name: "Download PDF in English",
|
|
});
|
|
|
|
await expect(downloadButton).toBeVisible();
|
|
await expect(downloadButton).toBeEnabled();
|
|
|
|
// Click the download button
|
|
await downloadButton.click();
|
|
|
|
// Wait for the download to start
|
|
const download = await downloadPromise;
|
|
|
|
// Get the suggested filename
|
|
const suggestedFilename = download.suggestedFilename();
|
|
|
|
// Save the file to a temporary location
|
|
const tmpPath = path.join(TEST_DOWNLOADS_DIR, suggestedFilename);
|
|
await download.saveAs(tmpPath);
|
|
|
|
// Read and verify PDF content using pdf-parse
|
|
const dataBuffer = await fs.promises.readFile(tmpPath);
|
|
const pdfData = await pdf(dataBuffer);
|
|
|
|
const lastDayOfCurrentMonth = dayjs().endOf("month").format("MMMM D, YYYY");
|
|
|
|
// Verify PDF content
|
|
expect(pdfData.text).toContain(
|
|
`Date of sales/of executing the service: ${lastDayOfCurrentMonth}`
|
|
);
|
|
|
|
expect(pdfData.text).toContain("HELLO FROM PLAYWRIGHT TEST!");
|
|
expect(pdfData.text).toContain("PLAYWRIGHT SELLER TEST");
|
|
expect(pdfData.text).toContain(`PLAYWRIGHT BUYER TEST`);
|
|
expect(pdfData.text).toContain(`PLAYWRIGHT BUYER ADDRESS TEST`);
|
|
expect(pdfData.text).toContain(`TEST_BUYER_EMAIL@mail.com`);
|
|
|
|
// Check that the PDF does NOT contain the seller's VAT number, account number, or SWIFT/BIC number
|
|
// because we toggled the visibility off
|
|
expect(pdfData.text).not.toContain(`VAT no: Seller vat number
|
|
Account Number - Seller account number
|
|
SWIFT/BIC number: Seller swift bic`);
|
|
|
|
// Check that the PDF does NOT contain the VAT table summary
|
|
// because we toggled the visibility off
|
|
expect(pdfData.text).not.toContain(`VAT rateNetVATPre-tax
|
|
NP3 000.000.003 000.00
|
|
Total3 000.000.003 000.00`);
|
|
|
|
expect(pdfData.text).toContain(`To pay: 3 000.00 GBP
|
|
Paid: 0.00 GBP
|
|
Left to pay: 3 000.00 GBP
|
|
Amount in words: three thousand GBP 00/100`);
|
|
|
|
expect(pdfData.text).toContain(`Reverse charge
|
|
Created with https://easyinvoicepdf.com`);
|
|
});
|
|
|
|
test("completes full invoice flow on mobile: tabs navigation, form editing and PDF download", async ({
|
|
page,
|
|
}) => {
|
|
// Set mobile viewport
|
|
await page.setViewportSize({ width: 375, height: 667 });
|
|
|
|
// Verify tabs are visible in mobile view
|
|
await expect(page.getByRole("tab", { name: "Edit Invoice" })).toBeVisible();
|
|
await expect(page.getByRole("tab", { name: "Preview PDF" })).toBeVisible();
|
|
|
|
// Download button in English is visible and enabled
|
|
const downloadButtonEnglish = page.getByRole("link", {
|
|
name: "Download PDF in English",
|
|
});
|
|
// Wait for download button to be visible
|
|
await expect(downloadButtonEnglish).toBeVisible();
|
|
// Wait for download button to be enabled
|
|
await expect(downloadButtonEnglish).toBeEnabled();
|
|
|
|
// Switch to Polish
|
|
await page
|
|
.getByRole("combobox", { name: "Invoice PDF Language" })
|
|
.selectOption("pl");
|
|
|
|
// Fill in some invoice data
|
|
await page
|
|
.getByRole("textbox", { name: "Invoice Number" })
|
|
.fill("MOBILE-TEST-001");
|
|
await page
|
|
.getByRole("textbox", { name: "Notes", exact: true })
|
|
.fill("Mobile test note");
|
|
|
|
// Fill in seller information
|
|
const sellerSection = page.getByTestId("seller-information-section");
|
|
await sellerSection
|
|
.getByRole("textbox", { name: "Name" })
|
|
.fill("Mobile Test Seller");
|
|
await sellerSection
|
|
.getByRole("textbox", { name: "Address" })
|
|
.fill("456 Mobile St");
|
|
|
|
// Fill in an invoice item
|
|
const invoiceItemsSection = page.getByTestId("invoice-items-section");
|
|
await invoiceItemsSection
|
|
.getByRole("spinbutton", { name: "Amount" })
|
|
.fill("3");
|
|
await invoiceItemsSection
|
|
.getByRole("spinbutton", { name: "Net Price" })
|
|
.fill("50");
|
|
await invoiceItemsSection
|
|
.getByRole("textbox", { name: "VAT", exact: true })
|
|
.fill("23");
|
|
|
|
// we wait until this button is visible and enabled, that means that the PDF preview has been regenerated
|
|
const downloadButtonPolish = page.getByRole("link", {
|
|
name: "Download PDF in Polish",
|
|
});
|
|
// Wait for download button to be visible and enabled
|
|
await expect(downloadButtonPolish).toBeVisible();
|
|
await expect(downloadButtonPolish).toBeEnabled();
|
|
|
|
// Switch to preview tab
|
|
await page.getByRole("tab", { name: "Preview PDF" }).click();
|
|
|
|
// Verify preview tab is selected
|
|
await expect(
|
|
page.getByRole("tabpanel", { name: "Preview PDF" })
|
|
).toBeVisible();
|
|
await expect(
|
|
page.getByRole("tabpanel", { name: "Edit Invoice" })
|
|
).toBeHidden();
|
|
|
|
// Set up download handler
|
|
const downloadPromise = page.waitForEvent("download");
|
|
|
|
// Click the download button
|
|
await downloadButtonPolish.click();
|
|
|
|
// Wait for the download to start
|
|
const download = await downloadPromise;
|
|
|
|
// Get the suggested filename
|
|
const suggestedFilename = download.suggestedFilename();
|
|
|
|
// Save the file to a temporary location
|
|
const tmpPath = path.join(TEST_DOWNLOADS_DIR, suggestedFilename);
|
|
await download.saveAs(tmpPath);
|
|
|
|
// Read and verify PDF content using pdf-parse
|
|
const dataBuffer = await fs.promises.readFile(tmpPath);
|
|
const pdfData = await pdf(dataBuffer);
|
|
|
|
// Verify PDF content in Polish
|
|
expect(pdfData.text).toContain("Faktura nr");
|
|
expect(pdfData.text).toContain("Data wystawienia");
|
|
expect(pdfData.text).toContain("Sprzedawca");
|
|
expect(pdfData.text).toContain("Nabywca");
|
|
expect(pdfData.text).toContain("Mobile Test Seller");
|
|
expect(pdfData.text).toContain("456 Mobile St");
|
|
|
|
const lastDayOfCurrentMonth = dayjs().endOf("month").format("YYYY-MM-DD");
|
|
expect(pdfData.text).toContain(
|
|
`Data sprzedaży / wykonania usługi: ${lastDayOfCurrentMonth}`
|
|
);
|
|
|
|
// Verify calculations in Polish
|
|
expect(pdfData.text).toContain(`Razem do zapłaty: 184.50 EUR
|
|
Wpłacono: 0.00 EUR
|
|
Pozostało do zapłaty: 184.50 EUR`);
|
|
|
|
// Switch back to form tab
|
|
await page.getByRole("tab", { name: "Edit Invoice" }).click();
|
|
|
|
// Verify form tab is selected and data persists
|
|
await expect(
|
|
page.getByRole("tabpanel", { name: "Edit Invoice" })
|
|
).toBeVisible();
|
|
await expect(
|
|
page.getByRole("tabpanel", { name: "Preview PDF" })
|
|
).toBeHidden();
|
|
|
|
// Verify form data persists
|
|
await expect(
|
|
page.getByRole("textbox", { name: "Invoice Number" })
|
|
).toHaveValue("MOBILE-TEST-001");
|
|
await expect(
|
|
page.getByRole("textbox", { name: "Notes", exact: true })
|
|
).toHaveValue("Mobile test note");
|
|
|
|
// Verify seller information persists
|
|
await expect(
|
|
sellerSection.getByRole("textbox", { name: "Name" })
|
|
).toHaveValue("Mobile Test Seller");
|
|
await expect(
|
|
sellerSection.getByRole("textbox", { name: "Address" })
|
|
).toHaveValue("456 Mobile St");
|
|
|
|
// Verify invoice item persists
|
|
await expect(
|
|
invoiceItemsSection.getByRole("spinbutton", { name: "Amount" })
|
|
).toHaveValue("3");
|
|
await expect(
|
|
invoiceItemsSection.getByRole("spinbutton", { name: "Net Price" })
|
|
).toHaveValue("50");
|
|
await expect(
|
|
invoiceItemsSection.getByRole("textbox", { name: "VAT", exact: true })
|
|
).toHaveValue("23");
|
|
|
|
// Verify calculations are correct
|
|
await expect(
|
|
invoiceItemsSection.getByRole("textbox", {
|
|
name: "Net Amount",
|
|
exact: true,
|
|
})
|
|
).toHaveValue("150.00");
|
|
await expect(
|
|
invoiceItemsSection.getByRole("textbox", {
|
|
name: "VAT Amount",
|
|
exact: true,
|
|
})
|
|
).toHaveValue("34.50");
|
|
await expect(
|
|
invoiceItemsSection.getByRole("textbox", {
|
|
name: "Pre-tax Amount",
|
|
exact: true,
|
|
})
|
|
).toHaveValue("184.50");
|
|
});
|
|
});
|