From 72d368173ba9dabbcdbec52298d54ed0226c7a74 Mon Sep 17 00:00:00 2001 From: trinnode Date: Sat, 17 Oct 2026 15:23:22 +0100 Subject: [PATCH] fix(frontend): unify modal focus traps, guard confirmation backdrops, dedupe routes - ConfirmationModal & WithdrawalConfirmationModal now use the shared useFocusTrap hook, ignore backdrop clicks and Escape while processing, expose dialog/aria-modal/aria-labelledby, and route all copy through t() - Drawer adopts useFocusTrap so focus is restored on both close and unmount - ToastCenter closes on Escape, moves focus into the panel, and stays a non-modal, non-scroll-locking popover - useFocusTrap gains trapTab and lockScroll options (backward compatible) - App.tsx drops duplicate route/ErrorBoundary definitions and wraps the not-found route in RouteErrorBoundary - Remove stale tracked build/test artifacts and extend .gitignore + README - Repair invalid root package.json (duplicate test script + trailing comma) - Add modal, drawer, toast-center and routing smoke tests Closes #1620 Closes #1621 Closes #1629 Closes #1564 --- frontend/.gitignore | 12 + frontend/README.md | 4 + frontend/bundle-stats.html | 4950 ----------------- frontend/lint-output.txt | Bin 1220 -> 0 bytes frontend/lint-output2.txt | 32 - frontend/lint-output3.txt | 12 - frontend/lint.txt | 26 - frontend/src/App.tsx | 19 +- .../src/components/ConfirmationModal.test.tsx | 125 + frontend/src/components/ConfirmationModal.tsx | 109 +- frontend/src/components/Drawer.test.tsx | 58 + frontend/src/components/Drawer.tsx | 84 +- frontend/src/components/ToastCenter.test.tsx | 67 + frontend/src/components/ToastCenter.tsx | 23 +- .../WithdrawalConfirmationModal.test.tsx | 118 + .../WithdrawalConfirmationModal.tsx | 16 +- frontend/src/hooks/useFocusTrap.ts | 20 +- frontend/src/i18n/locales/en.ts | 34 + frontend/src/i18n/locales/es.ts | 34 + frontend/src/tests/routes.test.tsx | 133 + frontend/test-output.txt | 5 - frontend/test-results.txt | 1 - frontend/test_output.json | Bin 180716 -> 0 bytes frontend/vitest-results.json | 2 - frontend/vitest-results2.json | 2 - frontend/vitest-results3.json | 2 - package.json | 4 +- 27 files changed, 686 insertions(+), 5206 deletions(-) delete mode 100644 frontend/bundle-stats.html delete mode 100644 frontend/lint-output.txt delete mode 100644 frontend/lint-output2.txt delete mode 100644 frontend/lint-output3.txt delete mode 100644 frontend/lint.txt create mode 100644 frontend/src/components/ConfirmationModal.test.tsx create mode 100644 frontend/src/components/ToastCenter.test.tsx create mode 100644 frontend/src/components/WithdrawalConfirmationModal.test.tsx create mode 100644 frontend/src/tests/routes.test.tsx delete mode 100644 frontend/test-output.txt delete mode 100644 frontend/test-results.txt delete mode 100644 frontend/test_output.json delete mode 100644 frontend/vitest-results.json delete mode 100644 frontend/vitest-results2.json delete mode 100644 frontend/vitest-results3.json diff --git a/frontend/.gitignore b/frontend/.gitignore index 6a56a8d1c..4bd9e15c7 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -29,3 +29,15 @@ GEMINI.md # Playwright test-results/ playwright-report/ + +# Generated lint / test / bundle reports (run locally, never commit) +lint-output*.txt +lint.txt +test-results.txt +test-output.txt +test_output.json +vitest-results*.json +bundle-stats.html + +# Local backups +*.bak diff --git a/frontend/README.md b/frontend/README.md index 5da3cb36c..e0ae6550e 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -38,6 +38,10 @@ npm run test:run npm run test:e2e ``` +## Generated Reports + +Lint, test and bundle reports are **generated locally and must not be committed**. Write them to the git-ignored `test-results/` directory, or delete them before committing. The common artifacts (`lint-output*.txt`, `lint.txt`, `test-results.txt`, `test-output.txt`, `test_output.json`, `vitest-results*.json`, `bundle-stats.html`) are covered by `.gitignore`. + ## Related Docs - API docs output: [`docs/api/frontend`](/Users/macbook/stellar/YieldVault-RWA/docs/api/frontend) diff --git a/frontend/bundle-stats.html b/frontend/bundle-stats.html deleted file mode 100644 index f21f1996c..000000000 --- a/frontend/bundle-stats.html +++ /dev/null @@ -1,4950 +0,0 @@ - - - - - - - - Rollup Visualizer - - - -
- - - - - diff --git a/frontend/lint-output.txt b/frontend/lint-output.txt deleted file mode 100644 index 8be0feddf9be725abc58b1daf10f7953fcbad9e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1220 zcmb7^&raJw7{o{7R;ll>;sTX$8p5H10|L=Qj};QFkT_t10SgjGcHFej!6WdPNVLCM z+qfiDDvIpve*b57*1wz(!$bfr>-rkX3`eW?QtHE5rpJLS>WMQ5s>ZGkx3yXQ!I zI@1Xe88s``U-XstXZDqD=}>6K{*;;-RW9{SscKepB|6r39pYV{=Zx49k3G455?fQ< z6$h-V*gb$_Tf6$i1Usz#TTr`XcF%tuEhnsMGBP@cSfN9gV9D{Mx?q|NulMdUrbn(H z(Y8r`fp3PVXd@?Nxt9?;&+i%DlDwu*gJ*(oBYM+VGtmuE1*}y1h}|q0Q-bY^ckkWM zhi8l<&SeP3ZGOh+uAZfgJ6n#iH5~%%Hywi3TiokJ7-wLHl#cFcF3gdYQ1jm)wj!LD zf@a1krf`_C58%3CH;-AaG{v4rtEUP0UQV%Ebxh=C z4wfQc?~zi`EC>Fr1b6n^O7VKpA~6c%uOoWnGGA7XZ$w6{Pq##Y|C(<~{QvecqprM& z9QZu`KCeboZ&giqC-2uD^hm`K&#G=WqmFtNZ4qT frontend@0.0.0 lint -> eslint . - - -C:\Users\HP\Desktop\YieldVault-RWA\frontend\e2e\fixtures.ts - 199:11 error React Hook "use" is called in function "appPage" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use" react-hooks/rules-of-hooks - -C:\Users\HP\Desktop\YieldVault-RWA\frontend\src\components\WalletConnect.test.tsx - 1:46 error 'act' is defined but never used @typescript-eslint/no-unused-vars - 104:15 error 'flushPromises' is assigned a value but never used @typescript-eslint/no-unused-vars - -C:\Users\HP\Desktop\YieldVault-RWA\frontend\src\forms\useForm.ts - 18:44 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 28:41 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -C:\Users\HP\Desktop\YieldVault-RWA\frontend\src\hooks\useUrlState.ts - 48:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 83:47 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -C:\Users\HP\Desktop\YieldVault-RWA\frontend\src\lib\vaultApi.ts - 97:9 error 'payload' is assigned a value but never used @typescript-eslint/no-unused-vars - 103:9 error 'payload' is assigned a value but never used @typescript-eslint/no-unused-vars - -C:\Users\HP\Desktop\YieldVault-RWA\frontend\src\pages\Portfolio.tsx - 171:6 warning React Hook useEffect has a missing dependency: 'urlState.filters.status'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -C:\Users\HP\Desktop\YieldVault-RWA\frontend\src\pages\TransactionHistory.test.tsx - 275:59 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -Γ£û 11 problems (10 errors, 1 warning) - diff --git a/frontend/lint-output3.txt b/frontend/lint-output3.txt deleted file mode 100644 index 7eca4d5fa..000000000 --- a/frontend/lint-output3.txt +++ /dev/null @@ -1,12 +0,0 @@ - -> frontend@0.0.0 lint -> eslint . - - -C:\Users\HP\Desktop\YieldVault-RWA\frontend\e2e\fixtures.ts - 197:3 warning Unused eslint-disable directive (no problems were reported from 'react-hooks/rules-of-hooks') - 200:11 error React Hook "use" is called in function "appPage" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use" react-hooks/rules-of-hooks - -Γ£û 2 problems (1 error, 1 warning) - 0 errors and 1 warning potentially fixable with the `--fix` option. - diff --git a/frontend/lint.txt b/frontend/lint.txt deleted file mode 100644 index 3764dc173..000000000 --- a/frontend/lint.txt +++ /dev/null @@ -1,26 +0,0 @@ - -> frontend@0.0.0 lint -> eslint . - - -C:\Users\HP\Desktop\YieldVault-RWA\frontend\e2e\fixtures.ts - 199:11 error React Hook "use" is called in function "appPage" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use" react-hooks/rules-of-hooks - -C:\Users\HP\Desktop\YieldVault-RWA\frontend\src\components\VaultDashboard.tsx - 61:90 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 103:67 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -C:\Users\HP\Desktop\YieldVault-RWA\frontend\src\components\WalletConnect.test.tsx - 1:46 error 'act' is defined but never used @typescript-eslint/no-unused-vars - 104:15 error 'flushPromises' is assigned a value but never used @typescript-eslint/no-unused-vars - -C:\Users\HP\Desktop\YieldVault-RWA\frontend\src\forms\useForm.ts - 18:44 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 28:41 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -C:\Users\HP\Desktop\YieldVault-RWA\frontend\src\hooks\useUrlState.ts - 48:23 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 83:47 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -Γ£û 9 problems (9 errors, 0 warnings) - diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 9df000a83..8d1a2b6f3 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -221,14 +221,6 @@ function AppContent() { } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - - + + + } /> - } /> diff --git a/frontend/src/components/ConfirmationModal.test.tsx b/frontend/src/components/ConfirmationModal.test.tsx new file mode 100644 index 000000000..4c1eaaba6 --- /dev/null +++ b/frontend/src/components/ConfirmationModal.test.tsx @@ -0,0 +1,125 @@ +import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { useState } from "react"; +import ConfirmationModal from "./ConfirmationModal"; + +const baseProps = { + onClose: vi.fn(), + onConfirm: vi.fn(), + actionType: "deposit" as const, + amount: 100, + expectedOutput: 99.5, + estimatedFee: 0.5, +}; + +function Harness({ isProcessing = false }: { isProcessing?: boolean }) { + const [open, setOpen] = useState(false); + return ( +
+ + { + baseProps.onClose(); + setOpen(false); + }} + /> +
+ ); +} + +const FOCUSABLE_SELECTOR = + 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'; + +describe("ConfirmationModal", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("renders nothing while closed", () => { + render(); + expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); + }); + + it("renders the dialog with translated copy when open", () => { + render(); + const dialog = screen.getByRole("dialog"); + expect(dialog).toHaveAttribute("aria-modal", "true"); + expect(screen.getByRole("heading", { name: "Confirm Deposit" })).toBeInTheDocument(); + expect(screen.getByText("Estimated Fee")).toBeInTheDocument(); + expect(screen.getByText("Total Cost")).toBeInTheDocument(); + }); + + it("shows the large-amount alert for amounts over the threshold", () => { + render(); + expect(screen.getByRole("alert")).toHaveTextContent("Large transaction amount detected"); + }); + + it("moves focus into the dialog on open", async () => { + render(); + const dialog = screen.getByRole("dialog"); + await waitFor(() => { + expect(dialog.contains(document.activeElement)).toBe(true); + }); + }); + + it("calls onConfirm when the confirm button is clicked", () => { + render(); + fireEvent.click(screen.getByLabelText("Confirm deposit")); + expect(baseProps.onConfirm).toHaveBeenCalledTimes(1); + }); + + it("closes on Escape", async () => { + render(); + fireEvent.keyDown(document, { key: "Escape" }); + await waitFor(() => expect(baseProps.onClose).toHaveBeenCalledTimes(1)); + }); + + it("closes on backdrop click", () => { + render(); + fireEvent.click(screen.getByRole("dialog")); + expect(baseProps.onClose).toHaveBeenCalledTimes(1); + }); + + it("ignores backdrop clicks while processing", () => { + render(); + fireEvent.click(screen.getByRole("dialog")); + expect(baseProps.onClose).not.toHaveBeenCalled(); + }); + + it("ignores Escape while processing", () => { + render(); + fireEvent.keyDown(document, { key: "Escape" }); + expect(baseProps.onClose).not.toHaveBeenCalled(); + }); + + it("does not close when clicking inside the panel", () => { + render(); + fireEvent.click(screen.getByRole("heading", { name: "Confirm Deposit" })); + expect(baseProps.onClose).not.toHaveBeenCalled(); + }); + + it("wraps Tab focus from the last element back to the first", () => { + render(); + const dialog = screen.getByRole("dialog"); + const focusables = dialog.querySelectorAll(FOCUSABLE_SELECTOR); + const last = focusables[focusables.length - 1]; + last.focus(); + fireEvent.keyDown(document, { key: "Tab" }); + expect(document.activeElement).toBe(focusables[0]); + }); + + it("restores focus to the trigger after closing", async () => { + render(); + const trigger = screen.getByTestId("trigger"); + trigger.focus(); + fireEvent.click(trigger); + await screen.findByRole("dialog"); + fireEvent.keyDown(document, { key: "Escape" }); + await waitFor(() => expect(trigger).toHaveFocus()); + }); +}); diff --git a/frontend/src/components/ConfirmationModal.tsx b/frontend/src/components/ConfirmationModal.tsx index 852fd5550..c1af35062 100644 --- a/frontend/src/components/ConfirmationModal.tsx +++ b/frontend/src/components/ConfirmationModal.tsx @@ -1,6 +1,8 @@ -import React, { useEffect, useRef, useCallback } from "react"; +import React from "react"; import { createPortal } from "react-dom"; import { AlertTriangle, X } from "./icons"; +import { useFocusTrap } from "../hooks/useFocusTrap"; +import { useTranslation } from "../i18n"; interface ConfirmationModalProps { isOpen: boolean; @@ -25,65 +27,25 @@ const ConfirmationModal: React.FC = ({ estimatedFee, isProcessing = false, }) => { - const modalRef = useRef(null); - const previousFocusRef = useRef(null); + const { t } = useTranslation(); + const modalRef = useFocusTrap({ + active: isOpen, + onEscape: onClose, + closeOnEscape: !isProcessing, + }); const isLargeAmount = amount >= LARGE_AMOUNT_THRESHOLD; - useEffect(() => { - if (isOpen) { - previousFocusRef.current = document.activeElement as HTMLElement; - requestAnimationFrame(() => { - const firstInteractive = modalRef.current?.querySelector( - 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' - ); - firstInteractive?.focus(); - }); - } - return () => { - if (isOpen) { - previousFocusRef.current?.focus(); - } - }; - }, [isOpen]); - - const handleKeyDown = useCallback( - (event: React.KeyboardEvent) => { - if (event.key === "Escape" && !isProcessing) { - onClose(); - return; - } - if (event.key !== "Tab" || !modalRef.current) return; - - const focusable = modalRef.current.querySelectorAll( - 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' - ); - if (focusable.length === 0) { - event.preventDefault(); - return; - } - const firstElement = focusable[0]; - const lastElement = focusable[focusable.length - 1]; - const activeElement = document.activeElement as HTMLElement | null; - - if (event.shiftKey && activeElement === firstElement) { - event.preventDefault(); - lastElement.focus(); - } else if (!event.shiftKey && activeElement === lastElement) { - event.preventDefault(); - firstElement.focus(); - } - }, - [isProcessing, onClose] - ); - if (!isOpen) return null; - const title = actionType === "deposit" ? "Confirm Deposit" : "Confirm Withdrawal"; + const isDeposit = actionType === "deposit"; + const title = t(isDeposit ? "confirmationModal.depositTitle" : "confirmationModal.withdrawTitle"); const subtitle = isLargeAmount - ? "This is a large transaction. Please review carefully before proceeding." - : "Please review your transaction details before confirming."; - const outputLabel = actionType === "deposit" ? "Shares to Receive" : "USDC to Receive"; + ? t("confirmationModal.largeAmountSubtitle") + : t("confirmationModal.defaultSubtitle"); + const outputLabel = t( + isDeposit ? "confirmationModal.sharesToReceive" : "confirmationModal.usdcToReceive", + ); const modalContent = (
= ({ zIndex: 1000, padding: "16px", }} - onClick={onClose} - onKeyDown={handleKeyDown} + onClick={() => { + if (!isProcessing) onClose(); + }} >
= ({
)} @@ -207,7 +170,7 @@ const ConfirmationModal: React.FC = ({ }} > - {actionType === "deposit" ? "Deposit Amount" : "Withdraw Amount"} + {t(isDeposit ? "confirmationModal.depositAmount" : "confirmationModal.withdrawAmount")} = ({ fontFamily: "var(--font-display)", }} > - {expectedOutput.toFixed(6)} {actionType === "deposit" ? "Shares" : "USDC"} + {expectedOutput.toFixed(6)} {isDeposit ? "Shares" : "USDC"}
@@ -251,7 +214,7 @@ const ConfirmationModal: React.FC = ({ }} > - Estimated Fee + {t("confirmationModal.estimatedFee")} = ({ }} > - Total Cost + {t("confirmationModal.totalCost")} = ({ }} >

- Note: - {actionType === "deposit" - ? "Depositing USDC will mint vault shares. This action cannot be undone once confirmed on the Stellar network." - : "Withdrawing USDC will burn your vault shares. Make sure you have sufficient shares to cover this withdrawal."} + {t("confirmationModal.noteLabel")} + {t( + isDeposit ? "confirmationModal.depositNote" : "confirmationModal.withdrawNote", + )}

@@ -316,7 +279,7 @@ const ConfirmationModal: React.FC = ({ diff --git a/frontend/src/components/Drawer.test.tsx b/frontend/src/components/Drawer.test.tsx index a869f766f..83d44451d 100644 --- a/frontend/src/components/Drawer.test.tsx +++ b/frontend/src/components/Drawer.test.tsx @@ -1,5 +1,6 @@ import { render, screen, fireEvent, waitFor } from "@testing-library/react"; import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { useState } from "react"; import { Drawer } from "./Drawer"; describe("Drawer", () => { @@ -95,4 +96,61 @@ describe("Drawer", () => { expect(document.activeElement).toBe(focusableElements[0]); }); + + it("restores focus to the trigger after closing", async () => { + function Harness() { + const [open, setOpen] = useState(false); + return ( + <> + + setOpen(false)} title="Drawer Title"> + + + + ); + } + + render(); + const trigger = screen.getByTestId("drawer-trigger"); + trigger.focus(); + fireEvent.click(trigger); + await screen.findByRole("dialog"); + + fireEvent.keyDown(document, { key: "Escape" }); + + await waitFor(() => expect(trigger).toHaveFocus()); + }); + + it("restores focus when the drawer unmounts", async () => { + function Harness() { + const [mounted, setMounted] = useState(false); + return ( + <> + + + {mounted && ( + {}} title="Drawer Title"> + + + )} + + ); + } + + render(); + const mount = screen.getByTestId("mount"); + mount.focus(); + fireEvent.click(mount); + await screen.findByRole("dialog"); + + fireEvent.click(screen.getByTestId("unmount")); + + await waitFor(() => expect(mount).toHaveFocus()); + }); }); diff --git a/frontend/src/components/Drawer.tsx b/frontend/src/components/Drawer.tsx index 171b0c69e..41a47f52a 100644 --- a/frontend/src/components/Drawer.tsx +++ b/frontend/src/components/Drawer.tsx @@ -1,6 +1,8 @@ -import React, { useEffect, useRef, useCallback } from "react"; +import React from "react"; import { createPortal } from "react-dom"; import { X } from "lucide-react"; +import { useFocusTrap } from "../hooks/useFocusTrap"; +import { useTranslation } from "../i18n"; export interface DrawerProps { isOpen: boolean; @@ -16,9 +18,6 @@ export interface DrawerProps { "aria-describedby"?: string; } -const FOCUSABLE_SELECTOR = - 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'; - export const Drawer: React.FC = ({ isOpen, onClose, @@ -32,75 +31,12 @@ export const Drawer: React.FC = ({ "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, }) => { - const panelRef = useRef(null); - const previousFocusRef = useRef(null); - - const handleKeyDown = useCallback( - (e: KeyboardEvent) => { - if (!isOpen) return; - - if (e.key === "Escape" && closeOnEscape) { - onClose(); - return; - } - - if (e.key === "Tab" && panelRef.current) { - const focusableElements = - panelRef.current.querySelectorAll(FOCUSABLE_SELECTOR); - - if (focusableElements.length === 0) { - e.preventDefault(); - return; - } - - const firstElement = focusableElements[0]; - const lastElement = focusableElements[focusableElements.length - 1]; - - if (e.shiftKey) { - if ( - document.activeElement === firstElement || - document.activeElement === panelRef.current - ) { - lastElement.focus(); - e.preventDefault(); - } - } else if (document.activeElement === lastElement) { - firstElement.focus(); - e.preventDefault(); - } - } - }, - [isOpen, onClose, closeOnEscape], - ); - - useEffect(() => { - if (isOpen) { - previousFocusRef.current = document.activeElement as HTMLElement; - document.addEventListener("keydown", handleKeyDown); - document.body.style.overflow = "hidden"; - - if (panelRef.current) { - const focusableElements = - panelRef.current.querySelectorAll(FOCUSABLE_SELECTOR); - if (focusableElements.length > 0) { - focusableElements[0].focus(); - } else { - panelRef.current.focus(); - } - } - } else { - document.removeEventListener("keydown", handleKeyDown); - document.body.style.overflow = ""; - if (previousFocusRef.current) { - previousFocusRef.current.focus(); - } - } - - return () => { - document.removeEventListener("keydown", handleKeyDown); - document.body.style.overflow = ""; - }; - }, [isOpen, handleKeyDown]); + const { t } = useTranslation(); + const panelRef = useFocusTrap({ + active: isOpen, + onEscape: onClose, + closeOnEscape, + }); if (!isOpen) return null; @@ -146,7 +82,7 @@ export const Drawer: React.FC = ({ + { + baseProps.onCancel(); + setOpen(false); + }} + /> + + ); +} + +const FOCUSABLE_SELECTOR = + 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'; + +describe("WithdrawalConfirmationModal", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("renders nothing while closed", () => { + render(); + expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); + }); + + it("renders the dialog with translated copy when open", () => { + render(); + const dialog = screen.getByRole("dialog"); + expect(dialog).toHaveAttribute("aria-modal", "true"); + expect(screen.getByRole("heading", { name: "Confirm Withdrawal" })).toBeInTheDocument(); + expect(screen.getByText("Estimated Network Fee")).toBeInTheDocument(); + expect(screen.getByText("Total Deducted")).toBeInTheDocument(); + }); + + it("moves focus into the dialog on open", async () => { + render(); + const dialog = screen.getByRole("dialog"); + await waitFor(() => { + expect(dialog.contains(document.activeElement)).toBe(true); + }); + }); + + it("calls onConfirm when the confirm button is clicked", () => { + render(); + fireEvent.click(screen.getByRole("button", { name: "Confirm Withdrawal" })); + expect(baseProps.onConfirm).toHaveBeenCalledTimes(1); + }); + + it("closes via onCancel on Escape", async () => { + render(); + fireEvent.keyDown(document, { key: "Escape" }); + await waitFor(() => expect(baseProps.onCancel).toHaveBeenCalledTimes(1)); + }); + + it("closes on backdrop click when idle", () => { + render(); + fireEvent.click(screen.getByRole("dialog")); + expect(baseProps.onCancel).toHaveBeenCalledTimes(1); + }); + + it("ignores backdrop clicks while processing", () => { + render(); + fireEvent.click(screen.getByRole("dialog")); + expect(baseProps.onCancel).not.toHaveBeenCalled(); + }); + + it("ignores Escape while processing", () => { + render(); + fireEvent.keyDown(document, { key: "Escape" }); + expect(baseProps.onCancel).not.toHaveBeenCalled(); + }); + + it("does not close when clicking inside the panel", () => { + render(); + fireEvent.click(screen.getByRole("heading", { name: "Confirm Withdrawal" })); + expect(baseProps.onCancel).not.toHaveBeenCalled(); + }); + + it("wraps Tab focus from the last element back to the first", () => { + render(); + const dialog = screen.getByRole("dialog"); + const focusables = dialog.querySelectorAll(FOCUSABLE_SELECTOR); + const last = focusables[focusables.length - 1]; + last.focus(); + fireEvent.keyDown(document, { key: "Tab" }); + expect(document.activeElement).toBe(focusables[0]); + }); + + it("restores focus to the trigger after closing", async () => { + render(); + const trigger = screen.getByTestId("trigger"); + trigger.focus(); + fireEvent.click(trigger); + await screen.findByRole("dialog"); + fireEvent.keyDown(document, { key: "Escape" }); + await waitFor(() => expect(trigger).toHaveFocus()); + }); +}); diff --git a/frontend/src/components/WithdrawalConfirmationModal.tsx b/frontend/src/components/WithdrawalConfirmationModal.tsx index 438decbdf..a03ba758f 100644 --- a/frontend/src/components/WithdrawalConfirmationModal.tsx +++ b/frontend/src/components/WithdrawalConfirmationModal.tsx @@ -1,6 +1,7 @@ import React from "react"; import { X } from "./icons"; import { useTranslation } from "../i18n"; +import { useFocusTrap } from "../hooks/useFocusTrap"; interface WithdrawalConfirmationModalProps { isOpen: boolean; @@ -20,6 +21,11 @@ const WithdrawalConfirmationModal: React.FC = isProcessing = false, }) => { const { t } = useTranslation(); + const panelRef = useFocusTrap({ + active: isOpen, + onEscape: onCancel, + closeOnEscape: !isProcessing, + }); if (!isOpen) return null; @@ -27,6 +33,9 @@ const WithdrawalConfirmationModal: React.FC = return (
= zIndex: 1000, padding: "16px", }} - onClick={onCancel} + onClick={() => { + if (!isProcessing) onCancel(); + }} >
= instead\u001b[39m\n at Proxy.expectWrapper (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/expect/dist/index.js:1882:12)\n at Proxy. (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/expect/dist/index.js:1155:15)\n at Proxy.methodWrapper (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/chai/index.js:1700:25)\n at C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/components/VaultDashboard.test.tsx:120:11\n at file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:1893:20"],"meta":{},"tags":[]},{"ancestorTitles":["VaultDashboard"],"fullName":"VaultDashboard shows a normalized API error message when data loading fails","status":"passed","title":"shows a normalized API error message when data loading fails","duration":1451.6965,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558028613,"endTime":1774558031334.6965,"status":"failed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/components/VaultDashboard.test.tsx"},{"assertionResults":[{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect renders the connect button when no wallet is connected","status":"passed","title":"renders the connect button when no wallet is connected","duration":168.3107,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect calls onConnect when manually connected via button","status":"passed","title":"calls onConnect when manually connected via button","duration":68.8127999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect shows the formatted address when connected","status":"passed","title":"shows the formatted address when connected","duration":56.05449999999837,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect calls onDisconnect when the disconnect button is clicked","status":"passed","title":"calls onDisconnect when the disconnect button is clicked","duration":34.47199999999975,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect handles wallet disconnects gracefully during polling","status":"passed","title":"handles wallet disconnects gracefully during polling","duration":18.457099999999627,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558034539,"endTime":1774558034885.457,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/components/WalletConnect.test.tsx"},{"assertionResults":[{"ancestorTitles":["ToastProvider"],"fullName":"ToastProvider shows and auto-dismisses toasts","status":"passed","title":"shows and auto-dismisses toasts","duration":754.0685000000012,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ToastProvider"],"fullName":"ToastProvider supports manual dismissal","status":"passed","title":"supports manual dismissal","duration":160.45460000000094,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558024330,"endTime":1774558025244.4546,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/context/ToastContext.test.tsx"},{"assertionResults":[{"ancestorTitles":["useForm"],"fullName":"useForm returns initial state","status":"passed","title":"returns initial state","duration":77.12700000000041,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm handleChange updates values","status":"passed","title":"handleChange updates values","duration":10.769800000000032,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm handleBlur marks touched and validates field","status":"passed","title":"handleBlur marks touched and validates field","duration":9.869499999999789,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm handleSubmit validates all fields and blocks submit when invalid","status":"passed","title":"handleSubmit validates all fields and blocks submit when invalid","duration":16.84859999999935,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm tracks isSubmitting lifecycle for valid submit","status":"passed","title":"tracks isSubmitting lifecycle for valid submit","duration":32.610999999999876,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm setFieldError stores server-side error","status":"passed","title":"setFieldError stores server-side error","duration":7.341199999999844,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558033051,"endTime":1774558033206.3413,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/forms/useForm.test.tsx"},{"assertionResults":[{"ancestorTitles":["validate"],"fullName":"validate fails when required field is missing","status":"passed","title":"fails when required field is missing","duration":6.921400000000176,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate"],"fullName":"validate fails min length and passes when min is met","status":"passed","title":"fails min length and passes when min is met","duration":1.4904999999998836,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate"],"fullName":"validate fails max length and passes when within max","status":"passed","title":"fails max length and passes when within max","duration":0.9960000000000946,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate"],"fullName":"validate fails pattern and passes matching pattern","status":"passed","title":"fails pattern and passes matching pattern","duration":0.7665999999999258,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate"],"fullName":"validate runs custom validator","status":"passed","title":"runs custom validator","duration":0.623000000000502,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558037289,"endTime":1774558037300.623,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/forms/validate.test.ts"},{"assertionResults":[{"ancestorTitles":["stellarAccount"],"fullName":"stellarAccount returns null when Freighter is not allowed","status":"passed","title":"returns null when Freighter is not allowed","duration":5.859400000000278,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stellarAccount"],"fullName":"stellarAccount returns discovered address when Freighter is allowed","status":"passed","title":"returns discovered address when Freighter is allowed","duration":0.9382999999997992,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stellarAccount"],"fullName":"stellarAccount extracts USDC balance from account balances","status":"passed","title":"extracts USDC balance from account balances","duration":1.1228000000000975,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558036645,"endTime":1774558036653.1228,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/lib/stellarAccount.test.ts"},{"assertionResults":[{"ancestorTitles":["Portfolio"],"fullName":"Portfolio shows the wallet prompt when disconnected","status":"passed","title":"shows the wallet prompt when disconnected","duration":228.5676000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Portfolio"],"fullName":"Portfolio renders holdings in the reusable table","status":"failed","title":"renders holdings in the reusable table","duration":1266.179900000001,"failureMessages":["Error: Unable to find an element with the text: /Tokenized T-Bills/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mYour \u001b[0m\n \u001b[36m\u001b[39m\n \u001b[0mPortfolio\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mOverview of your deposited real-world assets.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mData unavailable\u001b[0m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mInvalid value for \"walletAddress\": Must be a valid Stellar public key (starts with G, 56 chars).\u001b[0m\n \u001b[36m
\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mTotal Assets\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m$0.00\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mUnrealized Gain\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m+\u001b[0m\n \u001b[0m$0.00\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mHoldings\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mSort, search, and page through all current vault positions.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mStatus Filter\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mAll Statuses\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mActive\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mInactive\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mSearch holdings\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m0 holdings found\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m)\n at runWithCancel (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:2313:10)\n at file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:2295:20\n at new Promise ()"],"meta":{},"tags":[]},{"ancestorTitles":["Portfolio"],"fullName":"Portfolio persists filter state in the URL","status":"failed","title":"persists filter state in the URL","duration":1227.9897999999994,"failureMessages":["Error: Unable to find an element with the text: /Tokenized T-Bills/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mYour \u001b[0m\n \u001b[36m\u001b[39m\n \u001b[0mPortfolio\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mOverview of your deposited real-world assets.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mData unavailable\u001b[0m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mInvalid value for \"walletAddress\": Must be a valid Stellar public key (starts with G, 56 chars).\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mTotal Assets\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m$0.00\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mUnrealized Gain\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m+\u001b[0m\n \u001b[0m$0.00\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mHoldings\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mSort, search, and page through all current vault positions.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mStatus Filter\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mAll Statuses\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mActive\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mInactive\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mSearch holdings\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mReset Filters\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m0 holdings found\u001b[...\n at waitForWrapper (C:\\Users\\HP\\Desktop\\YieldVault-RWA\\frontend\\node_modules\\@testing-library\\dom\\dist\\wait-for.js:163:27)\n at C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/pages/Portfolio.test.tsx:151:11\n at runNextTicks (node:internal/process/task_queues:65:5)\n at processTimers (node:internal/timers:526:9)\n at file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:1893:20"],"meta":{},"tags":[]},{"ancestorTitles":["Portfolio"],"fullName":"Portfolio supports keyboard sorting and pagination state from the URL","status":"failed","title":"supports keyboard sorting and pagination state from the URL","duration":1122.061099999999,"failureMessages":["Error: Unable to find an element with the text: /Yield Bearing Cash/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mYour \u001b[0m\n \u001b[36m\u001b[39m\n \u001b[0mPortfolio\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mOverview of your deposited real-world assets.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mData unavailable\u001b[0m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mInvalid value for \"walletAddress\": Must be a valid Stellar public key (starts with G, 56 chars).\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mTotal Assets\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m$0.00\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mUnrealized Gain\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m+\u001b[0m\n \u001b[0m$0.00\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mHoldings\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mSort, search, and page through all current vault positions.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mStatus Filter\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mAll Statuses\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mActive\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mInactive\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mSearch holdings\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m0 holdings found\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m)\n at runWithCancel (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:2313:10)\n at file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:2295:20\n at new Promise ()"],"meta":{},"tags":[]}],"startTime":1774558026135,"endTime":1774558029980.061,"status":"failed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/pages/Portfolio.test.tsx"},{"assertionResults":[{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders connect-wallet prompt when walletAddress is null","status":"passed","title":"renders connect-wallet prompt when walletAddress is null","duration":208.9276000000009,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory shows loading indicator while fetch is pending","status":"passed","title":"shows loading indicator while fetch is pending","duration":181.09439999999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory calls getTransactions with the correct wallet address on mount","status":"failed","title":"calls getTransactions with the correct wallet address on mount","duration":1110.4812000000002,"failureMessages":["AssertionError: expected \"vi.fn()\" to be called with arguments: [ 'GABC1234567890TESTWALLETADDRESS' ]\u001b[90m\n\nReceived:\n\n\u001b[1m 1st vi.fn() call:\n\n\u001b[22m\u001b[2m [\u001b[22m\n\u001b[32m- \"GABC1234567890TESTWALLETADDRESS\",\u001b[90m\n\u001b[31m+ {\u001b[90m\n\u001b[31m+ \"limit\": 10,\u001b[90m\n\u001b[31m+ \"order\": \"desc\",\u001b[90m\n\u001b[31m+ \"type\": \"all\",\u001b[90m\n\u001b[31m+ \"walletAddress\": \"GABC1234567890TESTWALLETADDRESS\",\u001b[90m\n\u001b[31m+ },\u001b[90m\n\u001b[2m ]\u001b[22m\n\u001b[39m\u001b[90m\n\nNumber of calls: \u001b[1m1\u001b[22m\n\u001b[39m\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mTransaction \u001b[0m\n \u001b[36m\u001b[39m\n \u001b[0mHistory\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mView all your past deposits and withdrawals.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mTransactions\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mSort and filter your deposit and withdrawal history.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mType\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mAll\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mDeposit\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mWithdrawal\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mRows\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m10\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m25\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m50\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m0 transactions found\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mTransaction history\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mType\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mΓåò\u001b[0m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mAmount\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mΓåò\u001b[0m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mAsset\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mDate\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mΓåô\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mTransaction Hash\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mNo transactions found for this wallet.\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mPage 1 of 1\u001b[0m\n \u001b[0m ΓÇö Showing \u001b[0m\n \u001b[0m0\u001b[0m\n \u001b[0mΓÇô\u001b[0m\n \u001b[0m0\u001b[0m\n \u001b[0m of \u001b[0m\n \u001b[0m0\u001b[0m\n \u001b[0m results\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m1\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n\u001b[36m\u001b[39m\n at Proxy. (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/expect/dist/index.js:1486:10)\n at Proxy. (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/expect/dist/index.js:1155:15)\n at Proxy.methodWrapper (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/chai/index.js:1700:25)\n at C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/pages/TransactionHistory.test.tsx:101:35\n at runWithExpensiveErrorDiagnosticsDisabled (C:\\Users\\HP\\Desktop\\YieldVault-RWA\\frontend\\node_modules\\@testing-library\\dom\\dist\\config.js:47:12)\n at checkCallback (C:\\Users\\HP\\Desktop\\YieldVault-RWA\\frontend\\node_modules\\@testing-library\\dom\\dist\\wait-for.js:124:77)\n at Timeout.checkRealTimersCallback (C:\\Users\\HP\\Desktop\\YieldVault-RWA\\frontend\\node_modules\\@testing-library\\dom\\dist\\wait-for.js:118:16)\n at listOnTimeout (node:internal/timers:594:17)\n at processTimers (node:internal/timers:529:7)"],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders the transaction table when wallet is connected and fetch succeeds","status":"passed","title":"renders the transaction table when wallet is connected and fetch succeeds","duration":785.9304000000011,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory shows ApiStatusBanner on fetch failure","status":"passed","title":"shows ApiStatusBanner on fetch failure","duration":255.60270000000128,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders correct column headers: Type, Amount, Asset, Date, Transaction Hash","status":"passed","title":"renders correct column headers: Type, Amount, Asset, Date, Transaction Hash","duration":468.1130000000012,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory has sort buttons for Type, Amount, Date but not for Asset and Transaction Hash","status":"passed","title":"has sort buttons for Type, Amount, Date but not for Asset and Transaction Hash","duration":366.6665000000012,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory default page size select shows 10","status":"passed","title":"default page size select shows 10","duration":244.6980999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders filter control with All, Deposit, and Withdrawal options","status":"passed","title":"renders filter control with All, Deposit, and Withdrawal options","duration":208.63750000000073,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory resets page to 1 when filter is applied","status":"passed","title":"resets page to 1 when filter is applied","duration":688.8478000000014,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders deposit badge with 'cyan' class and withdrawal badge with 'red' class","status":"passed","title":"renders deposit badge with 'cyan' class and withdrawal badge with 'red' class","duration":153.87179999999898,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory shows empty state message when wallet is connected but no transactions exist","status":"passed","title":"shows empty state message when wallet is connected but no transactions exist","duration":91.08209999999963,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory shows filtered empty state message when filter yields no results","status":"failed","title":"shows filtered empty state message when filter yields no results","duration":1335.3719,"failureMessages":["Error: Unable to find an element with the text: No transactions matched the current filter.. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mTransaction \u001b[0m\n \u001b[36m\u001b[39m\n \u001b[0mHistory\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mView all your past deposits and withdrawals.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mTransactions\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mSort and filter your deposit and withdrawal history.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mType\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mAll\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mDeposit\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mWithdrawal\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mRows\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m10\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m25\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m50\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m1 transactions found\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mTransaction history\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mType\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mΓåò\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mAmount\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mΓåò\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mAsset\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mDate\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mΓåô\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mTransaction Hash\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mdeposit\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m100.00 USDC\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mUSDC\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mJan 15, 2025, 11:30 AM\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mabcdef12...ef12\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mPage 1 of 1\u001b[0m\n \u001b[0m ΓÇö Showing \u001b[0m\n \u001b[0m1\u001b[0m\n \u001b[0mΓÇô\u001b[0m\n \u001b[0m1\u001b[0m\n \u001b[0m of \u001b[0m\n \u001b[0m1\u001b[0m\n \u001b[0m results\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m1\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n\u001b[36m\u001b[39m\n at Proxy.waitForWrapper (C:\\Users\\HP\\Desktop\\YieldVault-RWA\\frontend\\node_modules\\@testing-library\\dom\\dist\\wait-for.js:163:27)\n at C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/pages/TransactionHistory.test.tsx:283:11\n at file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:1893:20"],"meta":{},"tags":[]}],"startTime":1774558025986,"endTime":1774558032085.3718,"status":"failed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/pages/TransactionHistory.test.tsx"},{"assertionResults":[{"ancestorTitles":["FormField"],"fullName":"FormField renders label and input","status":"passed","title":"renders label and input","duration":709.0949,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FormField"],"fullName":"FormField shows error and aria-invalid when error is provided","status":"passed","title":"shows error and aria-invalid when error is provided","duration":30.851399999999558,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FormField"],"fullName":"FormField does not render an error element when there is no error","status":"passed","title":"does not render an error element when there is no error","duration":16.754399999999805,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558024305,"endTime":1774558025061.7544,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/forms/components/FormField.test.tsx"},{"assertionResults":[{"ancestorTitles":["SubmitButton"],"fullName":"SubmitButton shows loading label when loading","status":"passed","title":"shows loading label when loading","duration":769.7527000000009,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SubmitButton"],"fullName":"SubmitButton is disabled while loading","status":"passed","title":"is disabled while loading","duration":79.72260000000097,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SubmitButton"],"fullName":"SubmitButton is disabled when disabled prop is true","status":"passed","title":"is disabled when disabled prop is true","duration":84.5065999999988,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558024304,"endTime":1774558025238.5066,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/forms/components/SubmitButton.test.tsx"},{"assertionResults":[{"ancestorTitles":["ApiClient"],"fullName":"ApiClient retries transient GET failures with backoff and emits telemetry","status":"passed","title":"retries transient GET failures with backoff and emits telemetry","duration":26.477100000000064,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ApiClient"],"fullName":"ApiClient does not retry non-idempotent requests","status":"passed","title":"does not retry non-idempotent requests","duration":4.21599999999944,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ApiClient"],"fullName":"ApiClient normalizes terminal HTTP errors","status":"passed","title":"normalizes terminal HTTP errors","duration":3.8078000000004977,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ApiClient"],"fullName":"ApiClient sets X-Correlation-ID header on outgoing requests via the request interceptor","status":"passed","title":"sets X-Correlation-ID header on outgoing requests via the request interceptor","duration":2.127900000000409,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ApiClient"],"fullName":"ApiClient attaches correlationId to ApiError on HTTP failure","status":"passed","title":"attaches correlationId to ApiError on HTTP failure","duration":2.2916000000004715,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558036005,"endTime":1774558036044.2915,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/lib/api/client.test.ts"},{"assertionResults":[{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema accepts a valid G... address (56 chars)","status":"passed","title":"accepts a valid G... address (56 chars)","duration":22.479600000000573,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema rejects an empty string","status":"passed","title":"rejects an empty string","duration":3.145999999999731,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema rejects an address that starts with a different letter","status":"passed","title":"rejects an address that starts with a different letter","duration":1.043999999999869,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema rejects an address that is too short","status":"passed","title":"rejects an address that is too short","duration":1.2951999999995678,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema rejects a non-string input","status":"passed","title":"rejects a non-string input","duration":2.988299999999981,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema trims surrounding whitespace and still validates a correct address","status":"passed","title":"trims surrounding whitespace and still validates a correct address","duration":0.6626999999998588,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema accepts a whole number string","status":"passed","title":"accepts a whole number string","duration":1.2229999999999563,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema accepts a decimal string with up to 7 decimal places","status":"passed","title":"accepts a decimal string with up to 7 decimal places","duration":0.8841000000002168,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema rejects zero","status":"passed","title":"rejects zero","duration":4.054900000000089,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema rejects a negative amount","status":"passed","title":"rejects a negative amount","duration":1.5424000000002707,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema rejects more than 7 decimal places","status":"passed","title":"rejects more than 7 decimal places","duration":1.0471999999999753,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema rejects a non-numeric string","status":"passed","title":"rejects a non-numeric string","duration":0.9118000000007669,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema accepts a valid deposit payload","status":"passed","title":"accepts a valid deposit payload","duration":5.945300000000316,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema accepts optional slippageBps within range","status":"passed","title":"accepts optional slippageBps within range","duration":1.3613000000004831,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects a missing walletAddress","status":"passed","title":"rejects a missing walletAddress","duration":7.312200000000303,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects a missing amount","status":"passed","title":"rejects a missing amount","duration":0.9618000000000393,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects zero amount","status":"passed","title":"rejects zero amount","duration":0.7761000000000422,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects an unsupported asset","status":"passed","title":"rejects an unsupported asset","duration":1.047099999999773,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects slippageBps above 500","status":"passed","title":"rejects slippageBps above 500","duration":1.647399999999834,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects negative slippageBps","status":"passed","title":"rejects negative slippageBps","duration":0.9997000000003027,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema includes field-level detail for the offending field","status":"passed","title":"includes field-level detail for the offending field","duration":1.094000000000051,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema accepts a valid withdrawal payload","status":"passed","title":"accepts a valid withdrawal payload","duration":1.643500000000131,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema accepts an optional destination address","status":"passed","title":"accepts an optional destination address","duration":9.012299999999414,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema rejects zero shares","status":"passed","title":"rejects zero shares","duration":0.8176999999996042,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema rejects fractional shares","status":"passed","title":"rejects fractional shares","duration":0.7831999999998516,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema rejects an invalid destination address","status":"passed","title":"rejects an invalid destination address","duration":0.8258000000005268,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema rejects a missing walletAddress","status":"passed","title":"rejects a missing walletAddress","duration":1.0281999999997424,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema accepts two valid dates where from <= to","status":"passed","title":"accepts two valid dates where from <= to","duration":2.061800000000403,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema accepts an empty object (all fields are optional)","status":"passed","title":"accepts an empty object (all fields are optional)","duration":0.6378000000004249,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema rejects from > to with a 'from' field-level detail","status":"passed","title":"rejects from > to with a 'from' field-level detail","duration":1.3512999999993554,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema rejects a malformed date string","status":"passed","title":"rejects a malformed date string","duration":0.9475999999995111,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema rejects limit > 365","status":"passed","title":"rejects limit > 365","duration":1.0468000000000757,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["PortfolioQuerySchema"],"fullName":"PortfolioQuerySchema accepts a valid address and defaults status to 'all'","status":"passed","title":"accepts a valid address and defaults status to 'all'","duration":2.2115999999996347,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["PortfolioQuerySchema"],"fullName":"PortfolioQuerySchema accepts an explicit status filter","status":"passed","title":"accepts an explicit status filter","duration":1.168100000000777,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["PortfolioQuerySchema"],"fullName":"PortfolioQuerySchema rejects an invalid status value","status":"passed","title":"rejects an invalid status value","duration":0.856300000000374,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["PortfolioQuerySchema"],"fullName":"PortfolioQuerySchema rejects a missing walletAddress","status":"passed","title":"rejects a missing walletAddress","duration":0.7511999999996988,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema applies default limit=50, order=desc, type=all","status":"passed","title":"applies default limit=50, order=desc, type=all","duration":1.3775000000005093,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema accepts a custom limit within range","status":"passed","title":"accepts a custom limit within range","duration":0.4686999999994441,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema rejects limit above 200","status":"passed","title":"rejects limit above 200","duration":0.9025000000001455,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema rejects an invalid order value","status":"passed","title":"rejects an invalid order value","duration":0.7731999999996333,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema rejects a missing walletAddress","status":"passed","title":"rejects a missing walletAddress","duration":0.7451000000000931,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate()"],"fullName":"validate() returns the parsed value on success","status":"passed","title":"returns the parsed value on success","duration":0.40310000000044965,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate()"],"fullName":"validate() throws ValidationError (not a generic Error) on failure","status":"passed","title":"throws ValidationError (not a generic Error) on failure","duration":1.0083000000004176,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate()"],"fullName":"validate() includes the context label in the error message when provided","status":"passed","title":"includes the context label in the error message when provided","duration":0.7548999999999069,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate()"],"fullName":"validate() error.toJSON() returns the canonical shape","status":"passed","title":"error.toJSON() returns the canonical shape","duration":5.640500000000429,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validateAsync()"],"fullName":"validateAsync() resolves with the parsed value on success","status":"passed","title":"resolves with the parsed value on success","duration":2.344100000000253,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validateAsync()"],"fullName":"validateAsync() rejects with ValidationError on failure","status":"passed","title":"rejects with ValidationError on failure","duration":2.032000000000153,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558033350,"endTime":1774558033460.032,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/lib/api/validation.test.ts"}]} diff --git a/frontend/vitest-results2.json b/frontend/vitest-results2.json deleted file mode 100644 index f87257b44..000000000 --- a/frontend/vitest-results2.json +++ /dev/null @@ -1,2 +0,0 @@ -[sentry-vite-plugin] Info: Sending telemetry data on issues and performance to Sentry. To disable telemetry, set `options.telemetry` to `false`. -{"numTotalTestSuites":36,"numPassedTestSuites":32,"numFailedTestSuites":4,"numPendingTestSuites":0,"numTotalTests":106,"numPassedTests":102,"numFailedTests":4,"numPendingTests":0,"numTodoTests":0,"snapshot":{"added":0,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0,"didUpdate":false},"startTime":1774558233227,"success":false,"testResults":[{"assertionResults":[{"ancestorTitles":["Navbar"],"fullName":"Navbar renders the navbar with navigation links","status":"passed","title":"renders the navbar with navigation links","duration":530.8791999999994,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Navbar"],"fullName":"Navbar renders the wallet connect button","status":"passed","title":"renders the wallet connect button","duration":74.41929999999957,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Navbar"],"fullName":"Navbar shows the truncated wallet address when connected","status":"passed","title":"shows the truncated wallet address when connected","duration":81.50749999999971,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558257280,"endTime":1774558257966.5076,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/components/Navbar.test.tsx"},{"assertionResults":[{"ancestorTitles":["ThemeToggle"],"fullName":"ThemeToggle renders the theme toggle button","status":"passed","title":"renders the theme toggle button","duration":761.6308000000008,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ThemeToggle"],"fullName":"ThemeToggle changes the theme when clicked","status":"passed","title":"changes the theme when clicked","duration":115.44390000000021,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558244375,"endTime":1774558245252.4438,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/components/ThemeToggle.test.tsx"},{"assertionResults":[{"ancestorTitles":["VaultDashboard"],"fullName":"VaultDashboard renders the connect overlay when wallet is not connected","status":"passed","title":"renders the connect overlay when wallet is not connected","duration":665.4140999999981,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultDashboard"],"fullName":"VaultDashboard renders the dashboard when wallet is connected","status":"passed","title":"renders the dashboard when wallet is connected","duration":182.77189999999973,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultDashboard"],"fullName":"VaultDashboard allows switching between deposit and withdraw tabs","status":"passed","title":"allows switching between deposit and withdraw tabs","duration":265.634399999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultDashboard"],"fullName":"VaultDashboard updates the amount input and processes a deposit","status":"failed","title":"updates the amount input and processes a deposit","duration":5028.278999999999,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:1774:27)\n at Object. (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:1807:16)\n at Object. (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:1553:28)\n at chain (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:589:14)\n at C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/components/VaultDashboard.test.tsx:105:3\n at file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:1879:40\n at runWithSuite (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:2248:8)\n at Object.collect (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:1879:10)\n at Object.collect (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:1883:54)\n at processTicksAndRejections (node:internal/process/task_queues:105:5)"],"meta":{},"tags":[]},{"ancestorTitles":["VaultDashboard"],"fullName":"VaultDashboard shows a normalized API error message when data loading fails","status":"passed","title":"shows a normalized API error message when data loading fails","duration":1269.7649999999994,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558251248,"endTime":1774558258661.765,"status":"failed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/components/VaultDashboard.test.tsx"},{"assertionResults":[{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect renders the connect button when no wallet is connected","status":"passed","title":"renders the connect button when no wallet is connected","duration":226.60679999999957,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect calls onConnect when manually connected via button","status":"passed","title":"calls onConnect when manually connected via button","duration":82.07099999999991,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect shows the formatted address when connected","status":"passed","title":"shows the formatted address when connected","duration":75.0030999999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect calls onDisconnect when the disconnect button is clicked","status":"passed","title":"calls onDisconnect when the disconnect button is clicked","duration":47.60159999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect handles wallet disconnects gracefully during polling","status":"passed","title":"handles wallet disconnects gracefully during polling","duration":33.1786000000011,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558257281,"endTime":1774558257746.1787,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/components/WalletConnect.test.tsx"},{"assertionResults":[{"ancestorTitles":["ToastProvider"],"fullName":"ToastProvider shows and auto-dismisses toasts","status":"passed","title":"shows and auto-dismisses toasts","duration":870.4106000000011,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ToastProvider"],"fullName":"ToastProvider supports manual dismissal","status":"passed","title":"supports manual dismissal","duration":237.5660000000007,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558243293,"endTime":1774558244400.566,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/context/ToastContext.test.tsx"},{"assertionResults":[{"ancestorTitles":["useForm"],"fullName":"useForm returns initial state","status":"passed","title":"returns initial state","duration":62.9393,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm handleChange updates values","status":"passed","title":"handleChange updates values","duration":11.664999999999054,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm handleBlur marks touched and validates field","status":"passed","title":"handleBlur marks touched and validates field","duration":16.35820000000058,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm handleSubmit validates all fields and blocks submit when invalid","status":"passed","title":"handleSubmit validates all fields and blocks submit when invalid","duration":14.084100000000035,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm tracks isSubmitting lifecycle for valid submit","status":"passed","title":"tracks isSubmitting lifecycle for valid submit","duration":33.50980000000163,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm setFieldError stores server-side error","status":"passed","title":"setFieldError stores server-side error","duration":4.847400000000562,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558254886,"endTime":1774558255029.8474,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/forms/useForm.test.tsx"},{"assertionResults":[{"ancestorTitles":["validate"],"fullName":"validate fails when required field is missing","status":"passed","title":"fails when required field is missing","duration":4.731899999999769,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate"],"fullName":"validate fails min length and passes when min is met","status":"passed","title":"fails min length and passes when min is met","duration":1.2265999999999622,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate"],"fullName":"validate fails max length and passes when within max","status":"passed","title":"fails max length and passes when within max","duration":0.8139000000001033,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate"],"fullName":"validate fails pattern and passes matching pattern","status":"passed","title":"fails pattern and passes matching pattern","duration":0.7382999999999811,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate"],"fullName":"validate runs custom validator","status":"passed","title":"runs custom validator","duration":0.7346000000006825,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558262239,"endTime":1774558262248.7346,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/forms/validate.test.ts"},{"assertionResults":[{"ancestorTitles":["stellarAccount"],"fullName":"stellarAccount returns null when Freighter is not allowed","status":"passed","title":"returns null when Freighter is not allowed","duration":10.690499999999702,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stellarAccount"],"fullName":"stellarAccount returns discovered address when Freighter is allowed","status":"passed","title":"returns discovered address when Freighter is allowed","duration":2.222099999999955,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stellarAccount"],"fullName":"stellarAccount extracts USDC balance from account balances","status":"passed","title":"extracts USDC balance from account balances","duration":3.4658999999992375,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558259507,"endTime":1774558259523.4658,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/lib/stellarAccount.test.ts"},{"assertionResults":[{"ancestorTitles":["Portfolio"],"fullName":"Portfolio shows the wallet prompt when disconnected","status":"passed","title":"shows the wallet prompt when disconnected","duration":323.08020000000033,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Portfolio"],"fullName":"Portfolio renders holdings in the reusable table","status":"failed","title":"renders holdings in the reusable table","duration":1422.4457000000002,"failureMessages":["Error: Unable to find an element with the text: /Tokenized T-Bills/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mYour \u001b[0m\n \u001b[36m\u001b[39m\n \u001b[0mPortfolio\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mOverview of your deposited real-world assets.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mData unavailable\u001b[0m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mInvalid value for \"walletAddress\": Must be a valid Stellar public key (starts with G, 56 chars).\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mTotal Assets\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m$0.00\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mUnrealized Gain\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m+\u001b[0m\n \u001b[0m$0.00\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mHoldings\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mSort, search, and page through all current vault positions.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mStatus Filter\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mAll Statuses\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mActive\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mInactive\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mSearch holdings\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m0 holdings found\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m)\n at runWithCancel (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:2313:10)\n at file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:2295:20\n at new Promise ()"],"meta":{},"tags":[]},{"ancestorTitles":["Portfolio"],"fullName":"Portfolio persists filter state in the URL","status":"failed","title":"persists filter state in the URL","duration":1261.7958999999992,"failureMessages":["Error: Unable to find an element with the text: /Tokenized T-Bills/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mYour \u001b[0m\n \u001b[36m\u001b[39m\n \u001b[0mPortfolio\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mOverview of your deposited real-world assets.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mData unavailable\u001b[0m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mInvalid value for \"walletAddress\": Must be a valid Stellar public key (starts with G, 56 chars).\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mTotal Assets\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m$0.00\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mUnrealized Gain\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m+\u001b[0m\n \u001b[0m$0.00\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mHoldings\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mSort, search, and page through all current vault positions.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mStatus Filter\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mAll Statuses\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mActive\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mInactive\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mSearch holdings\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mReset Filters\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m0 holdings found\u001b[...\n at waitForWrapper (C:\\Users\\HP\\Desktop\\YieldVault-RWA\\frontend\\node_modules\\@testing-library\\dom\\dist\\wait-for.js:163:27)\n at C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/pages/Portfolio.test.tsx:151:11\n at file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:1893:20"],"meta":{},"tags":[]},{"ancestorTitles":["Portfolio"],"fullName":"Portfolio supports keyboard sorting and pagination state from the URL","status":"failed","title":"supports keyboard sorting and pagination state from the URL","duration":1215.0582000000013,"failureMessages":["Error: Unable to find an element with the text: /Yield Bearing Cash/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mYour \u001b[0m\n \u001b[36m\u001b[39m\n \u001b[0mPortfolio\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mOverview of your deposited real-world assets.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mData unavailable\u001b[0m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mInvalid value for \"walletAddress\": Must be a valid Stellar public key (starts with G, 56 chars).\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mTotal Assets\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m$0.00\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mUnrealized Gain\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m+\u001b[0m\n \u001b[0m$0.00\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mHoldings\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mSort, search, and page through all current vault positions.\u001b[0m\n \u001b[36m

\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mStatus Filter\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mAll Statuses\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mActive\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mInactive\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0mSearch holdings\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[0m0 holdings found\u001b[0m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m\u001b[39m\n \u001b[36m)\n at runWithCancel (file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:2313:10)\n at file:///C:/Users/HP/Desktop/YieldVault-RWA/frontend/node_modules/@vitest/runner/dist/chunk-artifact.js:2295:20\n at new Promise ()"],"meta":{},"tags":[]}],"startTime":1774558245572,"endTime":1774558249794.058,"status":"failed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/pages/Portfolio.test.tsx"},{"assertionResults":[{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders connect-wallet prompt when walletAddress is null","status":"passed","title":"renders connect-wallet prompt when walletAddress is null","duration":210.30199999999968,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory shows loading indicator while fetch is pending","status":"passed","title":"shows loading indicator while fetch is pending","duration":186.41710000000057,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory calls getTransactions with the correct wallet address on mount","status":"passed","title":"calls getTransactions with the correct wallet address on mount","duration":120.14270000000033,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders the transaction table when wallet is connected and fetch succeeds","status":"passed","title":"renders the transaction table when wallet is connected and fetch succeeds","duration":895.4228000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory shows ApiStatusBanner on fetch failure","status":"passed","title":"shows ApiStatusBanner on fetch failure","duration":427.71639999999934,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders correct column headers: Type, Amount, Asset, Date, Transaction Hash","status":"passed","title":"renders correct column headers: Type, Amount, Asset, Date, Transaction Hash","duration":828.6372999999985,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory has sort buttons for Type, Amount, Date but not for Asset and Transaction Hash","status":"passed","title":"has sort buttons for Type, Amount, Date but not for Asset and Transaction Hash","duration":449.1260000000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory default page size select shows 10","status":"passed","title":"default page size select shows 10","duration":323.40560000000005,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders filter control with All, Deposit, and Withdrawal options","status":"passed","title":"renders filter control with All, Deposit, and Withdrawal options","duration":243.00400000000081,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory resets page to 1 when filter is applied","status":"passed","title":"resets page to 1 when filter is applied","duration":925.0737000000008,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders deposit badge with 'cyan' class and withdrawal badge with 'red' class","status":"passed","title":"renders deposit badge with 'cyan' class and withdrawal badge with 'red' class","duration":465.06540000000314,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory shows empty state message when wallet is connected but no transactions exist","status":"passed","title":"shows empty state message when wallet is connected but no transactions exist","duration":207.54369999999835,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory shows filtered empty state message when filter yields no results","status":"passed","title":"shows filtered empty state message when filter yields no results","duration":594.2430000000022,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558245266,"endTime":1774558251143.243,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/pages/TransactionHistory.test.tsx"},{"assertionResults":[{"ancestorTitles":["FormField"],"fullName":"FormField renders label and input","status":"passed","title":"renders label and input","duration":893.1222000000016,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FormField"],"fullName":"FormField shows error and aria-invalid when error is provided","status":"passed","title":"shows error and aria-invalid when error is provided","duration":59.21819999999934,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FormField"],"fullName":"FormField does not render an error element when there is no error","status":"passed","title":"does not render an error element when there is no error","duration":22.307200000001103,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558243651,"endTime":1774558244626.3071,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/forms/components/FormField.test.tsx"},{"assertionResults":[{"ancestorTitles":["SubmitButton"],"fullName":"SubmitButton shows loading label when loading","status":"passed","title":"shows loading label when loading","duration":812.3600000000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SubmitButton"],"fullName":"SubmitButton is disabled while loading","status":"passed","title":"is disabled while loading","duration":48.02190000000155,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SubmitButton"],"fullName":"SubmitButton is disabled when disabled prop is true","status":"passed","title":"is disabled when disabled prop is true","duration":75.21390000000065,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558243652,"endTime":1774558244588.2139,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/forms/components/SubmitButton.test.tsx"},{"assertionResults":[{"ancestorTitles":["ApiClient"],"fullName":"ApiClient retries transient GET failures with backoff and emits telemetry","status":"passed","title":"retries transient GET failures with backoff and emits telemetry","duration":51.91020000000026,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ApiClient"],"fullName":"ApiClient does not retry non-idempotent requests","status":"passed","title":"does not retry non-idempotent requests","duration":7.0341000000007625,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ApiClient"],"fullName":"ApiClient normalizes terminal HTTP errors","status":"passed","title":"normalizes terminal HTTP errors","duration":9.873700000000099,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ApiClient"],"fullName":"ApiClient sets X-Correlation-ID header on outgoing requests via the request interceptor","status":"passed","title":"sets X-Correlation-ID header on outgoing requests via the request interceptor","duration":3.7035999999989144,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ApiClient"],"fullName":"ApiClient attaches correlationId to ApiError on HTTP failure","status":"passed","title":"attaches correlationId to ApiError on HTTP failure","duration":4.145500000000538,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558259604,"endTime":1774558259682.1455,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/lib/api/client.test.ts"},{"assertionResults":[{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema accepts a valid G... address (56 chars)","status":"passed","title":"accepts a valid G... address (56 chars)","duration":8.347499999999854,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema rejects an empty string","status":"passed","title":"rejects an empty string","duration":4.1314000000002125,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema rejects an address that starts with a different letter","status":"passed","title":"rejects an address that starts with a different letter","duration":1.023300000000745,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema rejects an address that is too short","status":"passed","title":"rejects an address that is too short","duration":1.3470999999990454,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema rejects a non-string input","status":"passed","title":"rejects a non-string input","duration":3.3536999999996624,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema trims surrounding whitespace and still validates a correct address","status":"passed","title":"trims surrounding whitespace and still validates a correct address","duration":1.3683000000000902,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema accepts a whole number string","status":"passed","title":"accepts a whole number string","duration":3.042199999999866,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema accepts a decimal string with up to 7 decimal places","status":"passed","title":"accepts a decimal string with up to 7 decimal places","duration":2.7672999999995227,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema rejects zero","status":"passed","title":"rejects zero","duration":2.5247999999992317,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema rejects a negative amount","status":"passed","title":"rejects a negative amount","duration":1.3438000000005559,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema rejects more than 7 decimal places","status":"passed","title":"rejects more than 7 decimal places","duration":1.0992000000005646,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema rejects a non-numeric string","status":"passed","title":"rejects a non-numeric string","duration":0.9547000000002299,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema accepts a valid deposit payload","status":"passed","title":"accepts a valid deposit payload","duration":5.219699999999648,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema accepts optional slippageBps within range","status":"passed","title":"accepts optional slippageBps within range","duration":2.38379999999961,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects a missing walletAddress","status":"passed","title":"rejects a missing walletAddress","duration":1.8989000000001397,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects a missing amount","status":"passed","title":"rejects a missing amount","duration":6.348700000000463,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects zero amount","status":"passed","title":"rejects zero amount","duration":3.4570000000003347,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects an unsupported asset","status":"passed","title":"rejects an unsupported asset","duration":1.0830999999998312,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects slippageBps above 500","status":"passed","title":"rejects slippageBps above 500","duration":1.2101000000002387,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects negative slippageBps","status":"passed","title":"rejects negative slippageBps","duration":0.8960000000006403,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema includes field-level detail for the offending field","status":"passed","title":"includes field-level detail for the offending field","duration":2.1238000000012107,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema accepts a valid withdrawal payload","status":"passed","title":"accepts a valid withdrawal payload","duration":2.279000000000451,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema accepts an optional destination address","status":"passed","title":"accepts an optional destination address","duration":0.8493999999991502,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema rejects zero shares","status":"passed","title":"rejects zero shares","duration":0.8171000000002095,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema rejects fractional shares","status":"passed","title":"rejects fractional shares","duration":0.8546999999998661,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema rejects an invalid destination address","status":"passed","title":"rejects an invalid destination address","duration":1.100800000000163,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema rejects a missing walletAddress","status":"passed","title":"rejects a missing walletAddress","duration":1.1419000000005326,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema accepts two valid dates where from <= to","status":"passed","title":"accepts two valid dates where from <= to","duration":2.1005000000004657,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema accepts an empty object (all fields are optional)","status":"passed","title":"accepts an empty object (all fields are optional)","duration":0.6725999999998749,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema rejects from > to with a 'from' field-level detail","status":"passed","title":"rejects from > to with a 'from' field-level detail","duration":0.7616999999991094,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema rejects a malformed date string","status":"passed","title":"rejects a malformed date string","duration":0.8220000000001164,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema rejects limit > 365","status":"passed","title":"rejects limit > 365","duration":1.1183000000000902,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["PortfolioQuerySchema"],"fullName":"PortfolioQuerySchema accepts a valid address and defaults status to 'all'","status":"passed","title":"accepts a valid address and defaults status to 'all'","duration":2.9641000000010536,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["PortfolioQuerySchema"],"fullName":"PortfolioQuerySchema accepts an explicit status filter","status":"passed","title":"accepts an explicit status filter","duration":1.1793999999990774,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["PortfolioQuerySchema"],"fullName":"PortfolioQuerySchema rejects an invalid status value","status":"passed","title":"rejects an invalid status value","duration":1.0388999999995576,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["PortfolioQuerySchema"],"fullName":"PortfolioQuerySchema rejects a missing walletAddress","status":"passed","title":"rejects a missing walletAddress","duration":0.8744999999998981,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema applies default limit=50, order=desc, type=all","status":"passed","title":"applies default limit=50, order=desc, type=all","duration":1.499099999999089,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema accepts a custom limit within range","status":"passed","title":"accepts a custom limit within range","duration":0.5073999999985972,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema rejects limit above 200","status":"passed","title":"rejects limit above 200","duration":1.271999999999025,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema rejects an invalid order value","status":"passed","title":"rejects an invalid order value","duration":1.5300000000006548,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema rejects a missing walletAddress","status":"passed","title":"rejects a missing walletAddress","duration":1.4095000000015716,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate()"],"fullName":"validate() returns the parsed value on success","status":"passed","title":"returns the parsed value on success","duration":0.42189999999936845,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate()"],"fullName":"validate() throws ValidationError (not a generic Error) on failure","status":"passed","title":"throws ValidationError (not a generic Error) on failure","duration":1.2181000000000495,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate()"],"fullName":"validate() includes the context label in the error message when provided","status":"passed","title":"includes the context label in the error message when provided","duration":0.9503999999997177,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate()"],"fullName":"validate() error.toJSON() returns the canonical shape","status":"passed","title":"error.toJSON() returns the canonical shape","duration":7.324800000000323,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validateAsync()"],"fullName":"validateAsync() resolves with the parsed value on success","status":"passed","title":"resolves with the parsed value on success","duration":2.910700000000361,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validateAsync()"],"fullName":"validateAsync() rejects with ValidationError on failure","status":"passed","title":"rejects with ValidationError on failure","duration":2.4068999999999505,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774558256081,"endTime":1774558256182.407,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/lib/api/validation.test.ts"}]} diff --git a/frontend/vitest-results3.json b/frontend/vitest-results3.json deleted file mode 100644 index f6502870d..000000000 --- a/frontend/vitest-results3.json +++ /dev/null @@ -1,2 +0,0 @@ -[sentry-vite-plugin] Info: Sending telemetry data on issues and performance to Sentry. To disable telemetry, set `options.telemetry` to `false`. -{"numTotalTestSuites":36,"numPassedTestSuites":36,"numFailedTestSuites":0,"numPendingTestSuites":0,"numTotalTests":106,"numPassedTests":106,"numFailedTests":0,"numPendingTests":0,"numTodoTests":0,"snapshot":{"added":0,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0,"didUpdate":false},"startTime":1774560169295,"success":true,"testResults":[{"assertionResults":[{"ancestorTitles":["Navbar"],"fullName":"Navbar renders the navbar with navigation links","status":"passed","title":"renders the navbar with navigation links","duration":416.7017999999989,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Navbar"],"fullName":"Navbar renders the wallet connect button","status":"passed","title":"renders the wallet connect button","duration":134.65250000000015,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Navbar"],"fullName":"Navbar shows the truncated wallet address when connected","status":"passed","title":"shows the truncated wallet address when connected","duration":123.09419999999955,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774560195490,"endTime":1774560196165.0942,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/components/Navbar.test.tsx"},{"assertionResults":[{"ancestorTitles":["ThemeToggle"],"fullName":"ThemeToggle renders the theme toggle button","status":"passed","title":"renders the theme toggle button","duration":703.1424000000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ThemeToggle"],"fullName":"ThemeToggle changes the theme when clicked","status":"passed","title":"changes the theme when clicked","duration":97.84799999999996,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774560183475,"endTime":1774560184276.848,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/components/ThemeToggle.test.tsx"},{"assertionResults":[{"ancestorTitles":["VaultDashboard"],"fullName":"VaultDashboard renders the connect overlay when wallet is not connected","status":"passed","title":"renders the connect overlay when wallet is not connected","duration":734.5998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultDashboard"],"fullName":"VaultDashboard renders the dashboard when wallet is connected","status":"passed","title":"renders the dashboard when wallet is connected","duration":453.7635999999984,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultDashboard"],"fullName":"VaultDashboard allows switching between deposit and withdraw tabs","status":"passed","title":"allows switching between deposit and withdraw tabs","duration":470.8028999999988,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultDashboard"],"fullName":"VaultDashboard updates the amount input and processes a deposit","status":"passed","title":"updates the amount input and processes a deposit","duration":520.9108000000015,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultDashboard"],"fullName":"VaultDashboard shows a normalized API error message when data loading fails","status":"passed","title":"shows a normalized API error message when data loading fails","duration":1847.174500000001,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774560187964,"endTime":1774560191992.1746,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/components/VaultDashboard.test.tsx"},{"assertionResults":[{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect renders the connect button when no wallet is connected","status":"passed","title":"renders the connect button when no wallet is connected","duration":237.03759999999966,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect calls onConnect when manually connected via button","status":"passed","title":"calls onConnect when manually connected via button","duration":104.52359999999862,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect shows the formatted address when connected","status":"passed","title":"shows the formatted address when connected","duration":92.3044000000009,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect calls onDisconnect when the disconnect button is clicked","status":"passed","title":"calls onDisconnect when the disconnect button is clicked","duration":60.349199999998746,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WalletConnect"],"fullName":"WalletConnect handles wallet disconnects gracefully during polling","status":"passed","title":"handles wallet disconnects gracefully during polling","duration":32.34449999999924,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774560195492,"endTime":1774560196019.3445,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/components/WalletConnect.test.tsx"},{"assertionResults":[{"ancestorTitles":["ToastProvider"],"fullName":"ToastProvider shows and auto-dismisses toasts","status":"passed","title":"shows and auto-dismisses toasts","duration":709.0792000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ToastProvider"],"fullName":"ToastProvider supports manual dismissal","status":"passed","title":"supports manual dismissal","duration":147.77070000000094,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774560182768,"endTime":1774560183625.7708,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/context/ToastContext.test.tsx"},{"assertionResults":[{"ancestorTitles":["useForm"],"fullName":"useForm returns initial state","status":"passed","title":"returns initial state","duration":78.48390000000109,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm handleChange updates values","status":"passed","title":"handleChange updates values","duration":11.630800000000818,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm handleBlur marks touched and validates field","status":"passed","title":"handleBlur marks touched and validates field","duration":14.70509999999922,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm handleSubmit validates all fields and blocks submit when invalid","status":"passed","title":"handleSubmit validates all fields and blocks submit when invalid","duration":12.184199999999691,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm tracks isSubmitting lifecycle for valid submit","status":"passed","title":"tracks isSubmitting lifecycle for valid submit","duration":16.47910000000047,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["useForm"],"fullName":"useForm setFieldError stores server-side error","status":"passed","title":"setFieldError stores server-side error","duration":6.287900000001173,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774560193089,"endTime":1774560193230.2878,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/forms/useForm.test.tsx"},{"assertionResults":[{"ancestorTitles":["validate"],"fullName":"validate fails when required field is missing","status":"passed","title":"fails when required field is missing","duration":5.153500000000349,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate"],"fullName":"validate fails min length and passes when min is met","status":"passed","title":"fails min length and passes when min is met","duration":1.098800000000665,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate"],"fullName":"validate fails max length and passes when within max","status":"passed","title":"fails max length and passes when within max","duration":0.7452000000002954,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate"],"fullName":"validate fails pattern and passes matching pattern","status":"passed","title":"fails pattern and passes matching pattern","duration":0.5659999999998035,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate"],"fullName":"validate runs custom validator","status":"passed","title":"runs custom validator","duration":0.5159999999996217,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774560197865,"endTime":1774560197873.516,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/forms/validate.test.ts"},{"assertionResults":[{"ancestorTitles":["Portfolio"],"fullName":"Portfolio shows the wallet prompt when disconnected","status":"passed","title":"shows the wallet prompt when disconnected","duration":231.69859999999971,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Portfolio"],"fullName":"Portfolio renders holdings in the reusable table","status":"passed","title":"renders holdings in the reusable table","duration":1311.7322999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Portfolio"],"fullName":"Portfolio persists filter state in the URL","status":"passed","title":"persists filter state in the URL","duration":275.2228000000032,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Portfolio"],"fullName":"Portfolio supports keyboard sorting and pagination state from the URL","status":"passed","title":"supports keyboard sorting and pagination state from the URL","duration":479.47739999999976,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774560184999,"endTime":1774560187310.4773,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/pages/Portfolio.test.tsx"},{"assertionResults":[{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders connect-wallet prompt when walletAddress is null","status":"passed","title":"renders connect-wallet prompt when walletAddress is null","duration":299.3245000000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory shows loading indicator while fetch is pending","status":"passed","title":"shows loading indicator while fetch is pending","duration":203.8119000000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory calls getTransactions with the correct wallet address on mount","status":"passed","title":"calls getTransactions with the correct wallet address on mount","duration":89.91960000000108,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders the transaction table when wallet is connected and fetch succeeds","status":"passed","title":"renders the transaction table when wallet is connected and fetch succeeds","duration":775.1518000000015,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory shows ApiStatusBanner on fetch failure","status":"passed","title":"shows ApiStatusBanner on fetch failure","duration":272.08910000000105,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders correct column headers: Type, Amount, Asset, Date, Transaction Hash","status":"passed","title":"renders correct column headers: Type, Amount, Asset, Date, Transaction Hash","duration":530.4857999999986,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory has sort buttons for Type, Amount, Date but not for Asset and Transaction Hash","status":"passed","title":"has sort buttons for Type, Amount, Date but not for Asset and Transaction Hash","duration":386.3182000000015,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory default page size select shows 10","status":"passed","title":"default page size select shows 10","duration":183.38530000000173,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders filter control with All, Deposit, and Withdrawal options","status":"passed","title":"renders filter control with All, Deposit, and Withdrawal options","duration":166.3578000000016,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory resets page to 1 when filter is applied","status":"passed","title":"resets page to 1 when filter is applied","duration":691.2407999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory renders deposit badge with 'cyan' class and withdrawal badge with 'red' class","status":"passed","title":"renders deposit badge with 'cyan' class and withdrawal badge with 'red' class","duration":314.5601999999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory shows empty state message when wallet is connected but no transactions exist","status":"passed","title":"shows empty state message when wallet is connected but no transactions exist","duration":103.36520000000019,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionHistory"],"fullName":"TransactionHistory shows filtered empty state message when filter yields no results","status":"passed","title":"shows filtered empty state message when filter yields no results","duration":609.067500000001,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774560184670,"endTime":1774560189296.0674,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/pages/TransactionHistory.test.tsx"},{"assertionResults":[{"ancestorTitles":["stellarAccount"],"fullName":"stellarAccount returns null when Freighter is not allowed","status":"passed","title":"returns null when Freighter is not allowed","duration":7.574800000000323,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stellarAccount"],"fullName":"stellarAccount returns discovered address when Freighter is allowed","status":"passed","title":"returns discovered address when Freighter is allowed","duration":1.1943999999994048,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stellarAccount"],"fullName":"stellarAccount extracts USDC balance from account balances","status":"passed","title":"extracts USDC balance from account balances","duration":2.8676000000004933,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774560199782,"endTime":1774560199793.8677,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/lib/stellarAccount.test.ts"},{"assertionResults":[{"ancestorTitles":["FormField"],"fullName":"FormField renders label and input","status":"passed","title":"renders label and input","duration":751.6338000000014,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FormField"],"fullName":"FormField shows error and aria-invalid when error is provided","status":"passed","title":"shows error and aria-invalid when error is provided","duration":38.95440000000053,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FormField"],"fullName":"FormField does not render an error element when there is no error","status":"passed","title":"does not render an error element when there is no error","duration":11.274600000000646,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774560182857,"endTime":1774560183659.2747,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/forms/components/FormField.test.tsx"},{"assertionResults":[{"ancestorTitles":["SubmitButton"],"fullName":"SubmitButton shows loading label when loading","status":"passed","title":"shows loading label when loading","duration":558.775599999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SubmitButton"],"fullName":"SubmitButton is disabled while loading","status":"passed","title":"is disabled while loading","duration":56.511499999998705,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SubmitButton"],"fullName":"SubmitButton is disabled when disabled prop is true","status":"passed","title":"is disabled when disabled prop is true","duration":55.920099999999366,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774560182740,"endTime":1774560183410.9202,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/forms/components/SubmitButton.test.tsx"},{"assertionResults":[{"ancestorTitles":["ApiClient"],"fullName":"ApiClient retries transient GET failures with backoff and emits telemetry","status":"passed","title":"retries transient GET failures with backoff and emits telemetry","duration":43.126899999999296,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ApiClient"],"fullName":"ApiClient does not retry non-idempotent requests","status":"passed","title":"does not retry non-idempotent requests","duration":6.873099999998885,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ApiClient"],"fullName":"ApiClient normalizes terminal HTTP errors","status":"passed","title":"normalizes terminal HTTP errors","duration":6.994899999999689,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ApiClient"],"fullName":"ApiClient sets X-Correlation-ID header on outgoing requests via the request interceptor","status":"passed","title":"sets X-Correlation-ID header on outgoing requests via the request interceptor","duration":2.737300000000687,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ApiClient"],"fullName":"ApiClient attaches correlationId to ApiError on HTTP failure","status":"passed","title":"attaches correlationId to ApiError on HTTP failure","duration":2.561999999999898,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774560197076,"endTime":1774560197138.562,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/lib/api/client.test.ts"},{"assertionResults":[{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema accepts a valid G... address (56 chars)","status":"passed","title":"accepts a valid G... address (56 chars)","duration":10.230999999999767,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema rejects an empty string","status":"passed","title":"rejects an empty string","duration":3.1103000000002794,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema rejects an address that starts with a different letter","status":"passed","title":"rejects an address that starts with a different letter","duration":1.08009999999922,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema rejects an address that is too short","status":"passed","title":"rejects an address that is too short","duration":1.5487999999986641,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema rejects a non-string input","status":"passed","title":"rejects a non-string input","duration":1.7580999999991036,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["StellarAddressSchema"],"fullName":"StellarAddressSchema trims surrounding whitespace and still validates a correct address","status":"passed","title":"trims surrounding whitespace and still validates a correct address","duration":0.9565000000002328,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema accepts a whole number string","status":"passed","title":"accepts a whole number string","duration":1.6815999999998894,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema accepts a decimal string with up to 7 decimal places","status":"passed","title":"accepts a decimal string with up to 7 decimal places","duration":1.5480999999999767,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema rejects zero","status":"passed","title":"rejects zero","duration":3.3407999999999447,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema rejects a negative amount","status":"passed","title":"rejects a negative amount","duration":1.5767999999989115,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema rejects more than 7 decimal places","status":"passed","title":"rejects more than 7 decimal places","duration":1.43789999999899,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AmountSchema"],"fullName":"AmountSchema rejects a non-numeric string","status":"passed","title":"rejects a non-numeric string","duration":1.156699999999546,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema accepts a valid deposit payload","status":"passed","title":"accepts a valid deposit payload","duration":6.332000000000335,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema accepts optional slippageBps within range","status":"passed","title":"accepts optional slippageBps within range","duration":2.3999000000003434,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects a missing walletAddress","status":"passed","title":"rejects a missing walletAddress","duration":1.67909999999938,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects a missing amount","status":"passed","title":"rejects a missing amount","duration":1.0658999999996013,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects zero amount","status":"passed","title":"rejects zero amount","duration":1.1547999999984313,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects an unsupported asset","status":"passed","title":"rejects an unsupported asset","duration":1.345500000001266,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects slippageBps above 500","status":"passed","title":"rejects slippageBps above 500","duration":1.5191000000013446,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema rejects negative slippageBps","status":"passed","title":"rejects negative slippageBps","duration":1.020200000000841,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DepositRequestSchema"],"fullName":"DepositRequestSchema includes field-level detail for the offending field","status":"passed","title":"includes field-level detail for the offending field","duration":1.3634999999994761,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema accepts a valid withdrawal payload","status":"passed","title":"accepts a valid withdrawal payload","duration":2.0733000000000175,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema accepts an optional destination address","status":"passed","title":"accepts an optional destination address","duration":0.9553999999989173,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema rejects zero shares","status":"passed","title":"rejects zero shares","duration":1.2782999999999447,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema rejects fractional shares","status":"passed","title":"rejects fractional shares","duration":1.5445999999992637,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema rejects an invalid destination address","status":"passed","title":"rejects an invalid destination address","duration":1.5267000000003463,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["WithdrawalRequestSchema"],"fullName":"WithdrawalRequestSchema rejects a missing walletAddress","status":"passed","title":"rejects a missing walletAddress","duration":1.383399999998801,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema accepts two valid dates where from <= to","status":"passed","title":"accepts two valid dates where from <= to","duration":2.134299999999712,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema accepts an empty object (all fields are optional)","status":"passed","title":"accepts an empty object (all fields are optional)","duration":0.7554999999993015,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema rejects from > to with a 'from' field-level detail","status":"passed","title":"rejects from > to with a 'from' field-level detail","duration":0.966099999999642,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema rejects a malformed date string","status":"passed","title":"rejects a malformed date string","duration":0.9799000000002707,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["VaultHistoryQuerySchema"],"fullName":"VaultHistoryQuerySchema rejects limit > 365","status":"passed","title":"rejects limit > 365","duration":1.0925000000006548,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["PortfolioQuerySchema"],"fullName":"PortfolioQuerySchema accepts a valid address and defaults status to 'all'","status":"passed","title":"accepts a valid address and defaults status to 'all'","duration":1.9169000000001688,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["PortfolioQuerySchema"],"fullName":"PortfolioQuerySchema accepts an explicit status filter","status":"passed","title":"accepts an explicit status filter","duration":0.7645999999986088,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["PortfolioQuerySchema"],"fullName":"PortfolioQuerySchema rejects an invalid status value","status":"passed","title":"rejects an invalid status value","duration":0.8930999999993219,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["PortfolioQuerySchema"],"fullName":"PortfolioQuerySchema rejects a missing walletAddress","status":"passed","title":"rejects a missing walletAddress","duration":0.7651999999998225,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema applies default limit=50, order=desc, type=all","status":"passed","title":"applies default limit=50, order=desc, type=all","duration":1.8410000000003492,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema accepts a custom limit within range","status":"passed","title":"accepts a custom limit within range","duration":1.0049999999991996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema rejects limit above 200","status":"passed","title":"rejects limit above 200","duration":1.2714999999989232,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema rejects an invalid order value","status":"passed","title":"rejects an invalid order value","duration":0.9574000000011438,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TransactionQuerySchema"],"fullName":"TransactionQuerySchema rejects a missing walletAddress","status":"passed","title":"rejects a missing walletAddress","duration":0.9200999999993655,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate()"],"fullName":"validate() returns the parsed value on success","status":"passed","title":"returns the parsed value on success","duration":0.41159999999945285,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate()"],"fullName":"validate() throws ValidationError (not a generic Error) on failure","status":"passed","title":"throws ValidationError (not a generic Error) on failure","duration":0.9951999999993859,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate()"],"fullName":"validate() includes the context label in the error message when provided","status":"passed","title":"includes the context label in the error message when provided","duration":0.7281000000002678,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validate()"],"fullName":"validate() error.toJSON() returns the canonical shape","status":"passed","title":"error.toJSON() returns the canonical shape","duration":5.863900000000285,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validateAsync()"],"fullName":"validateAsync() resolves with the parsed value on success","status":"passed","title":"resolves with the parsed value on success","duration":2.7247999999999593,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["validateAsync()"],"fullName":"validateAsync() rejects with ValidationError on failure","status":"passed","title":"rejects with ValidationError on failure","duration":2.9945999999999913,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1774560193789,"endTime":1774560193879.9946,"status":"passed","message":"","name":"C:/Users/HP/Desktop/YieldVault-RWA/frontend/src/lib/api/validation.test.ts"}]} diff --git a/package.json b/package.json index 7f9c206b5..3816e8b19 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "scripts": { "check:pnpm-version": "node scripts/check-pnpm-version.js", "prepare": "husky", - "test": "vitest run", "lint:staged": "lint-staged", "commitlint": "commitlint", "build:schemas": "npm --prefix packages/api-schemas run build", @@ -78,6 +77,5 @@ }, "resolutions": { "lodash": "^4.17.21" - }, - + } }