Skip to content

Commit

Permalink
Saved Keypairs UI Tests (#1181)
Browse files Browse the repository at this point in the history
* Saved Keypairs UI Tests

* Wait for account response

* Fix Friendbot test

* Another try
  • Loading branch information
quietbits authored Dec 4, 2024
1 parent 4c3ca39 commit 796a005
Show file tree
Hide file tree
Showing 4 changed files with 306 additions and 4 deletions.
9 changes: 8 additions & 1 deletion src/app/(sidebar)/account/saved/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,14 @@ const SavedKeypairItem = ({
};

return (
<Box gap="sm" addlClassName="PageBody__content">
<Box
gap="sm"
addlClassName="PageBody__content"
data-testid="saved-keypair-item"
>
<Input
id={`saved-kp-${keypair.timestamp}-name`}
data-testid="saved-keypair-name"
fieldSize="md"
value={keypair.name}
readOnly
Expand All @@ -319,6 +324,7 @@ const SavedKeypairItem = ({

<Input
id={`saved-kp-${keypair.timestamp}-pk`}
data-testid="saved-keypair-pk"
fieldSize="md"
value={keypair.publicKey}
readOnly
Expand All @@ -328,6 +334,7 @@ const SavedKeypairItem = ({

<Input
id={`saved-kp-${keypair.timestamp}-sk`}
data-testid="saved-keypair-sk"
fieldSize="md"
value={keypair.secretKey}
readOnly
Expand Down
46 changes: 46 additions & 0 deletions tests/mock/localStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
export const SAVED_ACCOUNT_1 =
"GA46LGGOLXJY5OSX6N4LHV4MWDFXNGLK76I4NDNKKYAXRRSKI5AJGMXG";
export const SAVED_ACCOUNT_1_SECRET =
"SADVGAH3VA3NGZ5VLX2ZICV7JQAINB2ZJZYOPMBXUNI3YRLGWLOA2OFY";
export const SAVED_ACCOUNT_2 =
"GC5TQ7TXKHGE5JQMZPYV5KBSQ67X6PYQVU5QN7JRGWCHRA227UFPZ6LD";
export const SAVED_ACCOUNT_2_SECRET =
"SCPPMMBZBQGTGQKIPGFJDOHOPGK7SXZJGMYF76PFHF2PBLY2RWGZNSVV";

const SAVED_ACCOUNTS = [
{
timestamp: 1732287853955,
network: {
id: "testnet",
label: "Testnet",
},
name: "Account 1",
publicKey: SAVED_ACCOUNT_1,
secretKey: SAVED_ACCOUNT_1_SECRET,
},
{
timestamp: 1731083842367,
network: {
id: "testnet",
label: "Testnet",
},
name: "Account 2",
publicKey: SAVED_ACCOUNT_2,
secretKey: SAVED_ACCOUNT_2_SECRET,
},
];

export const MOCK_LOCAL_STORAGE = {
cookies: [],
origins: [
{
origin: "http://localhost:3000",
localStorage: [
{
name: "stellar_lab_saved_keypairs",
value: JSON.stringify(SAVED_ACCOUNTS),
},
],
},
],
};
249 changes: 249 additions & 0 deletions tests/savedKeypairs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
import { test, expect, Page } from "@playwright/test";
import {
MOCK_LOCAL_STORAGE,
SAVED_ACCOUNT_1,
SAVED_ACCOUNT_1_SECRET,
SAVED_ACCOUNT_2,
} from "./mock/localStorage";

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

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

test("Show no saved keypairs message", async ({ page }) => {
await expect(
page.getByText("There are no saved keypairs on Testnet network."),
).toBeVisible();
});

test.describe("Saved accounts", () => {
// Setting page context to share among all the tests in this section to keep
// local storage data
let pageContext: Page;

test.beforeAll(async ({ browser }) => {
const browserContext = await browser.newContext({
storageState: MOCK_LOCAL_STORAGE,
});
pageContext = await browserContext.newPage();

await pageContext.goto("http://localhost:3000/account/saved");

// Account 1 response (funded)
await pageContext.route(
`*/**/accounts/${SAVED_ACCOUNT_1}`,
async (route) => {
await route.fulfill({
status: 200,
contentType: "application/hal+json; charset=utf-8",
body: JSON.stringify(MOCK_ACCOUNT_1_RESPONSE),
});
},
);

// Account 2 response (unfunded)
await pageContext.route(
`*/**/accounts/${SAVED_ACCOUNT_2}`,
async (route) => {
await route.fulfill({
status: 404,
contentType: "application/problem+json; charset=utf-8",
body: JSON.stringify(MOCK_ACCOUNT_2_RESPONSE_UNFUNDED),
});
},
);

// Account 2 Friendbot response
await pageContext.route(
`https://friendbot.stellar.org/?addr=${SAVED_ACCOUNT_2}`,
async (route) => {
await route.fulfill({
status: 200,
contentType: "application/hal+json; charset=utf-8",
});
},
);
});

test("Loads", async () => {
await expect(
pageContext.getByText(
"There are no saved keypairs on Testnet network.",
),
).toBeHidden();

const keypairItems = pageContext.getByTestId("saved-keypair-item");

await expect(keypairItems).toHaveCount(2);
});

test("Funded account", async () => {
const keypairItem = pageContext.getByTestId("saved-keypair-item").first();

await expect(keypairItem).toBeVisible();
await expect(keypairItem.getByTestId("saved-keypair-name")).toHaveValue(
"Account 1",
);
await expect(keypairItem.getByTestId("saved-keypair-pk")).toHaveValue(
SAVED_ACCOUNT_1,
);
await expect(keypairItem.getByTestId("saved-keypair-sk")).toHaveValue(
SAVED_ACCOUNT_1_SECRET,
);
await expect(
keypairItem.getByText("Balance: 10000.0000000 XLM", { exact: true }),
).toBeVisible();
await expect(
keypairItem.getByText("Last saved Nov 22, 2024, 3:04 PM UTC", {
exact: true,
}),
).toBeVisible();
});

test("Fund unfunded account", async () => {
const keypairItem = pageContext.getByTestId("saved-keypair-item").nth(1);

await expect(keypairItem).toBeVisible();
await expect(keypairItem.getByTestId("saved-keypair-name")).toHaveValue(
"Account 2",
);

const fundButton = pageContext.getByText("Fund with Friendbot");

await expect(fundButton).toBeVisible();

// Wait for the Friendbot response
const friendbotResponse = pageContext.waitForResponse(
(response) =>
response.url().includes(`?addr=${SAVED_ACCOUNT_2}`) &&
response.status() === 200,
);

await fundButton.click();
await friendbotResponse;

// Mock Account 2 response
await pageContext.route(
`*/**/accounts/${SAVED_ACCOUNT_2}`,
async (route) => {
await route.fulfill({
status: 200,
contentType: "application/hal+json; charset=utf-8",
body: JSON.stringify(MOCK_ACCOUNT_1_RESPONSE),
});
},
);

const accountResponse = pageContext.waitForResponse(
(response) =>
response.url().includes(`accounts/${SAVED_ACCOUNT_2}`) &&
response.status() === 200,
);

await accountResponse;

await expect(fundButton).toBeHidden();
await expect(
keypairItem.getByText("Balance: 10000.0000000 XLM", { exact: true }),
).toBeVisible();
});

test("Delete keypair", async () => {
const keypairItem = pageContext.getByTestId("saved-keypair-item").first();
const deleteButton = keypairItem.locator(".Button--error");

await deleteButton.click();

const keypairItems = pageContext.getByTestId("saved-keypair-item");
await expect(keypairItems).toHaveCount(1);
});
});
});

// =============================================================================
// Mock data
// =============================================================================
const MOCK_ACCOUNT_1_RESPONSE = {
_links: {
self: {
href: "https://horizon-testnet.stellar.org/accounts/GDREGRXN4NCEKRACVD4SPMQTF6IEMNVSYT6ELCECQNEOPAIFA7MYVY6K",
},
transactions: {
href: "https://horizon-testnet.stellar.org/accounts/GDREGRXN4NCEKRACVD4SPMQTF6IEMNVSYT6ELCECQNEOPAIFA7MYVY6K/transactions{?cursor,limit,order}",
templated: true,
},
operations: {
href: "https://horizon-testnet.stellar.org/accounts/GDREGRXN4NCEKRACVD4SPMQTF6IEMNVSYT6ELCECQNEOPAIFA7MYVY6K/operations{?cursor,limit,order}",
templated: true,
},
payments: {
href: "https://horizon-testnet.stellar.org/accounts/GDREGRXN4NCEKRACVD4SPMQTF6IEMNVSYT6ELCECQNEOPAIFA7MYVY6K/payments{?cursor,limit,order}",
templated: true,
},
effects: {
href: "https://horizon-testnet.stellar.org/accounts/GDREGRXN4NCEKRACVD4SPMQTF6IEMNVSYT6ELCECQNEOPAIFA7MYVY6K/effects{?cursor,limit,order}",
templated: true,
},
offers: {
href: "https://horizon-testnet.stellar.org/accounts/GDREGRXN4NCEKRACVD4SPMQTF6IEMNVSYT6ELCECQNEOPAIFA7MYVY6K/offers{?cursor,limit,order}",
templated: true,
},
trades: {
href: "https://horizon-testnet.stellar.org/accounts/GDREGRXN4NCEKRACVD4SPMQTF6IEMNVSYT6ELCECQNEOPAIFA7MYVY6K/trades{?cursor,limit,order}",
templated: true,
},
data: {
href: "https://horizon-testnet.stellar.org/accounts/GDREGRXN4NCEKRACVD4SPMQTF6IEMNVSYT6ELCECQNEOPAIFA7MYVY6K/data/{key}",
templated: true,
},
},
id: "GDREGRXN4NCEKRACVD4SPMQTF6IEMNVSYT6ELCECQNEOPAIFA7MYVY6K",
account_id: "GDREGRXN4NCEKRACVD4SPMQTF6IEMNVSYT6ELCECQNEOPAIFA7MYVY6K",
sequence: "4914705307009024",
subentry_count: 0,
last_modified_ledger: 1144294,
last_modified_time: "2024-11-25T15:57:29Z",
thresholds: {
low_threshold: 0,
med_threshold: 0,
high_threshold: 0,
},
flags: {
auth_required: false,
auth_revocable: false,
auth_immutable: false,
auth_clawback_enabled: false,
},
balances: [
{
balance: "10000.0000000",
buying_liabilities: "0.0000000",
selling_liabilities: "0.0000000",
asset_type: "native",
},
],
signers: [
{
weight: 1,
key: "GDREGRXN4NCEKRACVD4SPMQTF6IEMNVSYT6ELCECQNEOPAIFA7MYVY6K",
type: "ed25519_public_key",
},
],
data: {},
num_sponsoring: 0,
num_sponsored: 0,
paging_token: "GDREGRXN4NCEKRACVD4SPMQTF6IEMNVSYT6ELCECQNEOPAIFA7MYVY6K",
};

const MOCK_ACCOUNT_2_RESPONSE_UNFUNDED = {
type: "https://stellar.org/horizon-errors/not_found",
title: "Resource Missing",
status: 404,
detail:
"The resource at the url requested was not found. This usually occurs for one of two reasons: The url requested is not valid, or no data in our database could be found with the parameters provided.",
};
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2356,9 +2356,9 @@ camelcase@^5.0.0:
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==

caniuse-lite@^1.0.30001579:
version "1.0.30001632"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001632.tgz#964207b7cba5851701afb4c8afaf1448db3884b6"
integrity sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==
version "1.0.30001685"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001685.tgz"
integrity sha512-e/kJN1EMyHQzgcMEEgoo+YTCO1NGCmIYHk5Qk8jT6AazWemS5QFKJ5ShCJlH3GZrNIdZofcNCEwZqbMjjKzmnA==

[email protected]:
version "0.4.4"
Expand Down

0 comments on commit 796a005

Please sign in to comment.