feat: customizable tax/VAT label text + a lot of other improvements and bug fixes#163
feat: customizable tax/VAT label text + a lot of other improvements and bug fixes#163
Conversation
* Introduced a new feature allowing users to customize the VAT label text in the invoice form. * Updated the invoice form, PDF templates, and related components to utilize the customizable VAT label. * Enhanced validation for VAT input to accept both numeric values and specific strings. * Improved user interface messages for clarity regarding VAT input requirements.
* Revised README.md to enhance project description, add links, and improve feature presentation. * Updated .env.example comments for better understanding of invoice generation functionality. * Added new screenshots for default and Stripe templates, while removing outdated images. * Enhanced localization files with HTML span tags for better styling in descriptions across multiple languages.
* Changed all instances of VAT to Tax in the invoice form, PDF templates, and related components for consistency. * Updated validation and schema to reflect the new tax label terminology. * Removed the dynamic local storage view component as it was no longer needed. * Cleaned up unused imports and components to streamline the codebase.
* Removed the local storage debug variable from .env.example for clarity. * Added dotenv package to package.json to manage environment variables more effectively. * Updated Playwright configuration to load environment variables from .env.local for improved testing setup. * Enhanced VSCode settings for better TypeScript development experience. * Cleaned up and organized various components and localization files for consistency.
* Added support for a customizable Tax Number label in the buyer and seller information sections. * Updated related components, forms, and validation schemas to accommodate the new label functionality. * Refactored existing tests to ensure proper handling of the Tax Number label in various scenarios. * Enhanced user interface to allow users to set and display custom labels for Tax Number in the invoice PDF templates.
* Added a new e2e test for the About page to ensure content visibility and proper screenshot generation. * Introduced a new command in package.json for non-flaky end-to-end tests. * Updated Playwright configuration to improve timeout settings and worker management. * Revised README to clarify key features and project details. * Added a TODO file for future enhancements and refinements. * Updated various localization files and metadata for consistency across languages. * Refactored components for better organization and improved user experience.
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughRemoves newsletter subscription/backend and related UI; introduces configurable tax labels ( Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant InvoiceForm
participant AppMetadata
participant LocalStorage
participant PDFGenerator
User->>InvoiceForm: edit or submit invoice
InvoiceForm->>AppMetadata: updateAppMetadata(invoiceLastUpdatedAt)
AppMetadata->>LocalStorage: write metadata JSON
LocalStorage-->>AppMetadata: ack
InvoiceForm->>PDFGenerator: request PDF (uses taxLabelText/vatNoLabelText)
PDFGenerator-->>InvoiceForm: returns PDF bytes
InvoiceForm->>LocalStorage: save invoice data
LocalStorage-->>InvoiceForm: ack
InvoiceForm-->>User: show "Invoice last updated" indicator
sequenceDiagram
participant TestRunner
participant Playwright
participant Page
participant TempDir as TempDownloadDir
participant Renderer as renderPdfOnCanvas
participant Comparator as ScreenshotComparator
TestRunner->>Playwright: start test with extended fixture
Playwright->>TempDir: create per-test downloadDir
TestRunner->>Page: trigger PDF download
Page-->>TempDir: save PDF file
TestRunner->>Renderer: renderPdfOnCanvas(page, pdfBytes)
Renderer->>Renderer: load pdf.js and render page to canvas
Renderer-->>TestRunner: canvas ready
TestRunner->>Comparator: capture & compare screenshot
Comparator-->>TestRunner: diff result
Playwright->>TempDir: cleanup downloadDir
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
package.json (1)
93-122: Update README to reflect removed devDependency.The
typescript-plugin-directivespackage was removed from devDependencies, but README.md (line 67) still references it in the "Other Tools & Services" section. Please update the documentation to maintain consistency.src/app/(app)/components/invoice-pdf-preview/mobile-pdf-viewer.tsx (1)
108-115: Inconsistent bug report URL usage.Line 57 uses the imported
BUG_REPORT_URLconstant, but line 109 hardcodes a URL with query parameters. This creates maintenance overhead if the base URL changes.🔎 Proposed fix
<a - href="https://pdfinvoicegenerator.userjot.com/board/bugs?cursor=1&order=top&limit=10" + href={BUG_REPORT_URL} target="_blank" rel="noopener noreferrer" className="text-blue-600 underline hover:text-blue-800" >
🧹 Nitpick comments (22)
src/components/ui/button-helper.tsx (1)
13-13: Explicittype="button"is redundant but can still be overridden.The explicit
type="button"declaration is redundant since the underlyingButtoncomponent already defaults totype="button"(as shown in the relevant code snippets). Additionally, because{...props}is spread after the explicit type prop, anytypepassed toButtonHelperwill override it.For a helper/utility button component, it's reasonable to always enforce
type="button"to prevent accidental form submissions. If that's the intent, consider either:
- Omitting
typefrom the accepted props type, or- Setting
type="button"after the spread operatorIf flexibility to override the type is intentional, the current implementation works but the explicit declaration is unnecessary.
🔎 Optional refactor to enforce type="button"
If
ButtonHelpershould always betype="button", exclude thetypeprop from the component's accepted props:+type ButtonHelperProps = { + children: React.ReactNode; + className?: string; +} & Omit<React.ComponentProps<typeof Button>, "type">; + export const ButtonHelper = ({ children, className, ...props -}: { children: React.ReactNode; className?: string } & React.ComponentProps< - typeof Button ->) => { +}: ButtonHelperProps) => { return ( <Button type="button" variant="link" size="sm" className={cn( "h-5 max-w-full whitespace-normal text-pretty p-0 text-left underline", className, )} {...props} > {children} </Button> ); };Also applies to: 20-20
TODO.md (1)
3-4: Minor grammar and maintenance notes.
- "Double check" should be hyphenated as "Double-check" per standard grammar.
- Line number references (e.g., "line: 235") can drift as code evolves—consider using a searchable code comment like
// TODO: verify localStorageinstead.🔎 Suggested fix
-- [ ] Double check if we need to save to local storage in the page.client.tsx (line: 235) +- [ ] Double-check if we need to save to local storage in the page.client.tsx (search for localStorage save logic).prettierignore (1)
2-4: Minor: Trailing whitespace on line 2.There's a trailing space after
messages/en.d.json.ts. While it won't break anything, it's generally cleaner to remove trailing whitespace.🔎 Suggested fix
# i18n -messages/en.d.json.ts +messages/en.d.json.ts pnpm-lock.yamlsrc/app/(app)/hooks/use-show-random-cta-toast.tsx (1)
60-60: Address the TODO for interaction count.The TODO correctly identifies that the current implementation triggers on a single interaction instead of requiring 2-3 meaningful interactions. This could lead to premature CTA toast displays.
Do you want me to generate an implementation that tracks multiple interactions before enabling the idle trigger?
e2e/utils/extended-playwright-test.ts (1)
27-28: Remove misleading eslint-disable comment.The
useparameter is a Playwright fixture callback, not a React hook. Thereact-hooks/rules-of-hooksdisable comment is unnecessary and misleading.🔎 Proposed fix
- // eslint-disable-next-line react-hooks/rules-of-hooks await use(downloadDir);src/app/(app)/components/invoice-pdf-template/invoice-vat-summary-table.tsx (1)
64-70: Consider a consistent internationalization approach.The VAT-related headers now use the dynamic
taxLabelText(lines 64, 70), while other headers like "Net" and "Pre-tax" still use translations (t.vatSummaryTable.net,t.vatSummaryTable.preTax). This creates a mixed approach.If
taxLabelTextis meant to be user-customizable (not just language-specific), this is appropriate. Otherwise, consider whether all labels should follow the same pattern.src/app/(app)/components/invoice-pdf-download-link.tsx (1)
110-113: Remove or track commented code.There's a commented-out code block that appears to be experimental logic for skipping CTA toasts when a toast was already shown in the session. If this feature is deferred, consider creating a TODO comment or issue to track it.
Options for handling this code
Option 1: Remove if no longer needed
- // if (isToastShownInSession) { - // umamiTrackEvent("cta_toast_skipped_downloaded_invoice"); - // return; - // } -Option 2: Add a TODO comment if planned
- // if (isToastShownInSession) { - // umamiTrackEvent("cta_toast_skipped_downloaded_invoice"); - // return; - // } + // TODO: Implement session-based CTA toast deduplication + // Track with issue #XXXsrc/app/(app)/components/invoice-pdf-preview/mobile-pdf-viewer.tsx (1)
96-101: Consider limiting re-render retries.The
onLoadErrorhandler increments the key to force a re-render on error. Without a limit, this could cause infinite re-renders if the error persists.🔎 Proposed fix with retry limit
+const MAX_RETRY_COUNT = 3; // In component: +const [retryCount, setRetryCount] = useState(0); onLoadError={(error) => { console.error(error); - // Force a re-render of the PDF viewer to try to recover from error - setKey((prev) => prev + 1); + // Force a re-render with retry limit to prevent infinite loops + if (retryCount < MAX_RETRY_COUNT) { + setRetryCount((prev) => prev + 1); + setKey((prev) => prev + 1); + } }}src/app/[locale]/about/page.tsx (3)
40-48: Remove unused newsletter translation variables.These variables are defined but never used since
SubscribeSectionis commented out on line 65. Either remove the newsletter functionality entirely or keep it for future use, but unused variables should be cleaned up.🔎 Proposed fix
const t = useTranslations("About"); - const tNewsletter = useTranslations("About.newsletter"); - - const newsletterTitle = tNewsletter("title"); - const newsletterDescription = tNewsletter("description"); - const newsletterSubscribe = tNewsletter("subscribe"); - const newsletterPlaceholder = tNewsletter("placeholder"); - const newsletterSuccessMessage = tNewsletter("success"); - const newsletterErrorMessage = tNewsletter("error"); - const newsletterEmailLanguageInfo = tNewsletter("emailLanguageInfo");Also update the Footer translations prop to remove the unused newsletter keys, or keep them if the Footer component still expects them.
516-534: Remove commented-out code.This large block of commented-out code should be removed. Version control preserves the history if you need to reference the old implementation later.
🔎 Proposed fix
function Logo() { - // const t = useTranslations("About"); - return ( <div> - {/* <div className="flex items-center gap-1"> - <ProjectLogo className="size-7 flex-shrink-0 sm:size-8" /> - <div className="flex flex-col"> - <h1 className="text-balance text-xl font-bold text-slate-800 lg:text-2xl"> - <a - href="https://dub.sh/easy-invoice?ref=about-page" - target="_blank" - rel="noopener noreferrer" - > - EasyInvoicePDF - </a> - </h1> - </div> */} - {/* <ProjectLogoDescription>{t("tagline")}</ProjectLogoDescription> */} - {/* </div> */} <div className="flex items-center">
65-65: Consider removing the commented-out SubscribeSection.If the newsletter subscription feature has been intentionally removed, delete this commented line rather than leaving it in the codebase.
src/app/(app)/utils/get-app-metadata.tsx (2)
13-18:DEFAULT_METADATAtimestamps are captured at module load time.
dayjs().toISOString()is evaluated when the module is first imported, not whenDEFAULT_METADATAis actually used. This means all consumers will get the same timestamp from when the module was loaded, which may be stale. Consider making this a function that generates fresh timestamps.🔎 Proposed fix
-export const DEFAULT_METADATA = { +export const getDefaultMetadata = (): Metadata => ({ appVersion: APP_VERSION, schemaVersion: SCHEMA_VERSION, invoiceCreatedAt: dayjs().toISOString(), invoiceLastUpdatedAt: dayjs().toISOString(), -} as const satisfies Metadata; +}); + +// For type-checking purposes or places needing a static reference +export const DEFAULT_METADATA = getDefaultMetadata();Then update
updateAppMetadatato usegetDefaultMetadata()instead ofDEFAULT_METADATAfor the reset case.
50-50: Redundant optional chaining.After checking
validatedMetadata.success, thedataproperty is guaranteed to exist. The optional chaining is unnecessary.🔎 Proposed fix
- return validatedMetadata?.data; + return validatedMetadata.data;src/components/seller-dialog.tsx (1)
340-350: Simplify the conditional FormMessage rendering.The
FormMessagecomponent from react-hook-form already handles conditional rendering internally—it only displays when there's an error. The explicit conditional check is redundant.🔎 Suggested simplification
- {form.formState.errors.vatNoLabelText && ( - <FormMessage> - {form.formState.errors.vatNoLabelText.message} - </FormMessage> - )} + <FormMessage />e2e/default-invoice-template/pdf.test.ts (1)
58-60: Consider more deterministic waiting instead of timeout.While the comment explains the blank screen issue, the 1-second timeout is non-deterministic. Consider waiting for a specific element or state if possible.
The current approach is pragmatic given PDF viewer rendering, but if flakiness occurs, consider:
- Using
page.waitForLoadState('networkidle')before the timeout- Reducing the timeout after confirming minimum stable wait time
src/app/(app)/components/invoice-form/sections/invoice-items.tsx (1)
165-166: Consider removing or uncommenting aria-label comments.There are several commented-out
aria-labelattributes (lines 165, 224, 283). Either restore these for accessibility or remove the commented code to reduce noise.Also applies to: 224-224, 283-283
src/app/(app)/components/invoice-form/index.tsx (2)
198-198: Consider using the taxLabelText default from schema instead of hardcoding.The fallback
"VAT"is hardcoded here, but the schema already defines a default. To maintain consistency, consider importing and using the schema's default value.
165-168: Consider consolidating duplicate localStorage saves.The codebase has two separate localStorage writes for
PDF_DATA_LOCAL_STORAGE_KEY:
- Debounced save in the child component (
invoice-form/index.tsx, line 174) after form validation- Save in the parent component's useEffect (
page.client.tsx, line 250) when invoice data state changesBoth save the same validated data, creating unnecessary writes. While the parent's useEffect also handles URL updates and notifications, the localStorage save logic could be centralized to eliminate duplication and reduce unnecessary writes.
e2e/stripe-invoice-template/template.test.ts (2)
808-811: Consider using a more reliable wait mechanism than fixed timeout.The
waitForTimeout(1000)calls are used to wait for PDF rendering, but fixed timeouts can be flaky. Consider usingwaitForLoadStateor polling for a specific element/condition.🔎 Suggested approach
- // sometimes there's a blank screen without this - // eslint-disable-next-line playwright/no-wait-for-timeout - await page.waitForTimeout(1000); + // Wait for PDF content to be rendered + await page.waitForLoadState('networkidle');Also applies to: 937-940
613-625: Address or remove the commented-out assertions.These TODO comments indicate that field switch visibility assertions for the Stripe template are incomplete. The commented code should either be fixed and enabled, or removed with a tracking issue created.
Would you like me to open an issue to track fixing these assertions?
src/app/(app)/components/invoice-form/sections/general-information.tsx (1)
728-729: Consider the TODO for renaming "Invoice Type" field.The TODO suggests renaming to "Invoice Notes" which may be more intuitive. If this is a planned change, consider creating a tracking issue.
src/app/(app)/page.client.tsx (1)
248-249: Address the duplicate localStorage save TODO.This TODO echoes the concern in invoice-form/index.tsx. Consider consolidating these saves to avoid redundant writes and potential race conditions.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (43)
.github/screenshots/default-template.pngis excluded by!**/*.png.github/screenshots/default.pngis excluded by!**/*.png.github/screenshots/easy-invoice-logo.svgis excluded by!**/*.svg.github/screenshots/stripe-template.pngis excluded by!**/*.png.github/screenshots/stripe-with-logo.pngis excluded by!**/*.png.github/screenshots/stripe.pngis excluded by!**/*.pnge2e/about-snapshot.test.ts-snapshots/about-page-english-screenshot-about-page-english-screenshot-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/about-snapshot.test.ts-snapshots/about-page-english-screenshot-about-page-english-screenshot-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/about-snapshot.test.ts-snapshots/about-page-english-screenshot-about-page-english-screenshot-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/completes-full-invoice-flow-on-mobile-pdf-playwright-screenshot-invoice-FR-2-05-2024-pdf-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/completes-full-invoice-flow-on-mobile-pdf-playwright-screenshot-invoice-FR-2-05-2024-pdf-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/completes-full-invoice-flow-on-mobile-pdf-playwright-screenshot-invoice-FR-2-05-2024-pdf-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/completes-full-invoice-flow-on-mobile-stripe-template-pdf-playwright-screenshot-stripe-invoice-FR-2-05-2024-pdf-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/completes-full-invoice-flow-on-mobile-stripe-template-pdf-playwright-screenshot-stripe-invoice-FR-2-05-2024-pdf-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/completes-full-invoice-flow-on-mobile-stripe-template-pdf-playwright-screenshot-stripe-invoice-FR-2-05-2024-pdf-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-English-pdf-playwright-screenshot-invoice-EN-1-12-2025-pdf-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-English-pdf-playwright-screenshot-invoice-EN-1-12-2025-pdf-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-English-pdf-playwright-screenshot-invoice-EN-1-12-2025-pdf-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-Polish-pdf-playwright-screenshot-invoice-PL-1-12-2025-pdf-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-Polish-pdf-playwright-screenshot-invoice-PL-1-12-2025-pdf-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-Polish-pdf-playwright-screenshot-invoice-PL-1-12-2025-pdf-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-Polish-stripe-template-pdf-playwright-screenshot-stripe-invoice-PL-1-12-2025-pdf-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-Polish-stripe-template-pdf-playwright-screenshot-stripe-invoice-PL-1-12-2025-pdf-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-Polish-stripe-template-pdf-playwright-screenshot-stripe-invoice-PL-1-12-2025-pdf-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/should-display-and-persist-invoice-number-in-different-languages-pdf-playwright-screenshot-invoice-PT-1-12-2025-pdf-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/should-display-and-persist-invoice-number-in-different-languages-pdf-playwright-screenshot-invoice-PT-1-12-2025-pdf-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/should-display-and-persist-invoice-number-in-different-languages-pdf-playwright-screenshot-invoice-PT-1-12-2025-pdf-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/should-display-and-persist-invoice-number-in-different-languages-stripe-template-pdf-playwright-screenshot-stripe-invoice-PT-1-12-2025-pdf-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/should-display-and-persist-invoice-number-in-different-languages-stripe-template-pdf-playwright-screenshot-stripe-invoice-PT-1-12-2025-pdf-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/should-display-and-persist-invoice-number-in-different-languages-stripe-template-pdf-playwright-screenshot-stripe-invoice-PT-1-12-2025-pdf-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/update-pdf-when-invoice-data-changes-pdf-playwright-screenshot-invoice-EN-1-12-2025-pdf-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/update-pdf-when-invoice-data-changes-pdf-playwright-screenshot-invoice-EN-1-12-2025-pdf-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/update-pdf-when-invoice-data-changes-pdf-playwright-screenshot-invoice-EN-1-12-2025-pdf-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/update-pdf-when-invoice-data-changes-stripe-template-pdf-playwright-screenshot-stripe-invoice-EN-1-12-2025-pdf-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/update-pdf-when-invoice-data-changes-stripe-template-pdf-playwright-screenshot-stripe-invoice-EN-1-12-2025-pdf-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/update-pdf-when-invoice-data-changes-stripe-template-pdf-playwright-screenshot-stripe-invoice-EN-1-12-2025-pdf-chromium-darwin.pngis excluded by!**/*.pnge2e/stripe-invoice-template/template.test.ts-snapshots/automatically-enables-VAT-field-visibility-and-sets-date-format-when-switching-to-Stripe-template-pdf-playwright-screenshot-invoice-EN-1-12-2025-pdf-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/stripe-invoice-template/template.test.ts-snapshots/automatically-enables-VAT-field-visibility-and-sets-date-format-when-switching-to-Stripe-template-pdf-playwright-screenshot-invoice-EN-1-12-2025-pdf-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/stripe-invoice-template/template.test.ts-snapshots/automatically-enables-VAT-field-visibility-and-sets-date-format-when-switching-to-Stripe-template-pdf-playwright-screenshot-invoice-EN-1-12-2025-pdf-chromium-darwin.pngis excluded by!**/*.pnge2e/stripe-invoice-template/template.test.ts-snapshots/pdf-with-logo-and-payment-url-when-using-stripe-template-pdf-playwright-screenshot-invoice-EN-1-12-2025-pdf-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/stripe-invoice-template/template.test.ts-snapshots/pdf-with-logo-and-payment-url-when-using-stripe-template-pdf-playwright-screenshot-invoice-EN-1-12-2025-pdf-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/stripe-invoice-template/template.test.ts-snapshots/pdf-with-logo-and-payment-url-when-using-stripe-template-pdf-playwright-screenshot-invoice-EN-1-12-2025-pdf-chromium-darwin.pngis excluded by!**/*.pngpnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (79)
.env.example.prettierignore.vscode/extensions.json.vscode/settings.jsonREADME.mdTODO.mde2e/about-snapshot.test.tse2e/about.test.tse2e/buyer.test.tse2e/default-invoice-template/pdf.test.tse2e/generate-invoice-link.test.tse2e/invoice-form.test.tse2e/seller.test.tse2e/stripe-invoice-template/template.test.tse2e/utils/extended-playwright-test.tsmessages/de.jsonmessages/en.jsonmessages/es.jsonmessages/fr.jsonmessages/it.jsonmessages/nl.jsonmessages/pl.jsonmessages/pt.jsonmessages/ru.jsonmessages/uk.jsonpackage.jsonplaywright.config.tssrc/actions/subscribe-action.tssrc/app/(app)/components/cta-toasts/index.tsxsrc/app/(app)/components/dev/dev-local-storage-view.tsxsrc/app/(app)/components/index.tsxsrc/app/(app)/components/invoice-form/index.tsxsrc/app/(app)/components/invoice-form/sections/buyer-information.tsxsrc/app/(app)/components/invoice-form/sections/general-information.tsxsrc/app/(app)/components/invoice-form/sections/invoice-items.tsxsrc/app/(app)/components/invoice-form/sections/seller-information.tsxsrc/app/(app)/components/invoice-pdf-download-link.tsxsrc/app/(app)/components/invoice-pdf-preview/desktop-pdf-viewer.tsxsrc/app/(app)/components/invoice-pdf-preview/mobile-pdf-viewer.tsxsrc/app/(app)/components/invoice-pdf-stripe-template/stripe-footer.tsxsrc/app/(app)/components/invoice-pdf-stripe-template/stripe-items-table.tsxsrc/app/(app)/components/invoice-pdf-stripe-template/stripe-seller-buyer-info.tsxsrc/app/(app)/components/invoice-pdf-stripe-template/stripe-totals.tsxsrc/app/(app)/components/invoice-pdf-template/invoice-footer.tsxsrc/app/(app)/components/invoice-pdf-template/invoice-items-table.tsxsrc/app/(app)/components/invoice-pdf-template/invoice-seller-buyer-info.tsxsrc/app/(app)/components/invoice-pdf-template/invoice-vat-summary-table.tsxsrc/app/(app)/error.tsxsrc/app/(app)/hooks/use-show-random-cta-toast.tsxsrc/app/(app)/loading.tsxsrc/app/(app)/page.client.tsxsrc/app/(app)/page.tsxsrc/app/(app)/utils/get-app-metadata.tsxsrc/app/[locale]/about/components/landing-cta-toast.tsxsrc/app/[locale]/about/page.tsxsrc/app/api/generate-invoice/render-pdf-on-server.tsxsrc/app/changelog/layout.tsxsrc/app/confirm-subscription/page.tsxsrc/app/constants/index.tssrc/app/constants/seo.tssrc/app/layout.tsxsrc/app/schema/i18n-schema.tssrc/app/schema/index.tssrc/components/buyer-dialog.tsxsrc/components/buyer-management.tsxsrc/components/etc/project-logo.tsxsrc/components/footer.tsxsrc/components/seller-dialog.tsxsrc/components/seller-management.tsxsrc/components/subscribe-input.tsxsrc/components/ui/button-helper.tsxsrc/components/ui/input-helper-message.tsxsrc/components/video.tsxsrc/config.tssrc/lib/localStorage-debug-listener.tssrc/utils/__tests__/data.tssrc/utils/subscription-token.tssrc/utils/url-compression.tstsconfig.json
💤 Files with no reviewable changes (7)
- src/utils/subscription-token.ts
- src/app/(app)/components/dev/dev-local-storage-view.tsx
- src/lib/localStorage-debug-listener.ts
- src/actions/subscribe-action.ts
- src/components/subscribe-input.tsx
- src/app/[locale]/about/components/landing-cta-toast.tsx
- src/app/confirm-subscription/page.tsx
🧰 Additional context used
🧠 Learnings (30)
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Integration tests must cover key user flows
Applied to files:
e2e/about-snapshot.test.tse2e/default-invoice-template/pdf.test.tse2e/buyer.test.tse2e/stripe-invoice-template/template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use meaningful assertions in tests
Applied to files:
e2e/about-snapshot.test.tse2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.tse2e/invoice-form.test.tse2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use descriptive test names
Applied to files:
e2e/about-snapshot.test.tse2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.tse2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Use Playwright for E2E testing
Applied to files:
e2e/about-snapshot.test.tse2e/default-invoice-template/pdf.test.tspackage.jsone2e/utils/extended-playwright-test.tsplaywright.config.ts
📚 Learning: 2025-07-20T21:59:36.257Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-07-20T21:59:36.257Z
Learning: Applies to **/*.tsx : Show generic user-friendly fallback UI for unexpected errors
Applied to files:
src/app/(app)/error.tsxsrc/app/(app)/components/invoice-pdf-preview/desktop-pdf-viewer.tsxsrc/app/(app)/components/invoice-pdf-preview/mobile-pdf-viewer.tsx
📚 Learning: 2025-07-20T21:59:36.257Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-07-20T21:59:36.257Z
Learning: Applies to **/*.tsx : Display user-friendly messages for expected errors
Applied to files:
src/app/(app)/error.tsx
📚 Learning: 2025-07-20T21:59:36.257Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-07-20T21:59:36.257Z
Learning: Applies to **/*.tsx : Use error boundaries for unexpected errors
Applied to files:
src/app/(app)/error.tsx
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Test error cases
Applied to files:
src/app/(app)/error.tsxe2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.tse2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T21:59:36.257Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-07-20T21:59:36.257Z
Learning: Applies to **/*.tsx : Wrap React components with ErrorBoundary and provide a FallbackComponent for error handling
Applied to files:
src/app/(app)/error.tsx
📚 Learning: 2025-07-20T21:59:36.257Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-07-20T21:59:36.257Z
Learning: Applies to **/*.tsx : Catch unexpected errors (runtime errors, network issues) with error boundaries
Applied to files:
src/app/(app)/error.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript for all code; prefer interfaces over types.
Applied to files:
tsconfig.json.vscode/settings.json
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Use Shadcn UI, Radix, and Tailwind for components and styling.
Applied to files:
src/app/[locale]/about/page.tsx.vscode/settings.jsonsrc/app/(app)/components/invoice-form/sections/general-information.tsxpackage.json
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Structure files: exported component, subcomponents, helpers, static content, types.
Applied to files:
src/app/[locale]/about/page.tsx
📚 Learning: 2025-07-20T21:59:36.257Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-07-20T21:59:36.257Z
Learning: Applies to **/*.{ts,tsx} : Use Zod schemas and z.infer for form validation and type inference in TypeScript
Applied to files:
.vscode/settings.jsonsrc/app/schema/index.ts
📚 Learning: 2025-07-20T21:59:36.257Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-07-20T21:59:36.257Z
Learning: Applies to **/*.{ts,tsx} : Use Zod for form validation and type inference
Applied to files:
.vscode/settings.jsonsrc/components/seller-dialog.tsxsrc/components/buyer-dialog.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Favor named exports for components.
Applied to files:
.vscode/settings.jsonsrc/app/(app)/components/invoice-form/sections/buyer-information.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.{ts,tsx} : Use `console.log({})` syntax for debugging.
Applied to files:
.vscode/settings.json
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use test data factories in tests
Applied to files:
e2e/generate-invoice-link.test.tse2e/default-invoice-template/pdf.test.tse2e/buyer.test.tse2e/stripe-invoice-template/template.test.tssrc/utils/__tests__/data.tse2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Follow AAA pattern (Arrange, Act, Assert) in tests
Applied to files:
e2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.tse2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use MSW for API mocking in tests
Applied to files:
e2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.tssrc/utils/__tests__/data.tse2e/utils/extended-playwright-test.tsplaywright.config.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Mock external dependencies in tests
Applied to files:
e2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.tse2e/utils/extended-playwright-test.tsplaywright.config.ts
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Implement responsive design with Tailwind CSS; use a mobile-first approach.
Applied to files:
src/app/(app)/components/index.tsx
📚 Learning: 2025-07-20T21:59:36.257Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-07-20T21:59:36.257Z
Learning: Applies to **/*.tsx : Implement client-side validation for immediate feedback
Applied to files:
src/app/(app)/components/invoice-form/sections/buyer-information.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Use functional components with TypeScript interfaces.
Applied to files:
src/app/(app)/components/invoice-form/sections/buyer-information.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Use pnpm.
Applied to files:
package.json
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use Vitest for unit and integration tests
Applied to files:
package.json
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Keep tests isolated
Applied to files:
e2e/utils/extended-playwright-test.tsplaywright.config.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Group related tests in describe blocks
Applied to files:
e2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Test cases should use it('should do something specific', () => {})
Applied to files:
e2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T21:59:36.257Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-07-20T21:59:36.257Z
Learning: Applies to **/*.{ts,tsx} : Define reusable validation schemas
Applied to files:
src/app/schema/index.ts
🧬 Code graph analysis (29)
e2e/about-snapshot.test.ts (1)
e2e/utils/extended-playwright-test.ts (1)
test(17-33)
src/app/(app)/error.tsx (1)
src/config.ts (1)
BUG_REPORT_URL(22-23)
src/app/(app)/components/invoice-pdf-stripe-template/stripe-footer.tsx (1)
src/config.ts (1)
PROD_WEBSITE_URL(16-16)
src/components/footer.tsx (1)
src/components/etc/project-logo.tsx (1)
ProjectLogo(3-26)
src/app/(app)/components/cta-toasts/index.tsx (2)
src/app/[locale]/about/components/landing-cta-toast.tsx (3)
showCTAToast(11-24)showRandomCTAToast(12-16)clearTimeout(21-23)src/app/(app)/contexts/cta-toast-context.tsx (1)
CTAToastContextValue(5-8)
src/app/(app)/components/invoice-form/sections/invoice-items.tsx (2)
src/app/(app)/components/invoice-form/common.tsx (1)
ErrorMessage(3-5)src/components/ui/input-helper-message.tsx (1)
InputHelperMessage(3-17)
e2e/generate-invoice-link.test.ts (1)
src/app/schema/index.ts (2)
SellerData(533-533)BuyerData(574-574)
messages/es.json (1)
src/app/(app)/utils/invoice-number-breaking-change.ts (1)
handleInvoiceNumberBreakingChange(18-66)
src/app/(app)/utils/get-app-metadata.tsx (1)
src/app/schema/index.ts (5)
APP_VERSION(345-345)SCHEMA_VERSION(352-352)Metadata(803-803)METADATA_LOCAL_STORAGE_KEY(805-805)metadataSchema(783-801)
e2e/default-invoice-template/pdf.test.ts (2)
e2e/utils/extended-playwright-test.ts (1)
test(17-33)e2e/stripe-invoice-template/share-logic.test.ts (1)
test(5-372)
src/app/(app)/components/invoice-pdf-preview/desktop-pdf-viewer.tsx (1)
src/config.ts (1)
BUG_REPORT_URL(22-23)
src/app/(app)/components/invoice-pdf-template/invoice-footer.tsx (1)
src/config.ts (1)
PROD_WEBSITE_URL(16-16)
src/components/seller-dialog.tsx (1)
src/components/ui/input-helper-message.tsx (1)
InputHelperMessage(3-17)
src/utils/url-compression.ts (1)
src/utils/__tests__/url-compression.test.ts (3)
it(525-628)key(613-617)key(557-561)
e2e/buyer.test.ts (1)
src/app/(app)/components/invoice-form/sections/buyer-information.tsx (1)
BUYER_TOOLTIP_CONTENT(24-25)
messages/pt.json (1)
src/app/(app)/utils/__tests__/invoice-number-breaking-change.test.ts (1)
it(18-109)
src/app/(app)/components/index.tsx (2)
src/app/schema/index.ts (2)
MOBILE_TABS_VALUES(775-775)DEFAULT_MOBILE_TAB(776-776)src/app/(app)/utils/get-app-metadata.tsx (2)
getAppMetadata(30-58)updateAppMetadata(81-116)
src/components/etc/project-logo.tsx (1)
src/components/etc/github-logo.tsx (1)
GithubIcon(3-18)
src/components/buyer-dialog.tsx (5)
src/components/ui/tooltip.tsx (1)
CustomTooltip(94-94)src/components/ui/label.tsx (1)
Label(24-24)src/components/ui/form.tsx (4)
FormItem(176-176)FormLabel(177-177)FormControl(178-178)FormMessage(180-180)src/components/ui/input.tsx (1)
Input(29-29)src/components/ui/input-helper-message.tsx (1)
InputHelperMessage(3-17)
src/app/(app)/components/invoice-form/sections/buyer-information.tsx (7)
src/app/(app)/components/invoice-form/common.tsx (1)
ErrorMessage(3-5)src/components/ui/switch.tsx (1)
Switch(34-34)src/components/ui/tooltip.tsx (1)
CustomTooltip(94-94)src/components/ui/label.tsx (1)
Label(24-24)src/components/label-with-edit-icon.tsx (1)
LabelWithEditIcon(14-56)src/components/ui/input.tsx (1)
Input(29-29)src/components/ui/input-helper-message.tsx (1)
InputHelperMessage(3-17)
src/app/constants/seo.ts (1)
src/config.ts (1)
STATIC_ASSETS_URL(8-8)
src/app/(app)/components/invoice-pdf-preview/mobile-pdf-viewer.tsx (1)
src/config.ts (1)
BUG_REPORT_URL(22-23)
src/components/ui/button-helper.tsx (2)
src/components/ui/button.tsx (2)
ButtonProps(38-42)props(48-61)src/components/subscribe-input.tsx (1)
SubmitButton(12-42)
e2e/seller.test.ts (2)
src/app/(app)/components/invoice-form/sections/seller-information.tsx (1)
SELLER_TOOLTIP_CONTENT(22-23)src/app/constants/index.ts (1)
DEFAULT_SELLER_DATA(30-49)
src/app/(app)/components/invoice-pdf-download-link.tsx (3)
src/lib/umami-analytics-track-event.tsx (1)
umamiTrackEvent(29-62)src/components/github-star-cta.tsx (1)
umamiTrackEvent(18-20)src/app/(app)/utils/__tests__/invoice-number-breaking-change.test.ts (1)
umamiTrackEvent(7-9)
e2e/invoice-form.test.ts (1)
src/app/(app)/utils/__tests__/invoice-number-breaking-change.test.ts (5)
it(18-109)it(342-371)it(173-265)input(268-290)input(19-43)
src/app/layout.tsx (1)
src/app/constants/seo.ts (1)
SOFTWARE_APPLICATION_JSONLD(122-133)
src/app/(app)/components/invoice-form/sections/seller-information.tsx (7)
src/app/(app)/components/invoice-form/common.tsx (1)
ErrorMessage(3-5)src/components/ui/switch.tsx (1)
Switch(34-34)src/components/ui/tooltip.tsx (1)
CustomTooltip(94-94)src/components/ui/label.tsx (1)
Label(24-24)src/components/label-with-edit-icon.tsx (1)
LabelWithEditIcon(14-56)src/components/ui/input.tsx (1)
Input(29-29)src/components/ui/input-helper-message.tsx (1)
InputHelperMessage(3-17)
src/app/schema/index.ts (2)
src/app/(app)/utils/invoice-number-breaking-change.ts (1)
handleInvoiceNumberBreakingChange(18-66)src/app/(app)/utils/__tests__/invoice-number-breaking-change.test.ts (2)
it(342-371)input(83-108)
🪛 ast-grep (0.40.3)
src/app/layout.tsx
[warning] 138-138: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 Biome (2.1.2)
src/app/layout.tsx
[error] 139-139: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
e2e/utils/extended-playwright-test.ts
[error] 20-20: Unexpected empty object pattern.
(lint/correctness/noEmptyPattern)
🪛 LanguageTool
TODO.md
[grammar] ~4-~4: Use a hyphen to join words.
Context: ... invoice form (better name) - [ ] Double check if we need to save to local storag...
(QB_NEW_EN_HYPHEN)
🪛 markdownlint-cli2 (0.18.1)
README.md
47-47: Bare URL used
(MD034, no-bare-urls)
| // @ts-expect-error - NODE_ENV is not defined in the environment variables | ||
| const isLocal = process.env.NODE_ENV === "local"; | ||
|
|
||
| const TIMEOUT = isLocal ? 35_000 : 80_000; // 80 seconds |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for NODE_ENV references and "local" value
rg -n 'NODE_ENV.*local' -g '!node_modules' -g '!playwright-output'Repository: VladSez/easy-invoice-pdf
Length of output: 141
🏁 Script executed:
#!/bin/bash
# More broadly search for NODE_ENV references
rg -n 'NODE_ENV' -g '!node_modules' -g '!playwright-output' | head -30Repository: VladSez/easy-invoice-pdf
Length of output: 340
🏁 Script executed:
#!/bin/bash
# Check for environment configuration files
fd -e '.env*' -e '*.json' -e '*.config.*' | grep -E '(env|config)' | head -20Repository: VladSez/easy-invoice-pdf
Length of output: 50
🏁 Script executed:
#!/bin/bash
# Read the playwright.config.ts file to understand the context
head -40 playwright.config.tsRepository: VladSez/easy-invoice-pdf
Length of output: 1382
🏁 Script executed:
#!/bin/bash
# Check if .env.local file exists
ls -la .env.local 2>/dev/null || echo "No .env.local file found"Repository: VladSez/easy-invoice-pdf
Length of output: 92
🏁 Script executed:
#!/bin/bash
# Search for any documentation or comments about NODE_ENV
rg -n 'NODE_ENV|environment' -g '!node_modules' -g '!dist' README.md .env* package.json 2>/dev/null | head -20Repository: VladSez/easy-invoice-pdf
Length of output: 50
🏁 Script executed:
#!/bin/bash
# Check for TypeScript environment type declarations
find . -name "*.d.ts" -o -name "env.ts" -o -name "types.ts" | xargs grep -l "NODE_ENV" 2>/dev/null || echo "No type definitions found for NODE_ENV"Repository: VladSez/easy-invoice-pdf
Length of output: 106
🏁 Script executed:
#!/bin/bash
# Check package.json for scripts that might set NODE_ENV
grep -A 5 '"scripts"' package.json | head -20Repository: VladSez/easy-invoice-pdf
Length of output: 254
Remove unreachable condition or document required environment setup.
The code checks for NODE_ENV === "local" to apply a 35-second timeout for local development, but .env.local doesn't exist and NODE_ENV is never set to "local" anywhere in the project. This means isLocal always evaluates to false, making the local timeout configuration unreachable dead code.
Either:
- Create
.env.localwithNODE_ENV=localand document this setup requirement for developers, or - Remove the unused
isLocalcondition and use a single timeout value
The @ts-expect-error suppression is a symptom of the deeper issue—process.env should be properly typed if custom environment variables are needed.
🤖 Prompt for AI Agents
In playwright.config.ts around lines 18 to 21, the isLocal branch (NODE_ENV ===
"local") is unreachable because .env.local and NODE_ENV=local are not provided;
either remove the conditional and use a single TIMEOUT value (keep the larger
80_000 or choose a single desired timeout) and delete the @ts-expect-error, or
add documentation and a .env.local file (and project bootstrap docs) that set
NODE_ENV=local and properly type custom env vars so process.env checks are
valid; update the file accordingly and remove the ts-expect-error once
environment typing is handled.
| // Always enable VAT field visibility for Stripe template (because we don't show Switches for items in Stripe template and we want to make sure the Tax column is visible in the PDF) | ||
| setValue("items.0.vatFieldIsVisible", true); |
There was a problem hiding this comment.
Hardcoded index items.0.vatFieldIsVisible may not cover all items.
When switching to Stripe template, only the first item's VAT visibility is set. If there are multiple items, the others won't have their visibility updated.
🔎 Consider updating all items
You may want to iterate over all items or use a different approach:
// Option: Set visibility for all items via a form-level field
// or iterate if needed🤖 Prompt for AI Agents
In src/app/(app)/components/invoice-form/sections/general-information.tsx around
lines 186-187, the code sets VAT visibility only for items.0 which only affects
the first item; update the logic to set VAT visibility for every item instead of
a hardcoded index — e.g., read the current items array (via getValues or watch),
loop through each item index and call setValue(`items.${i}.vatFieldIsVisible`,
true) for each, or alternatively introduce a form-level flag (e.g.,
itemsVatFieldIsVisible) and set that instead and update item render logic to
respect the form-level flag so all items show VAT when switching to the Stripe
template.
| <Text style={styles.tableCellBold}> | ||
| {t.invoiceItemsTable.vatAmount} | ||
| </Text> | ||
| <Text style={styles.tableCellBold}>{taxLabelText} Amount</Text> |
There was a problem hiding this comment.
Hardcoded "Amount" may break i18n consistency.
The header uses ${taxLabelText} Amount where "Amount" is hardcoded in English. Since the invoice supports 10+ languages via TRANSLATIONS, this could result in mixed-language headers (e.g., "MwSt. Amount" for German invoices).
Consider using the existing translation system for consistency:
- <Text style={styles.tableCellBold}>{taxLabelText} Amount</Text>
+ <Text style={styles.tableCellBold}>{taxLabelText} {t.invoiceItemsTable.amount}</Text>Or introduce a dedicated translation key like taxAmount if the semantics differ from the existing "amount" field.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/app/(app)/components/invoice-pdf-template/invoice-items-table.tsx around
line 110, the header uses a hardcoded English word "Amount" concatenated with
taxLabelText which breaks i18n; replace the literal with the translated string
from the existing translation map (e.g., use TRANSLATIONS[locale].amount) or, if
semantics differ, add a new translation key like taxAmount to the TRANSLATIONS
object and use that key so the header reads `${taxLabelText}
${translatedTaxAmount}` and remains correct for all locales.
| @@ -14,6 +14,7 @@ export function InvoiceVATSummaryTable({ | |||
| }) { | |||
| const language = invoiceData.language; | |||
| const t = TRANSLATIONS[language]; | |||
| const taxLabelText = invoiceData.taxLabelText || "VAT"; | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check the file structure and context around line 17
cat -n src/app/\(app\)/components/invoice-pdf-template/invoice-vat-summary-table.tsx | head -30Repository: VladSez/easy-invoice-pdf
Length of output: 1285
🏁 Script executed:
# Search for taxLabelText initialization patterns
rg -n 'taxLabelText' --type=ts --type=tsx -C2Repository: VladSez/easy-invoice-pdf
Length of output: 95
🏁 Script executed:
# Search for invoiceData type definition
rg -n 'invoiceData\s*[:=]' --type=ts --type=tsx -A5 | head -50Repository: VladSez/easy-invoice-pdf
Length of output: 95
🏁 Script executed:
# Look for language-related properties in invoice data
rg -n 'language' --type=ts --type=tsx -C2 | head -50Repository: VladSez/easy-invoice-pdf
Length of output: 95
🏁 Script executed:
# Find and read the translations file
fd translations -type f | head -5Repository: VladSez/easy-invoice-pdf
Length of output: 238
🏁 Script executed:
# Search for schema definition
fd schema -type f | head -10Repository: VladSez/easy-invoice-pdf
Length of output: 238
🏁 Script executed:
# Look for how taxLabelText is used in other places
rg 'taxLabelText' --no-require-git -C3Repository: VladSez/easy-invoice-pdf
Length of output: 20837
🏁 Script executed:
# Check the translations structure
rg 'TRANSLATIONS\s*=' --no-require-git -A20Repository: VladSez/easy-invoice-pdf
Length of output: 1264
🏁 Script executed:
# Get the full translations file to see all languages
cat src/app/schema/translations.ts | head -100Repository: VladSez/easy-invoice-pdf
Length of output: 2333
🏁 Script executed:
# Check the schema definition for taxLabelText
cat src/app/schema/index.ts | grep -A10 "taxLabelText"Repository: VladSez/easy-invoice-pdf
Length of output: 420
🏁 Script executed:
# Check if taxLabelText ever comes undefined or if it always has a default
rg 'taxLabelText' src/app/constants/index.ts -B5 -A5Repository: VladSez/easy-invoice-pdf
Length of output: 292
🏁 Script executed:
# Get the complete translations file
cat src/app/schema/translations.tsRepository: VladSez/easy-invoice-pdf
Length of output: 22361
🏁 Script executed:
# Also check how other components handle language defaults
rg 'TRANSLATIONS\[language\]' --no-require-git -B2 -A2Repository: VladSez/easy-invoice-pdf
Length of output: 8579
Use localized translation fallback for tax label instead of hardcoded "VAT".
The hardcoded fallback "VAT" is in English, but localized alternatives already exist in the t.invoiceItemsTable.vat translations for all supported languages (German: "MwSt", Russian: "НДС", French: "TVA", etc.).
Change line 17 to:
const taxLabelText = invoiceData.taxLabelText || t.invoiceItemsTable.vat;
This pattern also needs to be fixed in:
src/app/(app)/components/invoice-pdf-stripe-template/stripe-totals.tsxsrc/app/(app)/components/invoice-pdf-stripe-template/stripe-items-table.tsxsrc/app/(app)/components/invoice-pdf-template/invoice-items-table.tsxsrc/app/(app)/components/invoice-form/index.tsx
🤖 Prompt for AI Agents
In src/app/(app)/components/invoice-pdf-template/invoice-vat-summary-table.tsx
around line 17, the code uses a hardcoded English fallback "VAT" for
taxLabelText; replace that fallback with the localized translation
t.invoiceItemsTable.vat (i.e., set taxLabelText = invoiceData.taxLabelText ||
t.invoiceItemsTable.vat). Apply the same change pattern to the other listed
files so any fallback "VAT" is replaced by t.invoiceItemsTable.vat:
src/app/(app)/components/invoice-pdf-stripe-template/stripe-totals.tsx,
src/app/(app)/components/invoice-pdf-stripe-template/stripe-items-table.tsx,
src/app/(app)/components/invoice-pdf-template/invoice-items-table.tsx, and
src/app/(app)/components/invoice-form/index.tsx.
* Changed import of 'path' to 'node:path' in Playwright configuration for better compatibility. * Updated GitHub Actions workflow to run e2e tests on macOS for consistent snapshot generation. * Simplified screenshot assertions in various test files by removing unnecessary path joins. * Added new snapshot images for various invoice and template tests to ensure visual consistency. * Removed commented-out code related to tax field visibility in invoice items for cleaner codebase. * Introduced new type for mobile tab values in schema to enhance type safety.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
src/app/(app)/components/index.tsx (1)
7-7: Add missing dayjs locale import.The
.locale("en")call on line 124 requires the locale to be explicitly imported. Addimport "dayjs/locale/en";after the dayjs import to ensure the locale is properly loaded.🔎 Proposed fix
import dayjs from "dayjs"; +import "dayjs/locale/en";playwright.config.ts (1)
18-21: Dead code:isLocalcondition is unreachable.The
NODE_ENV === "local"check is never true because.env.localdoesn't exist in the repository andNODE_ENVis never set to "local" anywhere in the codebase. This makes the local timeout (35s) unreachable, and the@ts-expect-errorsuppresses a legitimate type issue.Either document and provide the required
.env.localsetup, or simplify to a singleTIMEOUTvalue.
🧹 Nitpick comments (6)
src/app/(app)/components/invoice-form/sections/invoice-items.tsx (1)
478-482: Good semantic grouping with fieldset.The Tax Settings fieldset provides clear organization of tax-related controls. The structure is valid and improves readability.
For future maintainability, consider extracting the Tax Settings section into a separate component (e.g.,
TaxSettingsSection) to reduce the complexity of this file.e2e/utils/extended-playwright-test.ts (1)
27-27: Unnecessary ESLint disable:useis not a React Hook.The
react-hooks/rules-of-hooksrule shouldn't apply here—useis a Playwright fixture callback, not a React Hook. The disable comment may be masking a misconfigured ESLint rule or plugin conflict.Consider removing the disable and verifying your ESLint configuration, or switching to a more specific suppression if the warning persists.
e2e/stripe-invoice-template/template.test.ts (2)
810-810: Flakiness risk:waitForTimeoutdepends on arbitrary delay.The 1-second timeout at line 810 (and line 936) assumes PDF rendering completes within that window. On slower CI runners or under load, this could produce intermittent failures.
Consider polling for a specific condition (e.g., PDF canvas ready, page title set) instead of a fixed delay.
🔎 Alternative approach using `waitForLoadState`
- // sometimes there's a blank screen without this - // eslint-disable-next-line playwright/no-wait-for-timeout - await page.waitForTimeout(1000); + await page.waitForLoadState('networkidle');
806-806: Cross-platform risk:file://URLs with absolute paths.Using
file://${absolutePath}works on macOS/Linux but can behave unexpectedly on Windows (drive letters, backslashes). Since the CI now runs on macOS, this is acceptable, but document the OS dependency if developers run tests locally on Windows.e2e/default-invoice-template/pdf.test.ts (2)
29-62: Consider extracting the PDF download-and-verify pattern to a helper function.This pattern repeats 8+ times throughout the file:
const [download] = await Promise.all([...]); const suggestedFilename = download.suggestedFilename(); const pdfFilePath = path.join(downloadDir, `${browserName}-...`); await download.saveAs(pdfFilePath); // ... file existence check, viewport, navigation, wait, screenshotExtracting this to a helper would reduce duplication and make future changes easier to maintain.
🔎 Proposed helper function
Add this helper at the top of the file or in a shared utils module:
async function downloadAndVerifyPdf( page: Page, downloadButton: Locator, options: { downloadDir: string; browserName: string; filenamePrefix?: string; screenshotName: string; } ) { const [download] = await Promise.all([ page.waitForEvent("download"), downloadButton.click(), ]); const suggestedFilename = download.suggestedFilename(); const prefix = options.filenamePrefix ? `${options.browserName}-${options.filenamePrefix}-` : `${options.browserName}-`; const pdfFilePath = path.join(options.downloadDir, `${prefix}${suggestedFilename}`); await download.saveAs(pdfFilePath); const absolutePath = path.resolve(pdfFilePath); await expect.poll(() => fs.existsSync(absolutePath)).toBe(true); await page.setViewportSize({ width: 1100, height: 1185 }); await page.goto(`file://${absolutePath}`); // eslint-disable-next-line playwright/no-wait-for-timeout await page.waitForTimeout(1000); await expect(page).toHaveScreenshot(options.screenshotName); }Then usage becomes:
await downloadAndVerifyPdf(page, downloadPdfEnglishButton, { downloadDir, browserName, screenshotName: `downloads-PDF-in-English.png`, });
58-61: Consider more deterministic wait conditions instead of fixed timeout for PDF viewer rendering.The 1000ms timeout waits for the browser's native PDF viewer to render the PDF file. While the concern about flakiness is valid, for local file:// PDF viewing, more deterministic alternatives may be limited. If investigating improvements, test whether
page.waitForLoadState('load')or waiting for a specific element in the PDF viewer UI (e.g., the canvas or toolbar to appear) provides reliable results across browsers. The current pragmatic solution with documentation is acceptable if alternatives prove incompatible with PDF viewer behavior.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (36)
e2e/about-snapshot.test.ts-snapshots/about-page-english-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/about-snapshot.test.ts-snapshots/about-page-english-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/about-snapshot.test.ts-snapshots/about-page-english-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/completes-full-invoice-flow-on-mobile-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/completes-full-invoice-flow-on-mobile-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/completes-full-invoice-flow-on-mobile-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/completes-full-invoice-flow-on-mobile-stripe-template-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/completes-full-invoice-flow-on-mobile-stripe-template-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/completes-full-invoice-flow-on-mobile-stripe-template-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-English-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-English-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-English-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-Polish-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-Polish-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-Polish-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-Polish-stripe-template-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-Polish-stripe-template-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/downloads-PDF-in-Polish-stripe-template-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/should-display-and-persist-invoice-number-in-different-languages-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/should-display-and-persist-invoice-number-in-different-languages-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/should-display-and-persist-invoice-number-in-different-languages-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/should-display-and-persist-invoice-number-in-different-languages-stripe-template-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/should-display-and-persist-invoice-number-in-different-languages-stripe-template-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/should-display-and-persist-invoice-number-in-different-languages-stripe-template-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/update-pdf-when-invoice-data-changes-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/update-pdf-when-invoice-data-changes-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/update-pdf-when-invoice-data-changes-chromium-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/update-pdf-when-invoice-data-changes-stripe-template-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/update-pdf-when-invoice-data-changes-stripe-template-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/default-invoice-template/pdf.test.ts-snapshots/update-pdf-when-invoice-data-changes-stripe-template-chromium-darwin.pngis excluded by!**/*.pnge2e/stripe-invoice-template/template.test.ts-snapshots/automatically-enables-VAT-field-visibility-and-sets-date-format-when-switching-to-Stripe-template-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/stripe-invoice-template/template.test.ts-snapshots/automatically-enables-VAT-field-visibility-and-sets-date-format-when-switching-to-Stripe-template-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/stripe-invoice-template/template.test.ts-snapshots/automatically-enables-VAT-field-visibility-and-sets-date-format-when-switching-to-Stripe-template-chromium-darwin.pngis excluded by!**/*.pnge2e/stripe-invoice-template/template.test.ts-snapshots/pdf-with-logo-and-payment-url-when-using-stripe-template-Mobile-Chrome-darwin.pngis excluded by!**/*.pnge2e/stripe-invoice-template/template.test.ts-snapshots/pdf-with-logo-and-payment-url-when-using-stripe-template-Mobile-Safari-darwin.pngis excluded by!**/*.pnge2e/stripe-invoice-template/template.test.ts-snapshots/pdf-with-logo-and-payment-url-when-using-stripe-template-chromium-darwin.pngis excluded by!**/*.png
📒 Files selected for processing (9)
.github/workflows/e2e.ymle2e/about-snapshot.test.tse2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.tse2e/utils/extended-playwright-test.tsplaywright.config.tssrc/app/(app)/components/index.tsxsrc/app/(app)/components/invoice-form/sections/invoice-items.tsxsrc/app/schema/index.ts
🧰 Additional context used
🧠 Learnings (14)
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Use Playwright for E2E testing
Applied to files:
.github/workflows/e2e.ymlplaywright.config.tse2e/default-invoice-template/pdf.test.tse2e/utils/extended-playwright-test.tse2e/about-snapshot.test.ts
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Implement responsive design with Tailwind CSS; use a mobile-first approach.
Applied to files:
src/app/(app)/components/index.tsx
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Keep tests isolated
Applied to files:
playwright.config.tse2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use MSW for API mocking in tests
Applied to files:
playwright.config.tse2e/utils/extended-playwright-test.tse2e/stripe-invoice-template/template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Mock external dependencies in tests
Applied to files:
playwright.config.tse2e/utils/extended-playwright-test.tse2e/stripe-invoice-template/template.test.ts
📚 Learning: 2025-07-20T21:59:36.257Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-07-20T21:59:36.257Z
Learning: Applies to **/*.{ts,tsx} : Define reusable validation schemas
Applied to files:
src/app/schema/index.ts
📚 Learning: 2025-07-20T21:59:36.257Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-07-20T21:59:36.257Z
Learning: Applies to **/*.{ts,tsx} : Use Zod schemas and z.infer for form validation and type inference in TypeScript
Applied to files:
src/app/schema/index.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Integration tests must cover key user flows
Applied to files:
e2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.tse2e/about-snapshot.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use meaningful assertions in tests
Applied to files:
e2e/default-invoice-template/pdf.test.tse2e/utils/extended-playwright-test.tse2e/stripe-invoice-template/template.test.tse2e/about-snapshot.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Follow AAA pattern (Arrange, Act, Assert) in tests
Applied to files:
e2e/default-invoice-template/pdf.test.tse2e/utils/extended-playwright-test.tse2e/stripe-invoice-template/template.test.tse2e/about-snapshot.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use descriptive test names
Applied to files:
e2e/default-invoice-template/pdf.test.tse2e/utils/extended-playwright-test.tse2e/stripe-invoice-template/template.test.tse2e/about-snapshot.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Test error cases
Applied to files:
e2e/default-invoice-template/pdf.test.tse2e/utils/extended-playwright-test.tse2e/stripe-invoice-template/template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use test data factories in tests
Applied to files:
e2e/utils/extended-playwright-test.tse2e/stripe-invoice-template/template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Test cases should use it('should do something specific', () => {})
Applied to files:
e2e/utils/extended-playwright-test.tse2e/about-snapshot.test.ts
🧬 Code graph analysis (4)
src/app/(app)/components/index.tsx (2)
src/app/schema/index.ts (3)
MOBILE_TABS_VALUES(775-775)DEFAULT_MOBILE_TAB(776-776)MobileTabsValues(778-778)src/app/(app)/utils/get-app-metadata.tsx (2)
getAppMetadata(30-58)updateAppMetadata(81-116)
e2e/default-invoice-template/pdf.test.ts (1)
e2e/utils/extended-playwright-test.ts (1)
test(17-33)
src/app/(app)/components/invoice-form/sections/invoice-items.tsx (4)
src/components/ui/label.tsx (1)
Label(24-24)src/components/ui/input.tsx (1)
Input(29-29)src/app/(app)/components/invoice-form/common.tsx (1)
ErrorMessage(3-5)src/components/ui/input-helper-message.tsx (1)
InputHelperMessage(3-17)
e2e/about-snapshot.test.ts (1)
e2e/utils/extended-playwright-test.ts (1)
test(17-33)
🔇 Additional comments (31)
src/app/schema/index.ts (7)
341-352: LGTM! Version constants added.The APP_VERSION and SCHEMA_VERSION constants are well-documented and provide a foundation for version management and future schema migrations.
405-459: Well-designed flexible VAT validation.The preprocessing approach elegantly handles both numeric (0-100) and text-based VAT values (e.g., "NP", "OO"). The logic correctly trims input, handles null/undefined, and attempts numeric conversion before falling back to string validation.
478-531: LGTM! Seller schema refactored with customizable tax label.The refactoring to
z.object()structure is clean, and the newvatNoLabelTextfield with sensible defaults and validation enables customizable tax labels as intended.
535-572: LGTM! Buyer schema refactored consistently.The buyer schema changes mirror the seller schema refactoring, maintaining consistency across entity types.
634-648: LGTM! Tax label customization added.The
taxLabelTextfield provides the intended flexibility for international tax terminology (VAT, GST, IVA, Sales Tax, etc.) with appropriate validation and a sensible default.
775-778: LGTM! Mobile tab constants defined.The mobile tab values and default are clearly defined with proper type inference.
780-807: LGTM! Metadata schema well-structured.The metadata schema appropriately captures app versioning, timestamps, and user state (mobile tab) with sensible defaults and validation.
src/app/(app)/components/index.tsx (8)
98-99: LGTM! Tab constants properly derived.The tab constants correctly reference the centralized MOBILE_TABS_VALUES array, improving maintainability.
120-129: LGTM! Metadata retrieval and formatting handled correctly.The code properly retrieves app metadata, safely formats the timestamp with null checks, and provides a sensible fallback for the mobile tab default.
135-148: LGTM! Mobile tab persistence correctly implemented.The tab component properly initializes from metadata and persists the last visited tab. The type assertion to
MobileTabsValueson line 139 correctly uses the union type (addressing the previous review concern).
165-165: LGTM! Mobile container heights increased.The height increase from 480px to 520px provides more viewing space for both form and preview on mobile devices.
Also applies to: 174-174
240-245: LGTM! Mobile timestamp display implemented correctly.The timestamp display includes proper null checks and smooth fade-in animations.
246-258: LGTM! Mobile credits section added.The credits are properly implemented with secure external link handling (
noopener noreferrer).
272-282: LGTM! Desktop credits section added.The credits section mirrors the mobile implementation with consistent styling and secure link attributes.
284-290: LGTM! Desktop timestamp display positioned correctly.The timestamp is properly positioned using absolute positioning with the parent container set to relative (line 284), ensuring correct layout.
src/app/(app)/components/invoice-form/sections/invoice-items.tsx (3)
44-44: LGTM! Clean API extension.The addition of
taxLabelTextto the component's props is well-typed and integrates cleanly with the existing interface.Also applies to: 56-56
84-88: LGTM! Proper use of dynamic tax label.The label correctly interpolates
taxLabelTextto display customizable tax terminology.
505-505: LGTM! Consistent dynamic tax label usage.All labels, tooltips, and helper messages correctly use the dynamic
taxLabelTextprop, enabling proper customization of tax terminology throughout the UI.Also applies to: 539-539, 641-641, 668-668, 700-702, 768-770
.github/workflows/e2e.yml (1)
12-13: macOS runner choice is well-justified.The switch to
macos-latestaligns with local snapshot generation and prevents cross-platform pixel differences. This is the correct approach for maintaining consistent visual regression tests.Note: macOS runners incur higher costs than Linux, but the reliability benefit outweighs the expense for snapshot testing.
playwright.config.ts (1)
66-66: LGTM: Explicit Chromium channel improves stability.Specifying
channel: "chromium"ensures tests run against the bundled Chromium rather than any installed Chrome/Edge, reducing environment variability.e2e/about-snapshot.test.ts (1)
15-21: Excellent: Video state normalization for snapshot stability.Pausing and resetting video playback ensures deterministic snapshots by eliminating frame-dependent visual differences. This is a best practice for visual regression testing.
e2e/utils/extended-playwright-test.ts (1)
20-32: Solid isolation pattern with automatic cleanup.The fixture correctly creates a unique temp directory per test and guarantees cleanup via the
finallyblock, preventing disk space leaks even when tests fail.e2e/stripe-invoice-template/template.test.ts (2)
16-17: Good: Fixed system time ensures deterministic test data.Setting a fixed date via
page.clock.setSystemTime()stabilizes invoice numbers, date formatting, and any time-dependent logic across test runs.
852-941: Comprehensive end-to-end coverage for Stripe template with assets.This test validates the complete flow: logo upload, payment URL entry, localStorage persistence, PDF generation, and visual verification. The screenshot-based approach ensures the PDF rendering matches expectations.
e2e/default-invoice-template/pdf.test.ts (7)
1-7: LGTM on imports and fixture usage!The switch to the custom extended Playwright fixture is a good pattern that provides isolated temporary download directories per test, preventing cross-test pollution. The fixture handles cleanup automatically in its
finallyblock. Based on learnings, this aligns with proper Playwright e2e testing practices.
9-15: Good use of fixed system time for deterministic tests.Setting
page.clock.setSystemTimeensures invoice numbers and dates remain consistent across test runs, which is essential for screenshot-based verification.
65-128: Good test coverage for form interactions and calculations.The test properly verifies:
- Language switching to Polish
- Invoice item manipulation (name, quantity, price, VAT)
- Adding a new invoice item
- Total calculation verification (
3,300.00)This follows the AAA pattern and includes meaningful assertions as per testing guidelines.
171-180: Template switching verification is thorough.Good practice to verify both the URL change (
waitForURL) and the select element value after switching templates.
443-453: Excellent mobile-specific test coverage.This test properly covers:
- Mobile viewport simulation
- Tab-based navigation (Edit/Preview)
- Form data persistence across navigation
- French localization
This aligns with the learning that integration tests must cover key user flows.
581-586: Good practice: restoring mobile viewport after PDF verification.The viewport reset to mobile dimensions after navigating back ensures the subsequent mobile-specific assertions (tab navigation, form persistence) work correctly.
707-764: Thorough language and persistence testing.The test covers important scenarios:
- Invoice number label translation per language
- "Switch to default format" warning UI
- Data persistence after page reload
- Currency persistence across template changes
Using
TRANSLATIONS.pl.invoiceNumberfor expected values ensures tests stay in sync with the actual translations.
| checked={value} | ||
| onCheckedChange={onChange} | ||
| className="h-5 w-8 [&_span]:size-4 [&_span]:data-[state=checked]:translate-x-3 rtl:[&_span]:data-[state=checked]:-translate-x-3" | ||
| // aria-label={`Show/hide the 'Name of Goods/Service' Column in the PDF for item ${index + 1}`} |
There was a problem hiding this comment.
Remove commented-out aria-label attributes.
These commented-out attributes are remnants from refactoring and should be deleted. Version control preserves history if they're needed later.
🔎 Proposed cleanup
className="h-5 w-8 [&_span]:size-4 [&_span]:data-[state=checked]:translate-x-3 rtl:[&_span]:data-[state=checked]:-translate-x-3"
- // aria-label={`Show/hide the 'Name of Goods/Service' Column in the PDF for item ${index + 1}`}
/> className="h-5 w-8 [&_span]:size-4 [&_span]:data-[state=checked]:translate-x-3 rtl:[&_span]:data-[state=checked]:-translate-x-3"
- // aria-label={`Show/hide the 'Type of GTU' Column in the PDF for item ${index + 1}`}
/> className="h-5 w-8 [&_span]:size-4 [&_span]:data-[state=checked]:translate-x-3 rtl:[&_span]:data-[state=checked]:-translate-x-3"
- // aria-label={`Show/hide the 'Amount' Column in the PDF for item ${index + 1}`}
/>Also applies to: 224-224, 283-283
🤖 Prompt for AI Agents
In src/app/(app)/components/invoice-form/sections/invoice-items.tsx around lines
165, 224, and 283 there are commented-out aria-label attributes left from
refactoring; remove those commented lines entirely (delete the commented
aria-label snippets at those exact line locations) so the file contains no
leftover commented accessibility attributes—rely on VCS history if the old lines
are needed later.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
e2e/utils/extended-playwright-test.ts (1)
20-20: Replace empty object pattern with underscore.The first parameter uses an empty object pattern
{}which is flagged by the linter. Replace it with_to indicate the parameter is intentionally unused.🔎 Proposed fix
- downloadDir: async ({}, use) => { + downloadDir: async (_, use) => {
🧹 Nitpick comments (1)
e2e/utils/extended-playwright-test.ts (1)
27-28: Remove inappropriate eslint-disable comment.The
usecallback is a Playwright fixture function, not a React hook. Thereact-hooks/rules-of-hooksdisable comment is misleading and unnecessary.🔎 Proposed fix
try { // Make the directory path available to the test - // eslint-disable-next-line react-hooks/rules-of-hooks await use(downloadDir); } finally {
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
e2e/utils/extended-playwright-test.ts
🧰 Additional context used
🧠 Learnings (11)
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Use Playwright for E2E testing
Applied to files:
e2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Keep tests isolated
Applied to files:
e2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use descriptive test names
Applied to files:
e2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Mock external dependencies in tests
Applied to files:
e2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use meaningful assertions in tests
Applied to files:
e2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use test data factories in tests
Applied to files:
e2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use MSW for API mocking in tests
Applied to files:
e2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Follow AAA pattern (Arrange, Act, Assert) in tests
Applied to files:
e2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Test error cases
Applied to files:
e2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Integration tests must cover key user flows
Applied to files:
e2e/utils/extended-playwright-test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Test cases should use it('should do something specific', () => {})
Applied to files:
e2e/utils/extended-playwright-test.ts
🪛 Biome (2.1.2)
e2e/utils/extended-playwright-test.ts
[error] 20-20: Unexpected empty object pattern.
(lint/correctness/noEmptyPattern)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run e2e tests via Vercel deployment
🔇 Additional comments (2)
e2e/utils/extended-playwright-test.ts (2)
1-4: LGTM! Clean imports with best practices.The imports are well-organized, using the
node:prefix for built-in modules as recommended.
17-33: Solid fixture implementation following Playwright best practices.The
downloadDirfixture correctly:
- Creates an isolated temporary directory per test
- Guarantees cleanup via try/finally block
- Uses appropriate rmSync options (recursive, force) for reliable cleanup
The export naming convention is also correctly preserved for ESLint plugin compatibility.
* Modified dotenv configuration to load environment variables quietly. * Updated GitHub Actions workflow to use macOS 14 for improved compatibility with Playwright snapshot generation.
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.github/workflows/e2e.ymlplaywright.config.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- playwright.config.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Use Playwright for E2E testing
Applied to files:
.github/workflows/e2e.yml
| # IMPORTANT: we use "macos-14" due to the playwright snapshot updates issues on mac os vs linux (locally I use macos to generate snapshots and it's required to have the same OS for the CI) | ||
| runs-on: macos-14 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/e2e.ymlRepository: VladSez/easy-invoice-pdf
Length of output: 5415
🏁 Script executed:
fd playwright.config -type fRepository: VladSez/easy-invoice-pdf
Length of output: 238
🏁 Script executed:
fd "\.e2e\.|\.spec\." -type f | head -20Repository: VladSez/easy-invoice-pdf
Length of output: 238
🌐 Web query:
Playwright cross-platform screenshot testing best practices 2025
💡 Result:
Here are concise, current best practices for cross‑platform Playwright screenshot/visual testing (2025):
-
Run screenshots in a consistent environment (same OS, browser version, headless/headful mode, GPU, scaling). Baselines must be generated where CI will run. [1][2]
-
Scope screenshots to stable elements when possible (locator.screenshot) instead of full‑page captures to reduce noise. [1][6]
-
Mask or remove dynamic content (timestamps, ads, user data) and freeze animations/transitions before capture (CSS injection or hide selectors). [2][3]
-
Use stable, semantic locators (getByRole, data-testid) to reduce flakiness in element selection. [4]
-
Tune comparison tolerance: set maxDiffPixels / thresholds and per‑test options rather than brittle exact matches. [1][5]
-
Version‑control snapshot files and review updates carefully; update baselines only after intentional UI changes (use --update-snapshots). [1][6]
-
Run visual tests on CI or real device/cloud providers for cross‑OS/browser parity (BrowserStack/Checkly/etc.) rather than developer laptops. [4][5]
-
Isolate tests (fresh browser context) and fix viewport/resolution to target breakpoints you care about (mobile/desktop). [1][4]
-
Use Playwright tooling for debugging failing visual tests: trace viewer, HTML reports, automatic screenshots on error. [0][1]
-
Start small and scale: cover critical user journeys/components first; automate review/approval workflow for snapshot changes. [3][4]
Sources: Playwright docs — Visual comparisons & Best Practices; BrowserStack Playwright guide; Checkly visual regression docs.
[1] Playwright — Visual comparisons / snapshots.
[0] Playwright — Best Practices.
[4] BrowserStack Playwright best practices.
[5] Checkly — Visual regression / Playwright docs.
🏁 Script executed:
find . -name "playwright.config.*" -type fRepository: VladSez/easy-invoice-pdf
Length of output: 90
🏁 Script executed:
rg "toMatchSnapshot|toHaveScreenshot|screenshot\(" --max-count 20 -nRepository: VladSez/easy-invoice-pdf
Length of output: 1180
🏁 Script executed:
ls -la | grep -i playwrightRepository: VladSez/easy-invoice-pdf
Length of output: 140
🏁 Script executed:
cat -n playwright.config.tsRepository: VladSez/easy-invoice-pdf
Length of output: 5506
🏁 Script executed:
rg "updateSnapshots|update|platform|mac|linux" playwright.config.ts -nRepository: VladSez/easy-invoice-pdf
Length of output: 50
🏁 Script executed:
find . -name "*snapshot*" -o -name "*.png" | grep -E "(e2e|__screenshots__|test-results)" | head -20Repository: VladSez/easy-invoice-pdf
Length of output: 2391
🏁 Script executed:
cat .gitignore | grep -i snapshotRepository: VladSez/easy-invoice-pdf
Length of output: 50
🏁 Script executed:
fd "CONTRIBUTING\|README" -type f | head -5Repository: VladSez/easy-invoice-pdf
Length of output: 238
🏁 Script executed:
ls -la e2e/default-invoice-template/pdf.test.ts-snapshots/ | head -10Repository: VladSez/easy-invoice-pdf
Length of output: 1008
🏁 Script executed:
git log --oneline -n 5 -- "e2e/*snapshot*" 2>/dev/null || echo "Git history check"Repository: VladSez/easy-invoice-pdf
Length of output: 142
🏁 Script executed:
rg "snapshotPathTemplate|updateSnapshots" . --type ts --type js 2>/dev/null || echo "No custom snapshot config found"Repository: VladSez/easy-invoice-pdf
Length of output: 99
Restructure snapshot generation to avoid forcing macOS-only workflow.
The current setup generates snapshots locally on macOS and commits them to git (all snapshots end with -darwin.png), then runs CI on macos-14 to match those baselines. This design forces all developers to use macOS for snapshot updates and increases CI costs by ~10x.
Instead, follow Playwright's 2025 best practice: generate baselines where CI will run, not on developer laptops. Recommended approach:
- Update
playwright.config.tsto addsnapshotPathTemplateconfiguration for CI snapshot generation - Switch CI runner to
ubuntu-latest(or your preferred platform) - Use
playwright test --update-snapshotsin a dedicated CI workflow step to generate/update baseline snapshots - Developers run tests locally to verify (snapshots auto-update during CI runs when intentional changes occur)
This eliminates the macOS requirement for developers while maintaining snapshot consistency and reducing CI costs significantly.
Alternatively, consider element-level screenshots with tolerance thresholds (await locator.screenshot() + maxDiffPixels) or a visual regression service for cross-platform testing.
🤖 Prompt for AI Agents
.github/workflows/e2e.yml lines 12-13: the workflow currently pins runs-on to
macos-14 to match locally generated Playwright snapshots; instead, change the
runner to ubuntu-latest and add a dedicated CI step that runs Playwright with
snapshot updates so baselines are generated where CI runs. Update
playwright.config.ts to set snapshotPathTemplate so CI-produced snapshots have
stable paths on Linux, add a workflow step to run `playwright test
--update-snapshots` (or equivalent) prior to the test step and commit or persist
the generated baselines as artifacts or in a branch/PR flow, and remove the
macOS requirement to allow developers on any OS and reduce CI cost.
* Changed the GitHub Actions workflow to use macOS-latest for improved compatibility with Playwright. * Updated screenshot assertions in various test files to include maxDiffPixelRatio for better visual comparison. * Enhanced screenshot handling in tests for the About page and Default Invoice Template to ensure consistency.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
e2e/stripe-invoice-template/template.test.ts (1)
613-625: Incomplete test coverage: Field visibility assertions still commented out.This TODO block with disabled assertions was flagged in a previous review. Tracking this as a known gap in test coverage.
🧹 Nitpick comments (4)
e2e/default-invoice-template/pdf.test.ts (3)
58-64: Consider extracting repeated PDF download-and-verify pattern into a helper.The pattern of downloading PDF → saving to temp dir → navigating to
file://URL → waiting 1000ms → taking screenshot is repeated 9+ times across this file. Extracting this into a helper function would reduce duplication and make maintenance easier.🔎 Example helper extraction
// In e2e/utils/pdf-helpers.ts export async function downloadAndVerifyPdf( page: Page, downloadButton: Locator, downloadDir: string, browserName: string, screenshotName: string, filePrefix = '' ) { const [download] = await Promise.all([ page.waitForEvent("download"), downloadButton.click(), ]); const suggestedFilename = download.suggestedFilename(); const pdfFilePath = path.join( downloadDir, `${browserName}-${filePrefix}${suggestedFilename}` ); await download.saveAs(pdfFilePath); const absolutePath = path.resolve(pdfFilePath); await expect.poll(() => fs.existsSync(absolutePath)).toBe(true); await page.setViewportSize({ width: 1100, height: 1185 }); await page.goto(`file://${absolutePath}`); await page.waitForTimeout(1000); await expect(page).toHaveScreenshot(screenshotName, { maxDiffPixelRatio: 0.01, }); }
58-61: Fixed timeout for PDF rendering may cause flakiness.The 1000ms
waitForTimeoutbefore taking screenshots is a potential flakiness source. Consider waiting for a more deterministic condition, such as the PDF viewer's loading indicator to disappear, a specific element within the PDF viewer, or usingpage.waitForLoadState('networkidle').
170-232: Consider reorganizing Stripe template verification into the Stripe test file.The Default Invoice Template test file contains significant Stripe template verification blocks (lines 170-232, 392-453, 665-723, 868-933). While testing template switching within a flow is valid, consider whether these could be better organized in
e2e/stripe-invoice-template/template.test.tsto improve test discoverability and maintainability.e2e/stripe-invoice-template/template.test.ts (1)
898-900: Remove commented-out code.Line 899 contains a commented-out
downloadPromisevariable that's no longer used. Clean up this dead code.🔎 Proposed fix
- // Set up download handler - // const downloadPromise = page.waitForEvent("download"); - const downloadPDFButton = page.getByRole("link", {
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
.github/workflows/e2e.ymle2e/about-snapshot.test.tse2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.ts
🧰 Additional context used
🧠 Learnings (11)
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use descriptive test names
Applied to files:
e2e/about-snapshot.test.tse2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Use Playwright for E2E testing
Applied to files:
e2e/about-snapshot.test.ts.github/workflows/e2e.yml
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use meaningful assertions in tests
Applied to files:
e2e/about-snapshot.test.tse2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Integration tests must cover key user flows
Applied to files:
e2e/about-snapshot.test.tse2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Follow AAA pattern (Arrange, Act, Assert) in tests
Applied to files:
e2e/about-snapshot.test.tse2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Test error cases
Applied to files:
e2e/about-snapshot.test.tse2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Test cases should use it('should do something specific', () => {})
Applied to files:
e2e/about-snapshot.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use MSW for API mocking in tests
Applied to files:
e2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Keep tests isolated
Applied to files:
e2e/default-invoice-template/pdf.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Mock external dependencies in tests
Applied to files:
e2e/default-invoice-template/pdf.test.tse2e/stripe-invoice-template/template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use test data factories in tests
Applied to files:
e2e/stripe-invoice-template/template.test.ts
🧬 Code graph analysis (2)
e2e/default-invoice-template/pdf.test.ts (1)
e2e/utils/extended-playwright-test.ts (1)
test(17-33)
e2e/stripe-invoice-template/template.test.ts (3)
e2e/utils/extended-playwright-test.ts (2)
test(17-33)expect(35-35)e2e/stripe-invoice-template/utils.ts (2)
uploadBase64LogoAsFile(10-51)SMALL_TEST_IMAGE_BASE64(4-5)src/app/schema/index.ts (2)
PDF_DATA_LOCAL_STORAGE_KEY(753-753)InvoiceData(751-751)
🔇 Additional comments (5)
e2e/about-snapshot.test.ts (1)
1-26: Well-structured snapshot test with good determinism handling.The test correctly pauses and resets video elements before capturing the screenshot, which prevents flakiness from video playback state. The
maxDiffPixelRatio: 0.01threshold is appropriately strict for visual regression testing.Note: The
downloadDirfixture is imported but not used in this test. Since the extended fixture also re-exportsexpect, this is fine, but you could alternatively import directly from@playwright/testif this file never needs download handling.e2e/default-invoice-template/pdf.test.ts (1)
10-14: Good use of fixed system time for test determinism.Setting the system time to a fixed date ensures invoice numbers and date-related fields are consistent across test runs. This is a best practice for avoiding flaky tests due to time-dependent data.
e2e/stripe-invoice-template/template.test.ts (2)
15-25: Good test isolation with localStorage cleanup.The
afterEachhook properly clears localStorage after each test, ensuring test isolation. Combined with the fixed system time inbeforeEach, this provides a clean, deterministic environment for each test.
855-947: Good coverage for Stripe template PDF generation with logo and payment URL.This new test properly validates the Stripe template's unique features (logo upload and payment URL) are correctly rendered in the generated PDF. The test follows the established pattern and includes proper localStorage verification before PDF generation.
.github/workflows/e2e.yml (1)
72-73: No action needed. The artifact upload path (playwright-output/test-results) and email attachment path (playwright-output/report/index.html) intentionally reference different directories. TheoutputDirin playwright.config.ts contains test artifacts (screenshots, videos, traces), while the HTMLreporteroutput folder contains the visual test report. These serve different purposes and both paths are correct.Likely an incorrect or invalid review comment.
* Updated GitHub Actions workflow to specify macOS 14 for consistency with local environment. * Modified e2e tests to check total value after invoice generation and adjusted wait times for better stability. * Changed URL assertions in Not Found page tests to include template parameter for accurate routing. * Added comments to clarify updates in seller and buyer information sections in the Default Invoice Template tests.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @src/app/[locale]/about/page.tsx:
- Line 271: The videoSrc assignment uses a misspelled filename
"instand-download.mp4" which will 404; update the videoSrc value to use the
correct filename "instant-download.mp4" (i.e., set videoSrc to
`${IMAGEKIT_CDN_URL}/instant-download.mp4`), and scan the same file for any
other occurrences of "instand-download" to keep names consistent with the
fallback image.
🧹 Nitpick comments (2)
src/app/[locale]/about/page.tsx (2)
499-501: Consider removinganimate-pulsefrom text content.Continuous pulsing animation on text can be distracting and may pose accessibility concerns (WCAG 2.2.2 - Pause, Stop, Hide). For a static informational message like "no signup required," a subtle static style might be more appropriate.
♻️ Suggested fix
- <p className="animate-pulse text-sm text-slate-400"> + <p className="text-sm text-slate-400">
510-528: Remove commented-out code.This block contains ~19 lines of commented-out code including an unused translation hook and old JSX markup. Dead code reduces readability; version control preserves history if needed later.
♻️ Suggested cleanup
function Logo() { - // const t = useTranslations("About"); - return ( <div> - {/* <div className="flex items-center gap-1"> - <ProjectLogo className="size-7 flex-shrink-0 sm:size-8" /> - <div className="flex flex-col"> - <h1 className="text-balance text-xl font-bold text-slate-800 lg:text-2xl"> - <a - href="https://dub.sh/easy-invoice?ref=about-page" - target="_blank" - rel="noopener noreferrer" - > - EasyInvoicePDF - </a> - </h1> - </div> */} - {/* <ProjectLogoDescription>{t("tagline")}</ProjectLogoDescription> */} - {/* </div> */} <div className="flex items-center">
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/app/[locale]/about/page.tsx
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Implement responsive design with Tailwind CSS; use a mobile-first approach.
Applied to files:
src/app/[locale]/about/page.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Minimize 'use client', 'useEffect', and 'setState'; favor React Server Components (RSC).
Applied to files:
src/app/[locale]/about/page.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Prefer Conditional (ternary) operator (? :) over Logical AND operator (&&) in React components.
Applied to files:
src/app/[locale]/about/page.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Use Shadcn UI, Radix, and Tailwind for components and styling.
Applied to files:
src/app/[locale]/about/page.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Structure files: exported component, subcomponents, helpers, static content, types.
Applied to files:
src/app/[locale]/about/page.tsx
🔇 Additional comments (2)
src/app/[locale]/about/page.tsx (2)
260-380: LGTM on the FeaturesSection restructure.The video-based feature cards with alternating layout are well-implemented. The use of
satisfiesfor type safety onFEATURES_CARDSand thecn()utility for conditional classes follows good patterns. Based on learnings, the mobile-first responsive approach is correctly applied.
168-258: LGTM on the HeroSection changes.The rich text implementation with
t.rich()for highlighted spans is clean, and the video container with Mac OS frame styling is well-structured. Responsive breakpoints follow mobile-first approach per project guidelines.
| translationKey: "shareableLinks", | ||
| icon: <Share2 className="h-10 w-10 text-slate-700" />, | ||
| translationKey: "instantDownload", | ||
| videoSrc: `${IMAGEKIT_CDN_URL}/instand-download.mp4`, |
There was a problem hiding this comment.
Typo in video filename: instand-download.mp4 should likely be instant-download.mp4.
The fallback image on line 272 uses the correct spelling instant-download-fallback.png, suggesting this is a typo that will cause a 404 error.
🔧 Suggested fix
- videoSrc: `${IMAGEKIT_CDN_URL}/instand-download.mp4`,
+ videoSrc: `${IMAGEKIT_CDN_URL}/instant-download.mp4`,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| videoSrc: `${IMAGEKIT_CDN_URL}/instand-download.mp4`, | |
| videoSrc: `${IMAGEKIT_CDN_URL}/instant-download.mp4`, |
🤖 Prompt for AI Agents
In @src/app/[locale]/about/page.tsx at line 271, The videoSrc assignment uses a
misspelled filename "instand-download.mp4" which will 404; update the videoSrc
value to use the correct filename "instant-download.mp4" (i.e., set videoSrc to
`${IMAGEKIT_CDN_URL}/instant-download.mp4`), and scan the same file for any
other occurrences of "instand-download" to keep names consistent with the
fallback image.
* Changed the video demo URL to a new format for better asset management. * Updated the fallback image used in the about page video component to align with the new demo structure. * Removed the static assets URL import as it is no longer needed.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @src/app/[locale]/about/page.tsx:
- Around line 274-278: There is a typo in the feature card object: the videoSrc
string uses "instand-download.mp4" while the translationKey and fallback image
use "instant"; update the videoSrc value to use "instant-download.mp4" so it
matches videoFallbackImg and translationKey (look for the object with
translationKey: "instantDownload" and correct the videoSrc property).
🧹 Nitpick comments (2)
src/app/[locale]/about/page.tsx (2)
514-545: Remove commented-out dead code in the Logo component.The Logo component contains a large block of commented-out JSX (lines 519-533). This clutters the codebase and provides no value since version control preserves history. Consider removing it entirely.
Proposed cleanup
function Logo() { - // const t = useTranslations("About"); - return ( <div> - {/* <div className="flex items-center gap-1"> - <ProjectLogo className="size-7 flex-shrink-0 sm:size-8" /> - <div className="flex flex-col"> - <h1 className="text-balance text-xl font-bold text-slate-800 lg:text-2xl"> - <a - href="https://dub.sh/easy-invoice?ref=about-page" - target="_blank" - rel="noopener noreferrer" - > - EasyInvoicePDF - </a> - </h1> - </div> */} - {/* <ProjectLogoDescription>{t("tagline")}</ProjectLogoDescription> */} - {/* </div> */} <div className="flex items-center"> <ProjectLogo className="size-7 flex-shrink-0 sm:size-8" /> <p className="text-balance text-center text-xl font-bold text-slate-800 sm:mt-0 sm:text-2xl lg:mr-5 lg:text-left"> <a href="https://easyinvoicepdf.com" target="_blank" rel="noopener noreferrer" > EasyInvoicePDF </a> </p> </div> </div> ); }
355-377: Consider extracting the repeated Mac OS frame component.The Mac OS-style browser chrome bar with the three colored dots is duplicated in both
HeroSection(lines 237-247) andFeaturesSection(lines 357-367). This pattern could be extracted into a reusable component to reduce duplication and ensure consistency.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
e2e/about.test.tssrc/app/[locale]/about/page.tsxsrc/config.ts
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Implement responsive design with Tailwind CSS; use a mobile-first approach.
Applied to files:
src/app/[locale]/about/page.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Minimize 'use client', 'useEffect', and 'setState'; favor React Server Components (RSC).
Applied to files:
src/app/[locale]/about/page.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Prefer Conditional (ternary) operator (? :) over Logical AND operator (&&) in React components.
Applied to files:
src/app/[locale]/about/page.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Use Shadcn UI, Radix, and Tailwind for components and styling.
Applied to files:
src/app/[locale]/about/page.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Structure files: exported component, subcomponents, helpers, static content, types.
Applied to files:
src/app/[locale]/about/page.tsx
🧬 Code graph analysis (1)
src/app/[locale]/about/page.tsx (3)
src/components/video.tsx (1)
Video(12-91)src/config.ts (4)
VIDEO_DEMO_URL(21-21)VIDEO_DEMO_FALLBACK_IMG(26-26)IMAGEKIT_CDN_URL(15-16)GITHUB_URL(32-32)src/lib/utils.ts (1)
cn(4-6)
🔇 Additional comments (6)
src/app/[locale]/about/page.tsx (1)
10-21: LGTM on the updated imports.The new imports (
IMAGEKIT_CDN_URL,VIDEO_DEMO_FALLBACK_IMG,cn,GITHUB_URL,VIDEO_DEMO_URL) align with the refactored asset handling and config-based URLs. Good use of centralized config constants.src/config.ts (1)
7-37: LGTM on the config constants.Good additions with clear documentation comments explaining the purpose of each constant. The separation of
STATIC_ASSETS_URLfor PDF templates andIMAGEKIT_CDN_URLfor landing page videos provides clear intent. The version query parameter onVIDEO_DEMO_URLis a sensible cache-busting approach.e2e/about.test.ts (4)
1-7: LGTM on the updated imports.The test file correctly imports
VIDEO_DEMO_FALLBACK_IMGto validate the updated video poster attribute.
42-51: Good update to test assertions for hero content.The test assertions are properly updated to validate the new hero description text and the
VIDEO_DEMO_FALLBACK_IMGposter attribute, aligning with the About page changes.
69-85: Test assertions updated for features section.The "coming soon" badge text and features description are correctly updated to match the new UI content.
183-185: Good addition of footer attribution test.The new assertion verifying "Made by Vlad Sazonau" ensures the footer attribution is properly rendered. Consider adding similar assertions to the French and German locale tests for consistency if the attribution is localized.
Verify if the footer attribution text is the same across all locales or if it's localized. If localized, consider adding assertions in the French and German tests as well.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
e2e/about.test.ts (1)
26-57: Consider usingSTATIC_ASSETS_URLfrom config for font URLs.The font URLs are hardcoded while other asset URLs (VIDEO_DEMO_URL, VIDEO_DEMO_FALLBACK_IMG) are imported from config. This inconsistency could lead to maintenance issues if the static assets domain changes.
♻️ Suggested refactor
+import { + GITHUB_URL, + MARKETING_FEATURES_CARDS, + STATIC_ASSETS_URL, + TWITTER_URL, + VIDEO_DEMO_FALLBACK_IMG, + VIDEO_DEMO_URL, +} from "@/config"; // Test PDF fonts const openSansRegularResponse = await page.request.get( - "https://static.easyinvoicepdf.com/open-sans-regular.ttf", + `${STATIC_ASSETS_URL}/open-sans-regular.ttf`, );Apply similarly to the other font URLs on lines 34, 41, 47, and 53.
src/app/[locale]/about/page.tsx (3)
40-48: Unused newsletter translation variables.These newsletter translation variables are defined but never used in the component's JSX. The
translationsprop passed toFooterincludes them (lines 129-135), but if the newsletter UI was removed, these may be vestigial code.Verify if the
Footercomponent still uses these newsletter translations. If not, remove them:#!/bin/bash # Check if Footer component uses newsletter props ast-grep --pattern 'newsletterTitle' rg -n "newsletter" src/components/footer.tsx
477-511: Remove commented-out code in Logo component.Lines 478-496 contain a large block of commented-out JSX that should be removed. Dead code clutters the codebase and creates confusion about the intended implementation.
♻️ Cleaned up Logo component
function Logo() { - // const t = useTranslations("About"); - return ( <div> - {/* <div className="flex items-center gap-1"> - <ProjectLogo className="size-7 flex-shrink-0 sm:size-8" /> - <div className="flex flex-col"> - <h1 className="text-balance text-xl font-bold text-slate-800 lg:text-2xl"> - <a - href="https://dub.sh/easy-invoice?ref=about-page" - target="_blank" - rel="noopener noreferrer" - > - EasyInvoicePDF - </a> - </h1> - </div> */} - {/* <ProjectLogoDescription>{t("tagline")}</ProjectLogoDescription> */} - {/* </div> */} <div className="flex items-center"> <ProjectLogo className="size-7 flex-shrink-0 sm:size-8" /> <p className="text-balance text-center text-xl font-bold text-slate-800 sm:mt-0 sm:text-2xl lg:mr-5 lg:text-left"> <a href="https://easyinvoicepdf.com" target="_blank" rel="noopener noreferrer" > EasyInvoicePDF </a> </p> </div> </div> ); }
318-340: Consider extracting Mac OS frame into a reusable component.The Mac OS browser frame UI (lines 320-330) is duplicated from the HeroSection (lines 237-247). Extracting this into a shared component would improve maintainability.
Example extracted component:
function MacOsFrame({ children }: { children: React.ReactNode }) { return ( <div className="relative overflow-hidden rounded-xl border border-slate-200 bg-white shadow-lg md:rounded-2xl md:shadow-xl"> <div className="h-8 w-full rounded-t-xl bg-gradient-to-b from-[#F3F3F3] to-[#E9E9E9] px-4 shadow-sm md:h-12 md:rounded-t-2xl"> <div className="flex h-full items-center"> <div className="flex space-x-2"> <div className="h-2.5 w-2.5 rounded-full bg-[#FF5F57] md:h-3 md:w-3" /> <div className="h-2.5 w-2.5 rounded-full bg-[#FEBC2E] md:h-3 md:w-3" /> <div className="h-2.5 w-2.5 rounded-full bg-[#28C840] md:h-3 md:w-3" /> </div> </div> </div> {children} </div> ); }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
e2e/about.test.tssrc/app/[locale]/about/page.tsxsrc/config.ts
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Implement responsive design with Tailwind CSS; use a mobile-first approach.
Applied to files:
src/app/[locale]/about/page.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Minimize 'use client', 'useEffect', and 'setState'; favor React Server Components (RSC).
Applied to files:
src/app/[locale]/about/page.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Prefer Conditional (ternary) operator (? :) over Logical AND operator (&&) in React components.
Applied to files:
src/app/[locale]/about/page.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Applies to **/*.tsx : Use Shadcn UI, Radix, and Tailwind for components and styling.
Applied to files:
src/app/[locale]/about/page.tsx
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Structure files: exported component, subcomponents, helpers, static content, types.
Applied to files:
src/app/[locale]/about/page.tsx
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use meaningful assertions in tests
Applied to files:
e2e/about.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Integration tests must cover key user flows
Applied to files:
e2e/about.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use descriptive test names
Applied to files:
e2e/about.test.ts
🧬 Code graph analysis (2)
src/app/[locale]/about/page.tsx (3)
src/components/video.tsx (1)
Video(12-91)src/config.ts (4)
VIDEO_DEMO_URL(21-21)VIDEO_DEMO_FALLBACK_IMG(26-26)MARKETING_FEATURES_CARDS(42-77)GITHUB_URL(32-32)src/lib/utils.ts (1)
cn(4-6)
e2e/about.test.ts (2)
e2e/utils/extended-playwright-test.ts (2)
test(17-33)expect(35-35)src/config.ts (3)
VIDEO_DEMO_URL(21-21)VIDEO_DEMO_FALLBACK_IMG(26-26)MARKETING_FEATURES_CARDS(42-77)
🔇 Additional comments (6)
src/config.ts (2)
7-26: LGTM!The CDN configuration is well-structured with proper separation of base URL and asset paths. Using template literals for composing URLs keeps things DRY.
42-72: No action required. The filenameinstand-download.mp4is correct and matches the actual CDN asset. The HTTP 200 response confirms the asset exists at that URL, while the corrected spelling returns HTTP 404, indicating the video is intentionally named with "instand" rather than "instant".Likely an incorrect or invalid review comment.
e2e/about.test.ts (2)
58-69: LGTM!Good coverage for feature card assets. The loop validates both video sources and fallback images for each marketing feature card. Based on learnings, the test uses meaningful assertions with specific content-type checks.
71-246: LGTM!Comprehensive English locale test covering header, hero section, features, FAQ, and footer elements. The assertions properly verify updated content like the new hero description, video poster attributes, and footer attribution.
src/app/[locale]/about/page.tsx (2)
189-263: LGTM!The hero section is well-implemented with proper responsive design using Tailwind's mobile-first approach. The
t.richusage for rich text interpolation with highlighted spans is a clean pattern for i18n.
350-427: LGTM!The FAQ section uses a clean accessible pattern with native
<details>elements. The FAQ_ITEMS array with type constraint matches the pattern used for MARKETING_FEATURES_CARDS.
* Moved static asset tests from the about page to a dedicated static-assets.test.ts file for better organization. * Updated asset URLs in the config to use STATIC_ASSETS_URL for consistency. * Registered fonts in invoice templates using the new INVOICE_PDF_FONTS structure for improved maintainability.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @src/config.ts:
- Line 44: The videoSrc constant currently references a misspelled filename
"instand-download.mp4"; update the string in the videoSrc assignment (the
template using STATIC_ASSETS_URL) to use "instant-download.mp4" so the path
becomes `${STATIC_ASSETS_URL}/demo-videos/instant-download.mp4`.
🧹 Nitpick comments (1)
src/config.ts (1)
15-15: Consider standardizing the cache-busting strategy.The codebase uses inconsistent versioning approaches:
VIDEO_DEMO_URL:?v=1- Some marketing videos:
?updatedAt=<timestamp>- Other assets: no version parameter
For consistency and clearer cache invalidation intent, consider adopting a uniform strategy (e.g., always use
?v=<semver>or always use timestamps).Also applies to: 39-40, 59-60
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
e2e/about.test.tse2e/static-assets.test.tssrc/app/(app)/components/invoice-templates/invoice-pdf-default-template/index.tsxsrc/app/(app)/components/invoice-templates/invoice-pdf-stripe-template/index.tsxsrc/config.ts
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-07-20T22:00:19.380Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/shared.mdc:0-0
Timestamp: 2025-07-20T22:00:19.380Z
Learning: Structure files: exported component, subcomponents, helpers, static content, types.
Applied to files:
src/config.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Integration tests must cover key user flows
Applied to files:
e2e/static-assets.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Use Playwright for E2E testing
Applied to files:
e2e/static-assets.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use meaningful assertions in tests
Applied to files:
e2e/static-assets.test.tse2e/about.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Test error cases
Applied to files:
e2e/static-assets.test.ts
🧬 Code graph analysis (5)
src/config.ts (2)
src/app/(app)/components/invoice-pdf-template/invoice-footer.tsx (1)
InvoiceFooter(9-66)e2e/default-invoice-template/pdf.test.ts (2)
DATE_FORMAT(225-419)page(161-223)
e2e/static-assets.test.ts (1)
src/config.ts (5)
VIDEO_DEMO_URL(15-15)VIDEO_DEMO_FALLBACK_IMG(20-20)INVOICE_PDF_FONTS(76-89)MARKETING_FEATURES_CARDS(36-71)STATIC_ASSETS_URL(10-10)
e2e/about.test.ts (1)
src/config.ts (1)
VIDEO_DEMO_FALLBACK_IMG(20-20)
src/app/(app)/components/invoice-templates/invoice-pdf-stripe-template/index.tsx (1)
src/config.ts (1)
INVOICE_PDF_FONTS(76-89)
src/app/(app)/components/invoice-templates/invoice-pdf-default-template/index.tsx (1)
src/config.ts (1)
INVOICE_PDF_FONTS(76-89)
🔇 Additional comments (9)
src/app/(app)/components/invoice-templates/invoice-pdf-stripe-template/index.tsx (2)
23-23: LGTM! Font configuration properly centralized.The migration from inline
STATIC_ASSETS_URLreferences toINVOICE_PDF_FONTS.STRIPE_TEMPLATEimproves maintainability by centralizing asset paths in the config file.Also applies to: 43-43, 48-48, 53-53
154-154: Verify the layout adjustment is intentional.The
colAmountflex value increased from 1.2 to 1.3. Ensure this change improves column width balance in the Stripe template and doesn't cause text overflow or layout issues with longer amounts.src/app/(app)/components/invoice-templates/invoice-pdf-default-template/index.tsx (2)
2-2: LGTM! Font asset configuration properly centralized.The import and font source updates align with the new
INVOICE_PDF_FONTSconfiguration structure, improving maintainability across templates.Also applies to: 20-20, 23-23
116-121: Verify the table column rebalancing.Multiple column flex values have been adjusted:
colName: 5 → 4.8colAmount: 1.1 → 1.2colVAT: 0.8 → 0.9Ensure these changes improve the table layout without causing text truncation or overflow, especially for longer item names or amounts in different locales.
e2e/static-assets.test.ts (2)
1-53: LGTM! Comprehensive static asset validation.The test thoroughly validates:
- Video assets (status and content-type)
- Fallback images
- PDF fonts across templates
- Marketing feature card assets
The assertions are meaningful and check both HTTP status and content-type headers, which aligns with E2E testing best practices.
55-83: LGTM! Effective CDN asset monitoring approach.The network monitoring tests use
page.on("response")to track failed CDN asset loads across critical pages. This approach effectively catches asset loading issues before they reach production.e2e/about.test.ts (2)
4-4: LGTM! Proper use of centralized config constants.The import and usage of
VIDEO_DEMO_FALLBACK_IMGaligns with the asset configuration refactoring, improving maintainability.Also applies to: 51-51
17-17: LGTM! Test assertions updated to reflect new content.The test assertions have been properly updated to match the new page content, including:
- Updated page title
- Condensed hero description
- Revised features section text
- Author attribution in footer
Also applies to: 44-44, 71-71, 83-83, 184-184
src/config.ts (1)
7-20: LGTM! Well-structured configuration with strong type safety.The new configuration constants are well-organized with:
- Clear JSDoc documentation
- Type-safe declarations using
as const satisfies- Logical grouping of related assets
This centralizes asset management and improves maintainability across the codebase.
Also applies to: 33-71, 73-89
| }, | ||
| { | ||
| translationKey: "instantDownload", | ||
| videoSrc: `${STATIC_ASSETS_URL}/demo-videos/instand-download.mp4`, |
There was a problem hiding this comment.
Fix typo in video filename.
The filename contains a typo: instand-download.mp4 should be instant-download.mp4 (missing 't').
📝 Proposed fix
- videoSrc: `${STATIC_ASSETS_URL}/demo-videos/instand-download.mp4`,
+ videoSrc: `${STATIC_ASSETS_URL}/demo-videos/instant-download.mp4`,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| videoSrc: `${STATIC_ASSETS_URL}/demo-videos/instand-download.mp4`, | |
| videoSrc: `${STATIC_ASSETS_URL}/demo-videos/instant-download.mp4`, |
🤖 Prompt for AI Agents
In @src/config.ts at line 44, The videoSrc constant currently references a
misspelled filename "instand-download.mp4"; update the string in the videoSrc
assignment (the template using STATIC_ASSETS_URL) to use "instant-download.mp4"
so the path becomes `${STATIC_ASSETS_URL}/demo-videos/instant-download.mp4`.
* Revised video source and fallback image URLs in the config to include versioning for better asset management. * Ensured all demo video links follow the new naming convention for improved organization.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
e2e/stripe-invoice-template/stripe-invoice-template.test.ts (1)
306-321: Incomplete test: Missing assertion for invalid URL validation error.The test fills an invalid URL but never asserts that an error message is displayed. This reduces the test's effectiveness for verifying validation behavior.
💚 Suggested fix
// Try invalid URL await paymentUrlInput.fill("not-a-valid-url"); await paymentUrlInput.blur(); - // Check for validation error (this would depend on your validation implementation) - // The actual validation error checking would depend on how your form validation works + // Verify validation error is shown for invalid URL + await expect( + page.getByText(/invalid url|please enter a valid url/i), + ).toBeVisible(); // Try valid URL await paymentUrlInput.fill("https://buy.stripe.com/test_payment_link"); await paymentUrlInput.blur(); + + // Verify validation error is no longer shown + await expect( + page.getByText(/invalid url|please enter a valid url/i), + ).toBeHidden(); // Should not show error for valid URL
🤖 Fix all issues with AI agents
In @src/config.ts:
- Around line 42-46: The videoSrc entry in src/config.ts for the object with
translationKey "instantDownload" has a typo in the filename
("instand-download-v1.mp4") causing the video to fail to load; update the
videoSrc value to use "instant-download-v1.mp4" so it matches the fallback image
naming and the STATIC_ASSETS_URL path.
🧹 Nitpick comments (2)
e2e/stripe-invoice-template/stripe-invoice-template.test.ts (2)
38-41: Consider using a flexible matcher for the versioned OG image URL.The hardcoded version string
?v=1755773921680will cause test failures when the version is updated. Consider using a regex matcher or checking only the base URL path.♻️ Suggested approach
await expect(page.locator('meta[property="og:image"]')).toHaveAttribute( "content", - `${STATIC_ASSETS_URL}/stripe-og.png?v=1755773921680`, + expect.stringMatching(new RegExp(`^${STATIC_ASSETS_URL}/stripe-og\\.png(\\?v=\\d+)?$`)), );Alternatively, extract the version from a shared constant if it needs to be exact.
272-274: Consistent debounce wait pattern across tests.Multiple tests use
waitForTimeout(800)for debounce handling. While the eslint-disable comments document the intent, consider extracting this to a named constant for maintainability.♻️ Optional: Extract debounce wait constant
Add near the top of the file:
/** Debounce timeout used throughout the app for form field updates */ const DEBOUNCE_WAIT_MS = 800;Then replace all
waitForTimeout(800)calls with:await page.waitForTimeout(DEBOUNCE_WAIT_MS);This makes it easier to adjust if the app's debounce timing changes.
Also applies to: 355-357, 761-763, 911-913
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
e2e/invoice-form.test.tse2e/stripe-invoice-template/stripe-invoice-template.test.tssrc/config.ts
🧰 Additional context used
🧠 Learnings (11)
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Integration tests must cover key user flows
Applied to files:
e2e/invoice-form.test.tse2e/stripe-invoice-template/stripe-invoice-template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use meaningful assertions in tests
Applied to files:
e2e/invoice-form.test.tse2e/stripe-invoice-template/stripe-invoice-template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use descriptive test names
Applied to files:
e2e/invoice-form.test.tse2e/stripe-invoice-template/stripe-invoice-template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Use Playwright for E2E testing
Applied to files:
e2e/invoice-form.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Follow AAA pattern (Arrange, Act, Assert) in tests
Applied to files:
e2e/stripe-invoice-template/stripe-invoice-template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Test error cases
Applied to files:
e2e/stripe-invoice-template/stripe-invoice-template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use MSW for API mocking in tests
Applied to files:
e2e/stripe-invoice-template/stripe-invoice-template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use test data factories in tests
Applied to files:
e2e/stripe-invoice-template/stripe-invoice-template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use Vitest for unit and integration tests
Applied to files:
e2e/stripe-invoice-template/stripe-invoice-template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Test cases should use it('should do something specific', () => {})
Applied to files:
e2e/stripe-invoice-template/stripe-invoice-template.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Unit tests must achieve at least 80% coverage
Applied to files:
e2e/stripe-invoice-template/stripe-invoice-template.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run e2e tests via Vercel deployment
🔇 Additional comments (8)
e2e/stripe-invoice-template/stripe-invoice-template.test.ts (1)
16-22: Good test structure and canvas-based PDF verification.The test suite demonstrates solid practices:
- Fixed system time via
page.clock.setSystemTimeensures deterministic date-dependent assertions.- Per-test
downloadDirfixture properly isolates test artifacts.- Canvas-based PDF rendering with
renderPdfOnCanvasand screenshot comparison provides reliable visual verification.- Tests follow the AAA pattern with clear arrange, act, and assert phases.
Based on learnings, this aligns well with integration test guidelines for key user flows.
Also applies to: 729-873, 875-975
src/config.ts (1)
76-89: Well-structured font configuration with type safety.The
as const satisfiespattern ensures both literal types and structural validation. The centralized font paths make maintenance easier.e2e/invoice-form.test.ts (6)
23-34: Good pattern: waiting for content before URL assertion.Using
waitUntil: "commit"followed by waiting for a visible element ensures the page is fully redirected before asserting the URL. This prevents flaky tests.
50-59: Good use of config constant for OG image URL.Using
STATIC_ASSETS_URLfrom config makes the test maintainable and ensures consistency with the application code.
293-299: Consistent selector pattern for Tax Number fieldset.The updated selector follows the same pattern used for Invoice Number (lines 245-255), maintaining consistency throughout the test file.
669-673: Good coverage for the new "Invoice last updated" indicator.The assertion verifies the new UI feedback feature, ensuring users see confirmation that their changes are being tracked.
991-1012: Comprehensive VAT field validation coverage.The tests cover boundary values (101, -1), valid numeric values (23), and custom text values (NP, OO, CUSTOM). This aligns with the updated helper text messaging.
1109-1194: Thorough test for date handling user flow.Good use of
page.clock.setSystemTimefor deterministic date testing. The test covers the complete flow: detecting out-of-date values, showing helper messages, and verifying the "Update all dates" functionality. Based on learnings, this integration test effectively covers a key user flow with meaningful assertions.Consider whether the test should also verify that updated dates persist to localStorage after the update, similar to the "persists data in local storage" test pattern earlier in the file.
| // TODO: fix below conditions | ||
| // await expect(nameFieldSwitch).toBeHidden(); | ||
| // await expect(typeOfGTUFieldSwitch).toBeHidden(); | ||
| // await expect(amountFieldSwitch).toBeHidden(); | ||
| // await expect(unitFieldSwitch).toBeHidden(); | ||
| // await expect(netPriceFieldSwitch).toBeHidden(); | ||
|
|
||
| // // we expect vat field switch to be visible because it is the only field that is visible in stripe template | ||
| // await expect(vatFieldSwitch).toBeVisible(); | ||
|
|
||
| // await expect(netAmountFieldSwitch).toBeHidden(); | ||
| // await expect(vatAmountFieldSwitch).toBeHidden(); | ||
| // await expect(preTaxAmountFieldSwitch).toBeHidden(); |
There was a problem hiding this comment.
Address TODO: Commented-out assertions indicate incomplete test coverage.
These commented assertions for hidden switches in Stripe template should either be implemented or removed with an explanation. Leaving TODOs with significant commented code in tests can mask regressions.
Would you like me to help investigate why these assertions are failing or open an issue to track this task?
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @src/app/(app)/components/invoice-form/sections/invoice-items.tsx:
- Around line 513-538: The tax label input (Controller name="taxLabelText" with
id="taxLabelText" and Label htmlFor="taxLabelText") is being rendered inside the
fields.map loop causing duplicate IDs and form fields; restrict rendering to
only the first iteration by adding an isFirstItem conditional (same pattern used
for the "Show in PDF" switches) so the
Controller/Input/Label/ErrorMessage/InputHelperMessage block is rendered only
when isFirstItem is true, leaving per-item fields (e.g., items.${index}.vat)
unchanged and preserving LANGUAGE_TO_LABEL and INVOICE_PDF_TRANSLATIONS usage.
🧹 Nitpick comments (1)
src/app/(app)/components/invoice-form/sections/invoice-items.tsx (1)
167-167: Consider uncommenting the aria-labels for better accessibility.The commented-out
aria-labelattributes would improve screen reader support by providing more descriptive context for these switches. While the switches have associatedLabelcomponents, the aria-labels would offer additional clarity about which item and column they control.♻️ Uncomment the aria-labels
<Switch {...field} id={`itemNameFieldIsVisible${index}`} checked={value} onCheckedChange={onChange} className="h-5 w-8 [&_span]:size-4 [&_span]:data-[state=checked]:translate-x-3 rtl:[&_span]:data-[state=checked]:-translate-x-3" - // aria-label={`Show/hide the 'Name of Goods/Service' Column in the PDF for item ${index + 1}`} + aria-label={`Show/hide the 'Name of Goods/Service' Column in the PDF for item ${index + 1}`} />Apply similar changes to lines 226 and 285.
Also applies to: 226-226, 285-285
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/app/(app)/components/invoice-form/sections/invoice-items.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/app/(app)/components/invoice-form/sections/invoice-items.tsx (4)
src/app/(app)/components/invoice-form/common.tsx (1)
ErrorMessage(3-5)src/components/ui/input-helper-message.tsx (1)
InputHelperMessage(3-17)src/app/schema/index.ts (1)
LANGUAGE_TO_LABEL(305-316)src/app/(app)/pdf-i18n-translations/pdf-translations.ts (1)
INVOICE_PDF_TRANSLATIONS(211-1097)
🔇 Additional comments (5)
src/app/(app)/components/invoice-form/sections/invoice-items.tsx (5)
1-31: LGTM!The new imports for
LANGUAGE_TO_LABELandINVOICE_PDF_TRANSLATIONSare correctly added to support the dynamic tax labeling feature.
37-59: LGTM!The
taxLabelTextprop is correctly added to both the interface and function signature to support customizable tax labels.
86-105: LGTM!The label correctly uses the dynamic
taxLabelTextto display the customizable tax term in the UI.
539-569: LGTM!The per-item VAT input correctly uses the dynamic
taxLabelTextfor the label and is properly scoped to each invoice item.
640-707: LGTM!The dynamic
taxLabelTextis correctly used in labels, tooltips, and helper messages to provide consistent, customizable tax terminology throughout the invoice item fields.Also applies to: 770-773
| <div> | ||
| <Label htmlFor="taxLabelText">Tax Label</Label> | ||
| <Controller | ||
| name="taxLabelText" | ||
| control={control} | ||
| render={({ field }) => ( | ||
| <Input | ||
| {...field} | ||
| type="text" | ||
| id="taxLabelText" | ||
| placeholder="Enter tax label (e.g., VAT, Tax, GST, Sales Tax)" | ||
| className="mt-1 block w-full" | ||
| /> | ||
| )} | ||
| /> | ||
| {errors.taxLabelText && ( | ||
| <ErrorMessage>{errors.taxLabelText.message}</ErrorMessage> | ||
| )} | ||
| /> | ||
| {!errors.taxLabelText && ( | ||
| <InputHelperMessage> | ||
| Customize the tax label on your invoice (e.g., VAT, Sales | ||
| Tax, IVA). {LANGUAGE_TO_LABEL[language]} default:{" "} | ||
| {INVOICE_PDF_TRANSLATIONS[language].invoiceItemsTable.vat} | ||
| </InputHelperMessage> | ||
| )} | ||
| </div> |
There was a problem hiding this comment.
Critical: Tax Label input is rendered multiple times with duplicate IDs.
The Tax Label input is inside the fields.map loop (line 108) but lacks an isFirstItem conditional check. This causes the input to be rendered once for every invoice item, creating:
- Duplicate IDs:
id="taxLabelText"appears multiple times (HTML validation error) - Duplicate form fields: Only the last input's value will be submitted
- Accessibility violation:
<Label htmlFor="taxLabelText">references multiple inputs - Poor UX: Users see the same global setting repeated for each item
Since taxLabelText is a global field (not per-item like items.${index}.vat), it should only be rendered once.
🐛 Proposed fix: Add isFirstItem check
- <div>
+ {isFirstItem ? (
+ <div>
<Label htmlFor="taxLabelText">Tax Label</Label>
<Controller
name="taxLabelText"
control={control}
render={({ field }) => (
<Input
{...field}
type="text"
id="taxLabelText"
placeholder="Enter tax label (e.g., VAT, Tax, GST, Sales Tax)"
className="mt-1 block w-full"
/>
)}
/>
{errors.taxLabelText && (
<ErrorMessage>{errors.taxLabelText.message}</ErrorMessage>
)}
{!errors.taxLabelText && (
<InputHelperMessage>
Customize the tax label on your invoice (e.g., VAT, Sales
Tax, IVA). {LANGUAGE_TO_LABEL[language]} default:{" "}
{INVOICE_PDF_TRANSLATIONS[language].invoiceItemsTable.vat}
</InputHelperMessage>
)}
</div>
+ ) : null}This follows the same pattern used for other global settings like the "Show in PDF" switches (e.g., lines 486-510).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div> | |
| <Label htmlFor="taxLabelText">Tax Label</Label> | |
| <Controller | |
| name="taxLabelText" | |
| control={control} | |
| render={({ field }) => ( | |
| <Input | |
| {...field} | |
| type="text" | |
| id="taxLabelText" | |
| placeholder="Enter tax label (e.g., VAT, Tax, GST, Sales Tax)" | |
| className="mt-1 block w-full" | |
| /> | |
| )} | |
| /> | |
| {errors.taxLabelText && ( | |
| <ErrorMessage>{errors.taxLabelText.message}</ErrorMessage> | |
| )} | |
| /> | |
| {!errors.taxLabelText && ( | |
| <InputHelperMessage> | |
| Customize the tax label on your invoice (e.g., VAT, Sales | |
| Tax, IVA). {LANGUAGE_TO_LABEL[language]} default:{" "} | |
| {INVOICE_PDF_TRANSLATIONS[language].invoiceItemsTable.vat} | |
| </InputHelperMessage> | |
| )} | |
| </div> | |
| {isFirstItem ? ( | |
| <div> | |
| <Label htmlFor="taxLabelText">Tax Label</Label> | |
| <Controller | |
| name="taxLabelText" | |
| control={control} | |
| render={({ field }) => ( | |
| <Input | |
| {...field} | |
| type="text" | |
| id="taxLabelText" | |
| placeholder="Enter tax label (e.g., VAT, Tax, GST, Sales Tax)" | |
| className="mt-1 block w-full" | |
| /> | |
| )} | |
| /> | |
| {errors.taxLabelText && ( | |
| <ErrorMessage>{errors.taxLabelText.message}</ErrorMessage> | |
| )} | |
| {!errors.taxLabelText && ( | |
| <InputHelperMessage> | |
| Customize the tax label on your invoice (e.g., VAT, Sales | |
| Tax, IVA). {LANGUAGE_TO_LABEL[language]} default:{" "} | |
| {INVOICE_PDF_TRANSLATIONS[language].invoiceItemsTable.vat} | |
| </InputHelperMessage> | |
| )} | |
| </div> | |
| ) : null} |
🤖 Prompt for AI Agents
In @src/app/(app)/components/invoice-form/sections/invoice-items.tsx around
lines 513 - 538, The tax label input (Controller name="taxLabelText" with
id="taxLabelText" and Label htmlFor="taxLabelText") is being rendered inside the
fields.map loop causing duplicate IDs and form fields; restrict rendering to
only the first iteration by adding an isFirstItem conditional (same pattern used
for the "Show in PDF" switches) so the
Controller/Input/Label/ErrorMessage/InputHelperMessage block is rendered only
when isFirstItem is true, leaving per-item fields (e.g., items.${index}.vat)
unchanged and preserving LANGUAGE_TO_LABEL and INVOICE_PDF_TRANSLATIONS usage.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/app/(app)/page.client.tsx (2)
260-338: URL compare path should applyhandleInvoiceNumberBreakingChangetoo.When
urlDatais present, you parse decompressed URL invoice data without the invoice-number breaking-change patch. That can make “old” shared links look “invalid” and trigger the “incorrect URL” toast during edits.Proposed diff
const decompressedInvoiceDataFromUrl = decompressInvoiceData( urlParsed as Record<string, unknown>, ); - const validatediInvoiceDataFromUrl = invoiceSchema.parse( - decompressedInvoiceDataFromUrl, - ); + const updatedUrlJson = handleInvoiceNumberBreakingChange( + decompressedInvoiceDataFromUrl, + ); + const validatedInvoiceDataFromUrl = invoiceSchema.parse(updatedUrlJson); const invoiceHasChanged = - JSON.stringify(validatediInvoiceDataFromUrl) !== + JSON.stringify(validatedInvoiceDataFromUrl) !== JSON.stringify(newInvoiceDataValidated);
699-705: Addrel="noopener noreferrer"on external link withtarget="_blank".This is a reverse-tabnabbing footgun.
Proposed diff
<a href="https://dub.sh/easy-invoice-pdf-feedback" className="transition-colors hover:text-blue-600 hover:underline" target="_blank" + rel="noopener noreferrer" > Share your feedback </a>
🤖 Fix all issues with AI agents
In @src/app/(app)/page.client.tsx:
- Around line 550-564: The prop typing for handleShareInvoice is wrong:
InvoicePageHeader currently types it as () => void but the actual implementation
is async and returns Promise<void>; update the prop type in the
InvoicePageHeader parameter/type annotation to () => Promise<void> (and update
any related prop interfaces or usages that declare handleShareInvoice) so the
component signature matches the async function.
🧹 Nitpick comments (7)
src/app/(app)/page.client.tsx (4)
25-57: Unifyget-app-metadataimports to a single module path.You’re importing
getAppMetadatavia an alias path andDEFAULT_METADATAvia a relative path, which can create duplicate module instances / bundling oddities.Proposed diff
-import { getAppMetadata } from "@/app/(app)/utils/get-app-metadata"; +import { + DEFAULT_METADATA, + getAppMetadata, +} from "@/app/(app)/utils/get-app-metadata"; ... -import { DEFAULT_METADATA } from "./utils/get-app-metadata";
112-170: Don’t let metadata write failures wipe out invoice loading.
loadFromLocalStoragewrites metadata inside the sametryas invoice parsing; iflocalStorage.setItemfails (quota/private mode), you fall into the catch and reset invoice data.Proposed diff (isolate metadata init)
const loadFromLocalStorage = useCallback(() => { try { - const appMetadata = getAppMetadata(); - - // add metadata with default values if missing for all users - if (!appMetadata) { - localStorage.setItem( - METADATA_LOCAL_STORAGE_KEY, - JSON.stringify(DEFAULT_METADATA), - ); - } + try { + const appMetadata = getAppMetadata(); + if (!appMetadata) { + localStorage.setItem( + METADATA_LOCAL_STORAGE_KEY, + JSON.stringify(DEFAULT_METADATA), + ); + } + } catch (e) { + Sentry.captureException(e); + } const savedData = localStorage.getItem(PDF_DATA_LOCAL_STORAGE_KEY);
177-233: HandledecompressFromEncodedURIComponentpossibly returningnull.If decompression returns
null,JSON.parse(decompressedData)will throw a less actionable error. Consider an explicit check and a clearer error path (and similarly isolate metadata writes so URL-loaded invoices don’t get discarded on metadata failures).
352-440: GuardlocalStorage.setIteminside the CTAsetTimeout(can throw).Even if you fix the main localStorage availability check,
localStorage.setItem(CTA_TOAST_LAST_SHOWN_STORAGE_KEY, ...)runs later in a timer without a try/catch; quota/private-mode failures can become unhandled errors.e2e/generate-invoice-link.test.ts (3)
252-261: Consider extracting the custom tax label to a constant.The assertion on line 260 uses the hardcoded string
"Custom VAT"which duplicates the value set on line 134. Extracting this to a named constant (e.g.,TEST_TAX_LABEL) would improve maintainability and make the relationship between setup and assertion more explicit.♻️ Proposed refactor
At the top of the file, add:
+const TEST_TAX_LABEL = "Custom VAT"; + const TEST_SELLER_DATA = {Then use it in both locations:
await taxSettingsFieldset .getByRole("textbox", { name: "Tax Label" }) - .fill("Custom VAT"); + .fill(TEST_TAX_LABEL);And in the assertion:
await expect( - newTaxSettingsFieldset.getByRole("textbox", { name: "Custom VAT" }), + newTaxSettingsFieldset.getByRole("textbox", { name: TEST_TAX_LABEL }), ).toHaveValue("23");
318-319: Consider extracting the old uncompressed URL to a top-level constant.The 800+ character URL string embedded in the test makes it harder to read the test logic. Moving it to the top of the file alongside
TEST_SELLER_DATAandTEST_BUYER_DATAwould improve readability.♻️ Proposed refactor
Move to the top of the file after the test data constants:
} as const satisfies BuyerData; +const OLD_UNCOMPRESSED_URL = + "N4IgNghgdg5grhGBTEAuEAHMIA0IAmEALkgGID2ATgLbFogCyTDABACI4sCaPXuIAYziVKSKAICe9AKoBlNvxLUsxFOgDORSgEsMKPGHIxy9ftqgA3ctoFIAcnGoAjJJQDyTgFZIBRNKEgXbHRSCABrImEIFihKdDwLCDA4NRAARgB6AAYADgBaACYsgoBWEABfPEISNwAzAEl1dRT6ItK83Ly0sqrVOtlXCxtUtpKO-IBmNLNLa1sAFQk9egAlJAtXdSQWAGEACwhKZBmrYcW9Um0kMHxGgDVtdW0nMDUtFLwtsFfKfxBtfD0NIAdgALFkAGw5UElCagiZZHogKAQaipABS5D2UHY5H0IAg+Hwoia9AAMuQoPhKZxpABpfiJIh2EzoNIFOElCGM4gsy7XW7qB5PF5vSgfEBIWjaYIgTxYqAAAWlYAAdAJyNR+BABBq4FBmY4XL82RyYdy8Dq9QaHM5XPybvdHs9Xmh3khPgB3bS1IgAIRsQLNXP46m9voDAgdguFLrFEqg5BI6noyb8eETyejTpFrtQtSSW0qICccAkrj+AKBwNhoIhWRhJWBDf4KLR9AAggI0bsTJaiSSU+g7EhPdwqGFOHYuLTZB2eczWelgxaQEy+VdHULnaK3eKPZKVfQdWjlRAZerNa2k0ghyBr1nNzGd3n3cXtEohwBtUDmU62eolFtY0czjPcE1RVJZHIX1PUObY2HWa5yAwNEDVbSDs23XN4wPIgliQOoAHF5mkUw8HwvRiNIrDY13fNCwPVFyH1PxUDSS1qBYg1aJfXC8H1D96C2SghlsfhBKIXicPAg8oCQIgAAUdHE9iSiyDSMwU5ThmksDUHdBI6GHRSFz0+jDORBSOy41i0G6DSsi0ogbO4qSn1Aiz9yMlzbPQ1AnLXYhXNY8zX28zBRHmCAAA8Qv8hzNMipBorivz3IFTzwpScoAF0KKTJJ7PUpKmWi0VZEcWhKAkLL+MwCAJDQogGAUvZyEBdBvVEFgtGgdRagrPAMEa5rWqIdr8DC+qRqasQiDYFp0FGcZClBUMtF0JBFMatwoDAcwkGkShZQfW9ViQygthYAQDiOfFM1vabZOGzZKQ7OAJqobQAC8kHweZyDWWxtA2Z6DIivQrvez72p0P6AfIRpmjIDzsP0t8gA"; + test.describe("Generate Invoice Link", () => {Then use it in the test:
- const OLD_UNCOMPRESSED_URL = - "N4IgNghgdg5grhGBTEAuEAHMIA0IAmEALkgGID2ATgLbFogCyTDABACI4sCaPXuIAYziVKSKAICe9AKoBlNvxLUsxFOgDORSgEsMKPGHIxy9ftqgA3ctoFIAcnGoAjJJQDyTgFZIBRNKEgXbHRSCABrImEIFihKdDwLCDA4NRAARgB6AAYADgBaACYsgoBWEABfPEISNwAzAEl1dRT6ItK83Ly0sqrVOtlXCxtUtpKO-IBmNLNLa1sAFQk9egAlJAtXdSQWAGEACwhKZBmrYcW9Um0kMHxGgDVtdW0nMDUtFLwtsFfKfxBtfD0NIAdgALFkAGw5UElCagiZZHogKAQaipABS5D2UHY5H0IAg+Hwoia9AAMuQoPhKZxpABpfiJIh2EzoNIFOElCGM4gsy7XW7qB5PF5vSgfEBIWjaYIgTxYqAAAWlYAAdAJyNR+BABBq4FBmY4XL82RyYdy8Dq9QaHM5XPybvdHs9Xmh3khPgB3bS1IgAIRsQLNXP46m9voDAgdguFLrFEqg5BI6noyb8eETyejTpFrtQtSSW0qICccAkrj+AKBwNhoIhWRhJWBDf4KLR9AAggI0bsTJaiSSU+g7EhPdwqGFOHYuLTZB2eczWelgxaQEy+VdHULnaK3eKPZKVfQdWjlRAZerNa2k0ghyBr1nNzGd3n3cXtEohwBtUDmU62eolFtY0czjPcE1RVJZHIX1PUObY2HWa5yAwNEDVbSDs23XN4wPIgliQOoAHF5mkUw8HwvRiNIrDY13fNCwPVFyH1PxUDSS1qBYg1aJfXC8H1D96C2SghlsfhBKIXicPAg8oCQIgAAUdHE9iSiyDSMwU5ThmksDUHdBI6GHRSFz0+jDORBSOy41i0G6DSsi0ogbO4qSn1Aiz9yMlzbPQ1AnLXYhXNY8zX28zBRHmCAAA8Qv8hzNMipBorivz3IFTzwpScoAF0KKTJJ7PUpKmWi0VZEcWhKAkLL+MwCAJDQogGAUvZyEBdBvVEFgtGgdRagrPAMEa5rWqIdr8DC+qRqasQiDYFp0FGcZClBUMtF0JBFMatwoDAcwkGkShZQfW9ViQygthYAQDiOfFM1vabZOGzZKQ7OAJqobQAC8kHweZyDWWxtA2Z6DIivQrvez72p0P6AfIRpmjIDzsP0t8gA"; - // Navigate to old uncompressed URL
439-447: Consider extracting the compression ratio threshold to a named constant.The hardcoded
20on line 447 represents a minimum expected compression ratio. Extracting this to a named constant would make the test's intent clearer and easier to adjust if compression algorithms change.♻️ Proposed refactor
At the top of the file:
+const MIN_COMPRESSION_RATIO_PERCENT = 20; + const TEST_SELLER_DATA = {Then use it in the assertion:
-// Verify significant compression occurred (at least 20% reduction) -expect(compressionRatio).toBeGreaterThan(20); +// Verify significant compression occurred +expect(compressionRatio).toBeGreaterThan(MIN_COMPRESSION_RATIO_PERCENT);
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
e2e/generate-invoice-link.test.tssrc/app/(app)/page.client.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use test data factories in tests
Applied to files:
e2e/generate-invoice-link.test.ts
📚 Learning: 2025-07-20T22:00:37.696Z
Learnt from: CR
Repo: VladSez/easy-invoice-pdf PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-20T22:00:37.696Z
Learning: Applies to **/*.test.{ts,tsx} : Use meaningful assertions in tests
Applied to files:
e2e/generate-invoice-link.test.ts
🧬 Code graph analysis (2)
e2e/generate-invoice-link.test.ts (1)
src/app/schema/index.ts (2)
SellerData(533-533)BuyerData(574-574)
src/app/(app)/page.client.tsx (4)
src/app/(app)/utils/get-app-metadata.tsx (2)
getAppMetadata(30-58)DEFAULT_METADATA(13-18)src/app/schema/index.ts (3)
METADATA_LOCAL_STORAGE_KEY(807-807)PDF_DATA_LOCAL_STORAGE_KEY(753-753)InvoiceData(751-751)src/utils/url-compression.ts (1)
decompressInvoiceData(239-243)src/app/(app)/utils/invoice-number-breaking-change.ts (1)
handleInvoiceNumberBreakingChange(18-66)
🔇 Additional comments (5)
src/app/(app)/page.client.tsx (2)
477-492: Footer copy update looks good.“Made by” is consistent and reads better.
235-243: No action needed.isLocalStorageAvailableis a const holding a boolean value (the result ofstorageAvailable("localStorage")), not a function. The guardif (!isLocalStorageAvailable)correctly checks whether localStorage is unavailable and properly returns early.Likely an incorrect or invalid review comment.
e2e/generate-invoice-link.test.ts (3)
4-36: LGTM! Excellent use of test data factories.The test data constants follow best practices with type-safe
as const satisfiespattern and provide reusable, well-structured test data. This aligns well with the project's testing guidelines.
127-143: LGTM! Comprehensive tax settings coverage.The test properly verifies the new customizable tax label functionality and validates the total calculation (5 × 100 × 1.23 = 615.00).
147-147: The 700ms timeout appropriately exceeds the application'sDEBOUNCE_TIMEOUTof 500ms (defined insrc/app/(app)/components/invoice-form/index.tsx). While hardcoded timeouts in e2e tests can be fragile, the 200ms buffer provides reasonable cushion for slower CI environments. This is acceptable as-is, though consider adding a comment explaining the relationship to the app's 500ms debounce if the timeout needs adjustment in the future.
| function InvoicePageHeader({ | ||
| canShareInvoice, | ||
| handleShareInvoice, | ||
| isDesktop, | ||
| invoiceDataState, | ||
| errorWhileGeneratingPdfIsShown, | ||
| setErrorWhileGeneratingPdfIsShown, | ||
| }: { | ||
| canShareInvoice: boolean; | ||
| handleShareInvoice: () => void; | ||
| isDesktop: boolean; | ||
| invoiceDataState: InvoiceData; | ||
| errorWhileGeneratingPdfIsShown: boolean; | ||
| setErrorWhileGeneratingPdfIsShown: (value: boolean) => void; | ||
| }) { |
There was a problem hiding this comment.
Fix prop typing: handleShareInvoice is async but typed as () => void.
handleShareInvoice returns Promise<void> but InvoicePageHeader declares it as () => void, which should be a TS type error.
Proposed diff
function InvoicePageHeader({
canShareInvoice,
handleShareInvoice,
isDesktop,
invoiceDataState,
errorWhileGeneratingPdfIsShown,
setErrorWhileGeneratingPdfIsShown,
}: {
canShareInvoice: boolean;
- handleShareInvoice: () => void;
+ handleShareInvoice: () => Promise<void>;
isDesktop: boolean;
invoiceDataState: InvoiceData;
errorWhileGeneratingPdfIsShown: boolean;
setErrorWhileGeneratingPdfIsShown: (value: boolean) => void;
}) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function InvoicePageHeader({ | |
| canShareInvoice, | |
| handleShareInvoice, | |
| isDesktop, | |
| invoiceDataState, | |
| errorWhileGeneratingPdfIsShown, | |
| setErrorWhileGeneratingPdfIsShown, | |
| }: { | |
| canShareInvoice: boolean; | |
| handleShareInvoice: () => void; | |
| isDesktop: boolean; | |
| invoiceDataState: InvoiceData; | |
| errorWhileGeneratingPdfIsShown: boolean; | |
| setErrorWhileGeneratingPdfIsShown: (value: boolean) => void; | |
| }) { | |
| function InvoicePageHeader({ | |
| canShareInvoice, | |
| handleShareInvoice, | |
| isDesktop, | |
| invoiceDataState, | |
| errorWhileGeneratingPdfIsShown, | |
| setErrorWhileGeneratingPdfIsShown, | |
| }: { | |
| canShareInvoice: boolean; | |
| handleShareInvoice: () => Promise<void>; | |
| isDesktop: boolean; | |
| invoiceDataState: InvoiceData; | |
| errorWhileGeneratingPdfIsShown: boolean; | |
| setErrorWhileGeneratingPdfIsShown: (value: boolean) => void; | |
| }) { |
🤖 Prompt for AI Agents
In @src/app/(app)/page.client.tsx around lines 550 - 564, The prop typing for
handleShareInvoice is wrong: InvoicePageHeader currently types it as () => void
but the actual implementation is async and returns Promise<void>; update the
prop type in the InvoicePageHeader parameter/type annotation to () =>
Promise<void> (and update any related prop interfaces or usages that declare
handleShareInvoice) so the component signature matches the async function.
Summary by CodeRabbit
New Features
Bug Fixes & Improvements
Removed
Tests
✏️ Tip: You can customize this high-level summary in your review settings.