-
Notifications
You must be signed in to change notification settings - Fork 105
Completed Task #567
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Completed Task #567
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
00319a6
test(frontend): add SenderDashboard test coverage and creation entry …
Enemuo-debug 90f3f9c
fix(frontend): address review comments and fix duration logic in Send…
Enemuo-debug 42c8b95
fix(frontend): render estimatedEndLabel and increase docstring coverage
Enemuo-debug 2e9cd1a
test(frontend): add SenderDashboard test coverage and resolve merge c…
Enemuo-debug 8aa2d2a
Merge branch 'main' into main
Enemuo-debug File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| import { render, screen, waitFor, fireEvent } from "@testing-library/react"; | ||
| import { describe, it, expect, vi, beforeEach } from "vitest"; | ||
| import { http, HttpResponse } from "msw"; | ||
| import { server } from "../server"; | ||
| import { SenderDashboard } from "./SenderDashboard"; | ||
| import { Stream } from "../types/stream"; | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Fixtures | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| const SENDER = "GSENDER123"; | ||
|
|
||
| const mockActiveStream = (id: string, sender: string): Stream => ({ | ||
| id, | ||
| sender: sender, | ||
| recipient: `GRECIPIENT_${id}`, | ||
| assetCode: "USDC", | ||
| totalAmount: 1000, | ||
| durationSeconds: 86400, | ||
| startAt: 1700000000, | ||
| createdAt: 1699990000, | ||
| progress: { | ||
| status: "active", | ||
| ratePerSecond: 0.01157, | ||
| elapsedSeconds: 43200, | ||
| vestedAmount: 500, | ||
| remainingAmount: 500, | ||
| percentComplete: 50, | ||
| }, | ||
| }); | ||
|
|
||
| const mockCompletedStream = (id: string, sender: string): Stream => ({ | ||
| ...mockActiveStream(id, sender), | ||
| progress: { | ||
| ...mockActiveStream(id, sender).progress, | ||
| status: "completed", | ||
| elapsedSeconds: 86400, | ||
| vestedAmount: 1000, | ||
| remainingAmount: 0, | ||
| percentComplete: 100, | ||
| }, | ||
| }); | ||
|
|
||
| function setupSenderHandler(streams: Stream[], sender: string) { | ||
| server.use( | ||
| http.get("/api/streams", ({ request }) => { | ||
| const url = new URL(request.url); | ||
| if (url.searchParams.get("sender") === sender) { | ||
| return HttpResponse.json({ data: streams }); | ||
| } | ||
| return HttpResponse.json({ data: [] }); | ||
| }), | ||
| http.get("/api/config", () => { | ||
| return HttpResponse.json({ allowedAssets: ["USDC", "XLM"] }); | ||
| }) | ||
| ); | ||
| } | ||
|
|
||
| function setupErrorHandler() { | ||
| server.use( | ||
| http.get("/api/streams", () => { | ||
| return HttpResponse.json({ error: "Server Error 500" }, { status: 500 }); | ||
| }) | ||
| ); | ||
| } | ||
|
|
||
| describe("SenderDashboard", () => { | ||
| const onEditStartTime = vi.fn(); | ||
|
|
||
| beforeEach(() => { | ||
| vi.clearAllMocks(); | ||
| }); | ||
|
|
||
| it("renders with 3 active and 2 completed streams and asserts metric counts", async () => { | ||
| const SENDER_METRICS = "GSENDER_METRICS"; | ||
| const streams = [ | ||
| mockActiveStream("1", SENDER_METRICS), | ||
| mockActiveStream("2", SENDER_METRICS), | ||
| mockActiveStream("3", SENDER_METRICS), | ||
| mockCompletedStream("4", SENDER_METRICS), | ||
| mockCompletedStream("5", SENDER_METRICS), | ||
| ]; | ||
| setupSenderHandler(streams, SENDER_METRICS); | ||
|
|
||
| render(<SenderDashboard senderAddress={SENDER_METRICS} onEditStartTime={onEditStartTime} />); | ||
|
|
||
| // Wait for loading to finish | ||
| await waitFor(() => expect(screen.queryByText(/Sender Dashboard/)).toBeInTheDocument()); | ||
|
|
||
| // Check metrics | ||
| // Total USDC Outgoing: 5 * 1000 = 5000 | ||
| expect(await screen.findByText("5000")).toBeInTheDocument(); | ||
|
|
||
| const activeMetric = screen.getByText("Active").parentElement; | ||
| expect(activeMetric?.querySelector("strong")?.textContent).toBe("3"); | ||
|
|
||
| const completedMetric = screen.getByText("Completed").parentElement; | ||
| expect(completedMetric?.querySelector("strong")?.textContent).toBe("2"); | ||
| }); | ||
|
|
||
| it("renders with no streams and asserts zero metrics and 'create your first stream' prompt", async () => { | ||
| const SENDER_EMPTY = "GSENDER_EMPTY"; | ||
| setupSenderHandler([], SENDER_EMPTY); | ||
|
|
||
| render(<SenderDashboard senderAddress={SENDER_EMPTY} onEditStartTime={onEditStartTime} />); | ||
|
|
||
| await waitFor(() => expect(screen.getByText("No Streams Found")).toBeInTheDocument()); | ||
| expect(screen.getByText("Create your first stream")).toBeInTheDocument(); | ||
|
|
||
| // Verify metrics are absent in the empty state | ||
| expect(screen.queryByText(/Total .* Outgoing/i)).not.toBeInTheDocument(); | ||
| expect(screen.queryByText("Active")).not.toBeInTheDocument(); | ||
| expect(screen.queryByText("Scheduled")).not.toBeInTheDocument(); | ||
| expect(screen.queryByText("Completed")).not.toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it("shows CreateStreamForm when 'Create Stream' button is clicked", async () => { | ||
| const SENDER_CREATE = "GSENDER_CREATE"; | ||
| setupSenderHandler([], SENDER_CREATE); | ||
|
|
||
| render(<SenderDashboard senderAddress={SENDER_CREATE} onEditStartTime={onEditStartTime} />); | ||
|
|
||
| await waitFor(() => expect(screen.getByText("Create your first stream")).toBeInTheDocument()); | ||
|
|
||
| fireEvent.click(screen.getByText("Create your first stream")); | ||
|
|
||
| // Check if CreateStreamForm elements are present | ||
| expect(screen.getByText(/Recipient Account/i)).toBeInTheDocument(); | ||
| expect(screen.getByText("Back to Dashboard")).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it("shows CreateStreamForm when 'Create Stream' button in header is clicked", async () => { | ||
| const SENDER_HEADER = "GSENDER_HEADER"; | ||
| const streams = [mockActiveStream("1", SENDER_HEADER)]; | ||
| setupSenderHandler(streams, SENDER_HEADER); | ||
|
|
||
| render(<SenderDashboard senderAddress={SENDER_HEADER} onEditStartTime={onEditStartTime} />); | ||
|
|
||
| await waitFor(() => expect(screen.getByText("Sender Dashboard")).toBeInTheDocument()); | ||
|
|
||
| // Click the "Create Stream" button in the header | ||
| fireEvent.click(screen.getByRole("button", { name: /Create Stream/i })); | ||
|
|
||
| // Check if CreateStreamForm elements are present | ||
| expect(screen.getByText(/Recipient Account/i)).toBeInTheDocument(); | ||
| expect(screen.getByText("Back to Dashboard")).toBeInTheDocument(); | ||
|
|
||
| // Toggle back | ||
| fireEvent.click(screen.getByText("Back to Dashboard")); | ||
| expect(screen.queryByText(/Recipient Account/i)).not.toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it("surfaces a user-visible message on API error", async () => { | ||
| const SENDER_ERROR = "GSENDER_ERROR"; | ||
| setupErrorHandler(); | ||
|
|
||
| render(<SenderDashboard senderAddress={SENDER_ERROR} onEditStartTime={onEditStartTime} />); | ||
|
|
||
| await waitFor(() => expect(screen.getByText("Dashboard Load Failed")).toBeInTheDocument()); | ||
| expect(screen.getByText(/500/)).toBeInTheDocument(); | ||
| }); | ||
| }); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.