From e940a79c5c03b0188f815c553afcdf15afb3560b Mon Sep 17 00:00:00 2001 From: Iveta Date: Fri, 5 Apr 2024 15:40:53 -0400 Subject: [PATCH] Endpoints tests (#811) * All Assets * Claimable Balances * Effects + refactor endpoint URL template * Fee Stats * Ledgers * Offers * Operations * Payments * Transactions * Update SDS * Order book + asset object params * Trade aggregations * Fix PrettyJson records rendering * Trades * Cleanup * Paths * Liquidity pools * Fix URL validation * Cleanup * Update reserves asset type * Cleanup * Endpoints tests * Update asset validation * openUrl helper --- .../explore-endpoints/[[...pages]]/page.tsx | 28 +-- src/app/page.tsx | 18 +- src/components/MainNav.tsx | 2 +- .../layout/LayoutSidebarContent.tsx | 14 +- src/constants/exploreEndpointsPages.ts | 4 +- src/helpers/openUrl.ts | 3 + src/validate/methods/asset.ts | 9 +- tests/endpointsPage.test.ts | 183 ++++++++++++++++++ 8 files changed, 227 insertions(+), 34 deletions(-) create mode 100644 src/helpers/openUrl.ts create mode 100644 tests/endpointsPage.test.ts diff --git a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx index 1f41821a..122b7171 100644 --- a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx +++ b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx @@ -26,6 +26,7 @@ import { isEmptyObject } from "@/helpers/isEmptyObject"; import { sanitizeArray } from "@/helpers/sanitizeArray"; import { sanitizeObject } from "@/helpers/sanitizeObject"; import { parseJsonString } from "@/helpers/parseJsonString"; +import { openUrl } from "@/helpers/openUrl"; import { Routes } from "@/constants/routes"; import { EXPLORE_ENDPOINTS_PAGES_HORIZON } from "@/constants/exploreEndpointsPages"; @@ -440,13 +441,17 @@ export default function ExploreEndpoints() { <>
+
{pageData.requestMethod}
} @@ -457,6 +462,7 @@ export default function ExploreEndpoints() { type="submit" disabled={!isSubmitEnabled()} isLoading={isLoading || isFetching} + data-testid="endpoints-submitBtn" > Submit @@ -486,7 +492,7 @@ export default function ExploreEndpoints() { return (
-
+
{renderPostPayload()} {allFields.map((f) => { @@ -616,7 +622,11 @@ export default function ExploreEndpoints() { {page.label} - }> + } + data-testid="endpoints-docsLink" + > {`View ${pageData.docsLabel ? `${pageData.docsLabel} ` : ""}documentation`}
@@ -703,10 +713,7 @@ const ExploreEndpointsLandingPage = () => { buttonLabel: "See docs", buttonIcon: , buttonAction: () => - window.open( - "https://developers.stellar.org/network/soroban-rpc/methods", - "_blank", - ), + openUrl("https://developers.stellar.org/network/soroban-rpc/methods"), }, { id: "horizon", @@ -715,10 +722,7 @@ const ExploreEndpointsLandingPage = () => { buttonLabel: "See docs", buttonIcon: , buttonAction: () => - window.open( - "https://developers.stellar.org/network/horizon/resources", - "_blank", - ), + openUrl("https://developers.stellar.org/network/horizon/resources"), }, ]; @@ -751,7 +755,7 @@ const ExploreEndpointsLandingPage = () => { For Stellar docs, take a look at the{" "} - + Stellar developers site . diff --git a/src/app/page.tsx b/src/app/page.tsx index be4f8959..64854cfa 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,6 +1,5 @@ "use client"; -import { useRouter } from "next/navigation"; import { Card, Link, Text, Icon } from "@stellar/design-system"; import { NextLink } from "@/components/NextLink"; @@ -8,10 +7,9 @@ import { LayoutContentContainer } from "@/components/layout/LayoutContentContain import { InfoCards } from "@/components/InfoCards"; import { SdsLink } from "@/components/SdsLink"; import { Routes } from "@/constants/routes"; +import { openUrl } from "@/helpers/openUrl"; export default function Introduction() { - const router = useRouter(); - const infoCards = [ { id: "stellar-quest", @@ -20,7 +18,7 @@ export default function Introduction() { "Learn to build world-class applications on the Stellar network in a gamified experience", buttonLabel: "Go to site", buttonIcon: , - buttonAction: () => window.open("https://quest.stellar.org/", "_blank"), + buttonAction: () => openUrl("https://quest.stellar.org/"), }, { id: "tools", @@ -29,7 +27,8 @@ export default function Introduction() { "Tools, like the Stellar CLI, for reading and interacting with smart contracts on the Stellar Network", buttonLabel: "See tools", buttonIcon: undefined, - buttonAction: () => router.push(Routes.SOROBAN_CONTRACT_EXPLORER), + buttonAction: () => + openUrl("https://developers.stellar.org/docs/tools/sdks"), }, { id: "stellar-rpc", @@ -38,10 +37,7 @@ export default function Introduction() { buttonLabel: "Go to docs", buttonIcon: , buttonAction: () => - window.open( - "https://developers.stellar.org/network/soroban-rpc", - "_blank", - ), + openUrl("https://developers.stellar.org/network/soroban-rpc"), }, { id: "horizon", @@ -51,7 +47,7 @@ export default function Introduction() { buttonLabel: "Go to docs", buttonIcon: , buttonAction: () => - window.open("https://developers.stellar.org/network/horizon", "_blank"), + openUrl("https://developers.stellar.org/network/horizon"), }, ]; @@ -83,7 +79,7 @@ export default function Introduction() { For Stellar docs, take a look at the{" "} - + Stellar developers site . diff --git a/src/components/MainNav.tsx b/src/components/MainNav.tsx index 233b3abe..cbfa7189 100644 --- a/src/components/MainNav.tsx +++ b/src/components/MainNav.tsx @@ -37,7 +37,7 @@ const primaryNavLinks: NavLink[] = [ const secondaryNavLinks: NavLink[] = [ { - href: "https://developers.stellar.org/network", + href: "https://developers.stellar.org/", label: "View Documentation", }, ]; diff --git a/src/components/layout/LayoutSidebarContent.tsx b/src/components/layout/LayoutSidebarContent.tsx index c28bc802..b12fb568 100644 --- a/src/components/layout/LayoutSidebarContent.tsx +++ b/src/components/layout/LayoutSidebarContent.tsx @@ -45,9 +45,13 @@ export const LayoutSidebarContent = ({
{sidebar.instruction ? ( -
+
{sidebar.instruction}
) : null} @@ -95,12 +99,16 @@ const Link = ({ item, pathname }: { item: SidebarLink; pathname: string }) => { if (item.nestedItems?.length) { return ( -
+
{ setIsExpanded(!isExpanded); }} + data-testid="endpoints-sidebar-linkToggle" > {item.label}
@@ -109,6 +117,7 @@ const Link = ({ item, pathname }: { item: SidebarLink; pathname: string }) => {
{item.nestedItems.map((nested) => ( @@ -117,6 +126,7 @@ const Link = ({ item, pathname }: { item: SidebarLink; pathname: string }) => { href={nested.route} className="SidebarLink" data-is-active={pathname === nested.route} + data-testid="endpoints-sidebar-link" > {nested.label} diff --git a/src/constants/exploreEndpointsPages.ts b/src/constants/exploreEndpointsPages.ts index cd7c1d0b..5b6b6472 100644 --- a/src/constants/exploreEndpointsPages.ts +++ b/src/constants/exploreEndpointsPages.ts @@ -94,7 +94,7 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { form: { docsUrl: "https://developers.stellar.org/network/horizon/resources/list-all-claimable-balances", - docsLabel: "claimable balance", + docsLabel: "claimable balances", requestMethod: "GET", endpointUrlTemplate: "/claimable_balances/{?sponsor,asset,claimant,cursor,limit,order}", @@ -108,7 +108,7 @@ export const EXPLORE_ENDPOINTS_PAGES_HORIZON: ExploreEndpointsPagesProps = { form: { docsUrl: "https://developers.stellar.org/network/horizon/resources/retrieve-a-claimable-balance", - docsLabel: "claimable balances", + docsLabel: "claimable balance", requestMethod: "GET", endpointUrlTemplate: "/claimable_balances/{claimable_balance_id}", requiredParams: "claimable_balance_id", diff --git a/src/helpers/openUrl.ts b/src/helpers/openUrl.ts new file mode 100644 index 00000000..60bea8a8 --- /dev/null +++ b/src/helpers/openUrl.ts @@ -0,0 +1,3 @@ +export const openUrl = (url: string) => { + return window.open(url, "_blank", "noopener,noreferrer"); +}; diff --git a/src/validate/methods/asset.ts b/src/validate/methods/asset.ts index cb3c1e85..2969c4a7 100644 --- a/src/validate/methods/asset.ts +++ b/src/validate/methods/asset.ts @@ -3,17 +3,14 @@ import { assetCode } from "./assetCode"; import { publicKey } from "./publicKey"; import { AssetObjectValue } from "@/types/types"; -export const asset = ( - asset: AssetObjectValue | undefined, - isRequired?: boolean, -) => { +export const asset = (asset: AssetObjectValue | undefined) => { if (asset?.type && asset.type === "native") { return false; } const invalid = Object.entries({ - code: assetCode(asset?.code || "", asset?.type, isRequired), - issuer: publicKey(asset?.issuer || "", isRequired), + code: assetCode(asset?.code || "", asset?.type), + issuer: publicKey(asset?.issuer || ""), }).reduce((res, cur) => { const [key, value] = cur; diff --git a/tests/endpointsPage.test.ts b/tests/endpointsPage.test.ts new file mode 100644 index 00000000..ac2aca15 --- /dev/null +++ b/tests/endpointsPage.test.ts @@ -0,0 +1,183 @@ +import { test, expect } from "@playwright/test"; + +test.describe("Endpoints page", () => { + test.beforeEach(async ({ page }) => { + await page.goto("http://localhost:3000/explore-endpoints"); + }); + + test("Loads", async ({ page }) => { + await expect(page.locator("h1")).toHaveText("Explore Endpoints"); + }); + + test("Renders info cards", async ({ page }) => { + await expect(page.locator("h2")).toHaveText([ + "Stellar RPC Endpoints", + "Horizon Endpoints", + ]); + }); + + test.describe("Sidebar", () => { + test("Renders Horizon endpoints", async ({ page }) => { + const sidebar = page.getByTestId("endpoints-sidebar-section").first(); + await expect( + sidebar.getByTestId("endpoints-sidebar-subtitle"), + ).toContainText("Horizon Endpoints"); + + const linkToggles = sidebar.getByTestId("endpoints-sidebar-linkToggle"); + + await expect(linkToggles).toHaveCount(15); + + await expect(linkToggles).toContainText([ + "Accounts", + "Assets", + "Claimable Balances", + "Effects", + "Fee Stats", + "Ledgers", + "Liquidity Pools", + "Offers", + "Operations", + "Order Book", + "Paths", + "Payments", + "Trade Aggregations", + "Trades", + "Transactions", + ]); + }); + + test("Expands dropdown on click with correct links", async ({ page }) => { + const sidebar = page.getByTestId("endpoints-sidebar-section").first(); + const accountsLink = sidebar + .getByTestId("endpoints-sidebar-linkToggle") + .filter({ hasText: "Accounts" }); + + const parent = sidebar.getByTestId( + "endpoints-sidebar/explore-endpoints/accounts", + ); + await expect(parent).toBeVisible(); + + const linksContainer = parent.getByTestId( + "endpoints-sidebar-linksContainer", + ); + + await expect(linksContainer).toBeHidden(); + await accountsLink.click(); + await expect(linksContainer).toBeVisible(); + + await expect( + linksContainer.getByTestId("endpoints-sidebar-link"), + ).toContainText(["All Accounts", "Single Account"]); + + await expect( + linksContainer + .getByTestId("endpoints-sidebar-link") + .filter({ hasText: "All Accounts" }), + ).toHaveAttribute("href", "/explore-endpoints/accounts"); + }); + }); + + test.describe("All Accounts", () => { + test.beforeEach(async ({ page }) => { + await page.goto("http://localhost:3000/explore-endpoints/accounts"); + }); + + test("Page loads with correct title and view docs link", async ({ + page, + }) => { + await expect(page.locator("h1")).toHaveText("All Accounts"); + + const docsLink = page.getByTestId("endpoints-docsLink"); + + await expect(docsLink).toContainText("View accounts documentation"); + await expect(docsLink).toHaveAttribute( + "href", + "https://developers.stellar.org/network/horizon/resources/list-all-accounts", + ); + }); + + test("URL and buttons have correct defaults", async ({ page }) => { + await expect(page.getByTestId("endpoints-url")).toHaveValue( + "https://horizon-testnet.stellar.org/accounts/", + ); + await expect(page.getByTestId("endpoints-url-method")).toContainText( + "GET", + ); + await expect(page.getByTestId("endpoints-submitBtn")).toBeEnabled(); + }); + + test("URL updates when inputs change", async ({ page }) => { + await page + .getByLabel("Sponsor(optional)") + .fill("GBV6DGKNXELF3TYU4V3NRCF57Q3477KLUBBINVG5GGR3LJYVQPKPDX4C"); + + await expect(page.getByTestId("endpoints-url")).toHaveValue( + "https://horizon-testnet.stellar.org/accounts/?sponsor=GBV6DGKNXELF3TYU4V3NRCF57Q3477KLUBBINVG5GGR3LJYVQPKPDX4C", + ); + + await page.getByLabel("Limit(optional)").fill("2"); + + await expect(page.getByTestId("endpoints-url")).toHaveValue( + "https://horizon-testnet.stellar.org/accounts/?sponsor=GBV6DGKNXELF3TYU4V3NRCF57Q3477KLUBBINVG5GGR3LJYVQPKPDX4C&limit=2", + ); + }); + }); + + test.describe("Effects for Account", () => { + test.beforeEach(async ({ page }) => { + await page.goto( + "http://localhost:3000/explore-endpoints/effects/account", + ); + }); + + test("Page loads with correct title and view docs link", async ({ + page, + }) => { + await expect(page.locator("h1")).toHaveText("Effects for Account"); + + const docsLink = page.getByTestId("endpoints-docsLink"); + + await expect(docsLink).toContainText( + "View effects for account documentation", + ); + await expect(docsLink).toHaveAttribute( + "href", + "https://developers.stellar.org/network/horizon/resources/get-effects-by-account-id", + ); + }); + + test("URL and buttons have correct defaults", async ({ page }) => { + await expect(page.getByTestId("endpoints-url")).toHaveValue( + "https://horizon-testnet.stellar.org/accounts//effects", + ); + await expect(page.getByTestId("endpoints-url-method")).toContainText( + "GET", + ); + await expect(page.getByTestId("endpoints-submitBtn")).toBeDisabled(); + }); + + test("Filling required field enables submit button", async ({ page }) => { + const accountInput = page.getByLabel("Account ID"); + await accountInput.fill( + "GAXELIT3WH4ALN66AYQAZILPSSZVIQV67R62FACYC6DHNZCLCN45CA2A", + ); + + await expect(accountInput).toHaveAttribute("aria-invalid", "false"); + await expect(page.getByTestId("endpoints-url")).toHaveValue( + "https://horizon-testnet.stellar.org/accounts/GAXELIT3WH4ALN66AYQAZILPSSZVIQV67R62FACYC6DHNZCLCN45CA2A/effects", + ); + await expect(page.getByTestId("endpoints-submitBtn")).toBeEnabled(); + }); + + test("Input with error disables submit button", async ({ page }) => { + const accountInput = page.getByLabel("Account ID"); + await accountInput.fill("abc"); + + await expect(accountInput).toHaveAttribute("aria-invalid", "true"); + await expect(page.getByTestId("endpoints-pageContent")).toContainText( + "Public key is invalid.", + ); + await expect(page.getByTestId("endpoints-submitBtn")).toBeDisabled(); + }); + }); +});