Skip to content
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

adding tests for create and fund account #808

Merged
merged 6 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions src/app/(sidebar)/account/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useRouter } from "next/navigation";
import { Card, Text, Button } from "@stellar/design-system";
import { Keypair } from "@stellar/stellar-sdk";

import { useIsTestingNetwork } from "@/hooks/useIsTestingNetwork";
import { Routes } from "@/constants/routes";
import { useStore } from "@/store/useStore";
import { GenerateKeypair } from "@/components/GenerateKeypair";
Expand All @@ -17,6 +18,8 @@ export default function CreateAccount() {
const router = useRouter();
const [secretKey, setSecretKey] = useState("");

const IS_TESTING_NETWORK = useIsTestingNetwork();

const generateKeypair = () => {
const keypair = Keypair.random();

Expand All @@ -39,18 +42,19 @@ export default function CreateAccount() {
account signer, and/or as a stellar-core node key.
</Text>
</div>
<div className="Account__CTA">
<div className="Account__CTA" data-testid="createAccount-buttons">
<Button size="md" variant="secondary" onClick={generateKeypair}>
Generate keypair
</Button>

<Button
size="md"
variant="tertiary"
onClick={() => router.push(Routes.ACCOUNT_FUND)}
>
Fund account with Friendbot
</Button>
{IS_TESTING_NETWORK ? (
<Button
size="md"
variant="tertiary"
onClick={() => router.push(Routes.ACCOUNT_FUND)}
>
Fund account with Friendbot
</Button>
) : null}
</div>

<ExpandBox isExpanded={Boolean(account.publicKey)} offsetTop="xl">
Expand Down
16 changes: 15 additions & 1 deletion src/app/(sidebar)/account/fund/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

import { useEffect, useState } from "react";
import { Alert, Card, Input, Text, Button } from "@stellar/design-system";
import Link from "next/link";
import { Routes } from "@/constants/routes";

import { shortenStellarAddress } from "@/helpers/shortenStellarAddress";
import { useIsTestingNetwork } from "@/hooks/useIsTestingNetwork";
import { useFriendBot } from "@/query/useFriendBot";
import { useStore } from "@/store/useStore";

Expand All @@ -18,6 +21,8 @@ export default function FundAccount() {
const [generatedPublicKey, setGeneratedPublicKey] = useState<string>("");
const [inlineErrorMessage, setInlineErrorMessage] = useState<string>("");

const IS_TESTING_NETWORK = useIsTestingNetwork();

const { error, isError, isLoading, isSuccess, refetch, isFetchedAfterMount } =
useFriendBot({
network: network.id,
Expand All @@ -30,6 +35,15 @@ export default function FundAccount() {
}
}, [isError, isSuccess]);

if (!IS_TESTING_NETWORK) {
Copy link
Contributor Author

@jeesunikim jeesunikim Apr 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also tried out with adding a middleware.ts to reroute to 404 if nextUrl includes mainnet in params. However, it doesn't always work in all cases. ex. user directly types in /account/fund in a browser which doesn't populate params until after user lands on the page.

another method i tried is router.push on the client side but that produced a glitch which wasn't a good ux.

so I came up with this. I'll @ at charles about it after hearing your thoughts!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that we shouldn't do anything tricky with the router. This approach is perfectly fine with me, we just need to update the copy to display the correct message. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll update the copy in the next pr when I make the overall changes following the updates

return (
<div className="Account">
<h2>Not Found</h2>
<p>Could not find requested resource</p>
<Link href={Routes.ROOT}>Return Home</Link>
</div>
);
}
return (
<div className="Account">
<Card>
Expand Down Expand Up @@ -60,7 +74,7 @@ export default function FundAccount() {
error={inlineErrorMessage}
/>

<div className="Account__CTA">
<div className="Account__CTA" data-testid="fundAccount-buttons">
<Button
disabled={!generatedPublicKey || Boolean(inlineErrorMessage)}
size="md"
Expand Down
53 changes: 35 additions & 18 deletions src/app/(sidebar)/account/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,50 @@ import React from "react";

import { LayoutSidebarContent } from "@/components/layout/LayoutSidebarContent";
import { Routes } from "@/constants/routes";
import { useIsTestingNetwork } from "@/hooks/useIsTestingNetwork";

// Includes navigation items that should ONLY display
// in TESTNET or FUTURENET
const DEFAULT_ACCOUNT_NAV_ITEMS = [
{
route: Routes.ACCOUNT_CREATE,
label: "Create Account",
},
{
route: Routes.ACCOUNT_FUND,
label: "Fund Account",
isTestnetOnly: true,
Copy link
Contributor Author

@jeesunikim jeesunikim Apr 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for filtering on side nav

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We won't need this extra logic for the new update where we're not hiding the nav, right? Just a note for the next PR. 🤓

},
{
route: Routes.ACCOUNT_CREATE_MUXED,
label: "Create Muxed Account",
},
{
route: Routes.ACCOUNT_PARSE_MUXED,
label: "Parse Muxed Account",
},
];

export default function AccountTemplate({
children,
}: {
children: React.ReactNode;
}) {
const IS_TESTING_NETWORK = useIsTestingNetwork();

// Filtered navigation items for MAINNET
const filteredNavItems = DEFAULT_ACCOUNT_NAV_ITEMS.filter(
({ isTestnetOnly }) => !isTestnetOnly,
);

const navItems = IS_TESTING_NETWORK
? DEFAULT_ACCOUNT_NAV_ITEMS
: filteredNavItems;

return (
<LayoutSidebarContent
sidebar={{
navItems: [
{
route: Routes.ACCOUNT_CREATE,
label: "Create Account",
},
{
route: Routes.ACCOUNT_FUND,
label: "Fund Account",
},
{
route: Routes.ACCOUNT_CREATE_MUXED,
label: "Create Muxed Account",
},
{
route: Routes.ACCOUNT_PARSE_MUXED,
label: "Parse Muxed Account",
},
],
navItems,
}}
>
{children}
Expand Down
2 changes: 1 addition & 1 deletion src/app/(sidebar)/account/muxed-create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export default function CreateMuxedAccount() {
/>

<Input
id="muxed-account-iD"
id="muxed-account-id"
fieldSize="md"
placeholder="Ex: 1"
label="Muxed Account ID"
Expand Down
2 changes: 1 addition & 1 deletion src/components/MuxedAccountResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const MuxedAccountResult = ({
muxedId: string;
muxedAddress: string;
}) => (
<div className="Account__result">
<div className="Account__result" data-testid="createAccount-success">
<PubKeyPicker
id="muxed-public-key-result"
label="Base Account G Address"
Expand Down
1 change: 1 addition & 0 deletions src/components/layout/LayoutSidebarContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type SidebarLink = {
route: Routes | string;
label: string;
icon?: ReactNode;
isTestnetOnly?: boolean;
nestedItems?: SidebarLink[];
};

Expand Down
7 changes: 7 additions & 0 deletions src/hooks/useIsTestingNetwork.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useStore } from "@/store/useStore";

export const useIsTestingNetwork = (): boolean => {
jeesunikim marked this conversation as resolved.
Show resolved Hide resolved
const { network } = useStore();

return network.id === "testnet" || network.id === "futurenet";
};
39 changes: 39 additions & 0 deletions tests/createAccountPage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { test, expect } from "@playwright/test";

test.describe("Create Account Page", () => {
test.beforeEach(async ({ page }) => {
await page.goto("http://localhost:3000/account/create");
});

test("Loads", async ({ page }) => {
await expect(page.locator("h1")).toHaveText("Keypair Generator");
});

test("Renders 'Generate keypair' and 'Fund account' button", async ({
page,
}) => {
const buttonContainer = page.getByTestId("createAccount-buttons");
expect(buttonContainer.getByText("Generate keypair")).toBeVisible;
expect(buttonContainer.getByText("Fund account with Friendbot"))
.toBeVisible;
});

test("Test 'Generate keypair' button", async ({ page }) => {
await page.getByRole("button", { name: "Generate keypair" }).click();

await expect(
page.locator("input[id='generate-keypair-publickey']"),
).toHaveValue(/^G/);
await expect(
page.locator("input[id='generate-keypair-secretkey']"),
).toHaveValue(/^S/);
});

test("Test 'Fund account' button", async ({ page }) => {
await page
.getByRole("button", { name: "Fund account with Friendbot" })
.click();

await expect(page).toHaveURL(/.*\/account\/fund/);
});
});
72 changes: 72 additions & 0 deletions tests/createMuxedAccountPage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { test, expect } from "@playwright/test";

import { Account, MuxedAccount } from "@stellar/stellar-sdk";

test.describe("Create Muxed Account Page", () => {
test.beforeEach(async ({ page }) => {
await page.goto("http://localhost:3000/account/muxed-create");
});

test("Loads", async ({ page }) => {
await expect(page.locator("h1")).toHaveText("Create Multiplexed Account");
});

test("Renders 'Base Account G Address' and 'Muxed Account ID' input field", async ({
page,
}) => {
expect(page.locator("input[id='muxed-public-key']")).toBeVisible;
expect(page.locator("input[id='muxed-account-iD']")).toBeVisible;
jeesunikim marked this conversation as resolved.
Show resolved Hide resolved
});

test("Gets an error with an invalid public key in 'Base Account G Address' field", async ({
page,
}) => {
const publicKeyInput = page.locator("#muxed-public-key");

// Type in an invalid string in 'Public Key' input field
await publicKeyInput.fill("XLKDSFJLSKDJF");

await expect(publicKeyInput).toHaveAttribute("aria-invalid", "true");
await expect(
page.getByText("Base account address should start with G"),
).toBeVisible();
});

test("Gets an error with a non whole number 'Muxed Account ID' field", async ({
page,
}) => {
const muxedAccountIdInput = page.locator("#muxed-account-id");

await muxedAccountIdInput.fill("XLKDSFJLSKDJF");

await expect(muxedAccountIdInput).toHaveAttribute("aria-invalid", "true");
await expect(page.getByText("Expected a whole number")).toBeVisible();
});

test("Successfully creates a muxed account", async ({ page }) => {
const publicKey =
"GDVOT2ALMUF3G54RBHNJUEV6LOAZCQQCARHEVNUPKGMVPWFC4PFN33QR";
const muxedId = "2";
const publicKeyInput = page.locator("#muxed-public-key");
await publicKeyInput.fill(publicKey);

const muxedAccountIdInput = page.locator("#muxed-account-id");
await muxedAccountIdInput.fill(muxedId);

const createButton = page.getByRole("button").getByText("Create");

await expect(publicKeyInput).toHaveAttribute("aria-invalid", "false");
await expect(muxedAccountIdInput).toHaveAttribute("aria-invalid", "false");
await expect(createButton).toBeEnabled();

await createButton.click();

await expect(page.getByTestId("createAccount-success")).toBeVisible();
jeesunikim marked this conversation as resolved.
Show resolved Hide resolved

const muxedValue = page.locator("input[id='muxed-account-address-result']");

const muxedAccount = new MuxedAccount(new Account(publicKey, "0"), muxedId);

await expect(muxedValue).toHaveValue(muxedAccount.accountId());
jeesunikim marked this conversation as resolved.
Show resolved Hide resolved
});
});
Loading
Loading