-
Notifications
You must be signed in to change notification settings - Fork 44
port contract id search back to master #2684
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| import { Page } from "@playwright/test"; | ||
| import { test, expect } from "./test-fixtures"; | ||
| import { loginToTestAccount } from "./helpers/login"; | ||
| import { | ||
| stubTokenDetails, | ||
| stubIsSac, | ||
| stubScanAssetSafe, | ||
| stubAssetSearchWithContractId, | ||
| stubAccountBalancesE2e, | ||
| } from "./helpers/stubs"; | ||
|
|
||
| /** | ||
| * Helper to locate a ManageAssetRow by its exact asset code. | ||
| */ | ||
| const getAssetRow = (page: Page, code: string) => | ||
| page.getByTestId("ManageAssetRow").filter({ | ||
| has: page.getByTestId("ManageAssetCode").getByText(code, { exact: true }), | ||
| }); | ||
|
|
||
| test("Stellar Expert contract ID result shows as already added", async ({ | ||
| page, | ||
| extensionId, | ||
| context, | ||
| }) => { | ||
| test.slow(); | ||
|
|
||
| await loginToTestAccount({ | ||
| page, | ||
| extensionId, | ||
| context, | ||
| stubOverrides: async () => { | ||
| await stubAssetSearchWithContractId(page); | ||
| await stubAccountBalancesE2e(page); | ||
| await stubTokenDetails(page); | ||
| await stubIsSac(page); | ||
| await stubScanAssetSafe(page); | ||
| }, | ||
| }); | ||
|
|
||
| await page.getByTestId("account-options-dropdown").click(); | ||
| const manageAssets = page.getByText("Manage assets"); | ||
| await expect(manageAssets).toBeVisible(); | ||
| await manageAssets.click(); | ||
|
|
||
| await expect(page.getByText("Your assets")).toBeVisible({ timeout: 10000 }); | ||
| await page.getByText("Add an asset").click({ force: true }); | ||
|
|
||
| await page.getByTestId("search-asset-input").fill("E2E"); | ||
|
|
||
| // Wait for search results to appear | ||
| const rows = page.getByTestId("ManageAssetRow"); | ||
| await expect(rows.first()).toBeVisible({ timeout: 10000 }); | ||
|
|
||
| // The E2E token row should show the ellipsis menu instead of "Add" | ||
| // because the token is already in the user's balances | ||
| await expect( | ||
| page.getByTestId("ManageAssetRowButton__ellipsis-E2E"), | ||
| ).toBeVisible(); | ||
| }); | ||
|
|
||
| test("Stellar Expert contract ID result shows Add when not owned", async ({ | ||
| page, | ||
| extensionId, | ||
| context, | ||
| }) => { | ||
| test.slow(); | ||
|
|
||
| await loginToTestAccount({ | ||
| page, | ||
| extensionId, | ||
| context, | ||
| stubOverrides: async () => { | ||
| await stubAssetSearchWithContractId(page); | ||
| await stubTokenDetails(page); | ||
| await stubIsSac(page); | ||
| await stubScanAssetSafe(page); | ||
|
Comment on lines
+72
to
+76
|
||
| }, | ||
| }); | ||
|
|
||
| await page.getByTestId("account-options-dropdown").click(); | ||
| const manageAssets = page.getByText("Manage assets"); | ||
| await expect(manageAssets).toBeVisible(); | ||
| await manageAssets.click(); | ||
|
|
||
| await expect(page.getByText("Your assets")).toBeVisible({ timeout: 10000 }); | ||
| await page.getByText("Add an asset").click({ force: true }); | ||
|
|
||
| await page.getByTestId("search-asset-input").fill("E2E"); | ||
|
|
||
| // Wait for search results | ||
| const rows = page.getByTestId("ManageAssetRow"); | ||
| await expect(rows.first()).toBeVisible({ timeout: 10000 }); | ||
|
|
||
| // Find the E2E token row by its exact asset code | ||
| const e2eRow = getAssetRow(page, "E2E"); | ||
| await expect(e2eRow).toBeVisible(); | ||
|
|
||
| // The button should say "Add" since the user does not have this token | ||
| const rowButton = e2eRow.getByTestId("ManageAssetRowButton"); | ||
| await expect(rowButton).toHaveText("Add"); | ||
| }); | ||
|
|
||
| test("Can add a token returned as contract ID from Stellar Expert search", async ({ | ||
| page, | ||
| extensionId, | ||
| context, | ||
| }) => { | ||
| test.slow(); | ||
|
|
||
| await loginToTestAccount({ | ||
| page, | ||
| extensionId, | ||
| context, | ||
| stubOverrides: async () => { | ||
| await stubAssetSearchWithContractId(page); | ||
| await stubTokenDetails(page); | ||
| await stubIsSac(page); | ||
| await stubScanAssetSafe(page); | ||
|
Comment on lines
+114
to
+118
|
||
| }, | ||
| }); | ||
|
|
||
| await page.getByTestId("account-options-dropdown").click(); | ||
| const manageAssets = page.getByText("Manage assets"); | ||
| await expect(manageAssets).toBeVisible(); | ||
| await manageAssets.click(); | ||
|
|
||
| await expect(page.getByText("Your assets")).toBeVisible({ timeout: 10000 }); | ||
| await page.getByText("Add an asset").click({ force: true }); | ||
|
|
||
| await page.getByTestId("search-asset-input").fill("E2E"); | ||
|
|
||
| // Wait for search results | ||
| const rows = page.getByTestId("ManageAssetRow"); | ||
| await expect(rows.first()).toBeVisible({ timeout: 10000 }); | ||
|
|
||
| // Find the E2E token row by its exact asset code and click Add | ||
| const e2eRow = getAssetRow(page, "E2E"); | ||
| await expect(e2eRow).toBeVisible(); | ||
| await e2eRow.getByTestId("ManageAssetRowButton").click(); | ||
|
|
||
| // Should navigate to the Add Token confirmation page | ||
| await expect(page.getByTestId("ToggleToken__asset-code")).toHaveText( | ||
| "E2E Token", | ||
| ); | ||
| await expect(page.getByTestId("ToggleToken__asset-add-remove")).toHaveText( | ||
| "Add Token", | ||
| ); | ||
|
|
||
| // Confirm the add | ||
| await page.getByRole("button", { name: "Confirm" }).click(); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,7 +29,15 @@ import { AppDispatch, store } from "popup/App"; | |
| interface AssetRecord { | ||
| asset: string; | ||
| domain?: string; | ||
| tomlInfo?: { image: string }; | ||
| code?: string; | ||
| token_name?: string; | ||
| decimals?: number; | ||
| tomlInfo?: { | ||
| image?: string; | ||
| code?: string; | ||
| issuer?: string; | ||
| name?: string; | ||
| }; | ||
| } | ||
|
|
||
| interface AssetLookupDetails { | ||
|
|
@@ -257,7 +265,8 @@ const useAssetLookup = () => { | |
|
|
||
| /* | ||
| * Fetches data from Stellar Expert for the given asset. | ||
| * It returns an array of ManageAssetCurrency objects. | ||
| * Returns an array of ManageAssetCurrency objects for both classic | ||
| * ({code}-{issuer}) and contract ID results. | ||
| * | ||
| * @param {string} asset - The asset to look up. | ||
| * @returns {Promise<ManageAssetCurrency[]>} | ||
|
|
@@ -282,13 +291,27 @@ const useAssetLookup = () => { | |
| throw new Error("Failed to fetch Stellar Expert data"); | ||
| }); | ||
|
|
||
| return resJson._embedded.records.map((record: AssetRecord) => ({ | ||
| code: record.asset.split("-")[0], | ||
| issuer: record.asset.split("-")[1], | ||
| domain: record.domain, | ||
| image: record.tomlInfo?.image, | ||
| isSuspicious: false, | ||
| })); | ||
| return resJson._embedded.records.map((record: AssetRecord) => { | ||
| if (isContractId(record.asset)) { | ||
| return { | ||
| code: record.code || record.tomlInfo?.code || "", | ||
| issuer: record.asset, | ||
| contract: record.asset, | ||
|
Comment on lines
+295
to
+299
|
||
| domain: record.domain ?? null, | ||
| image: record.tomlInfo?.image, | ||
| name: record.token_name || record.tomlInfo?.name, | ||
| decimals: record.decimals, | ||
| isSuspicious: false, | ||
| }; | ||
| } | ||
| return { | ||
| code: record.asset.split("-")[0], | ||
| issuer: record.asset.split("-")[1], | ||
| domain: record.domain ?? null, | ||
| image: record.tomlInfo?.image, | ||
| isSuspicious: false, | ||
| }; | ||
| }); | ||
| }; | ||
|
|
||
| /* | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| import { getSwapErrorMessage, ERROR_TO_DISPLAY } from "../useSimulateSwapData"; | ||
|
|
||
| const CONTRACT_ID = "CAZXEHTSQATVQVWDPWWDTFSY6CM764JD4MZ6HUVPO3QKS64QEEP4KJH7"; | ||
| const CLASSIC_ISSUER = | ||
| "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"; | ||
|
|
||
| const classicAsset = { issuer: CLASSIC_ISSUER }; | ||
| const contractAsset = { issuer: CONTRACT_ID }; | ||
|
|
||
| describe("getSwapErrorMessage", () => { | ||
| it("returns custom token error when source is a contract ID", () => { | ||
| const result = getSwapErrorMessage( | ||
| new Error("some error"), | ||
| contractAsset, | ||
| classicAsset, | ||
| ); | ||
| expect(result).toBe(ERROR_TO_DISPLAY.CUSTOM_TOKEN_NOT_SUPPORTED); | ||
| }); | ||
|
|
||
| it("returns custom token error when dest is a contract ID", () => { | ||
| const result = getSwapErrorMessage( | ||
| new Error("some error"), | ||
| classicAsset, | ||
| contractAsset, | ||
| ); | ||
| expect(result).toBe(ERROR_TO_DISPLAY.CUSTOM_TOKEN_NOT_SUPPORTED); | ||
| }); | ||
|
|
||
| it("returns custom token error when both are contract IDs", () => { | ||
| const result = getSwapErrorMessage( | ||
| new Error("some error"), | ||
| contractAsset, | ||
| contractAsset, | ||
| ); | ||
| expect(result).toBe(ERROR_TO_DISPLAY.CUSTOM_TOKEN_NOT_SUPPORTED); | ||
| }); | ||
|
|
||
| it("returns known error even when assets are contract IDs", () => { | ||
| const result = getSwapErrorMessage( | ||
| new Error(ERROR_TO_DISPLAY.NO_PATH_FOUND), | ||
| contractAsset, | ||
| classicAsset, | ||
| ); | ||
| expect(result).toBe(ERROR_TO_DISPLAY.NO_PATH_FOUND); | ||
| }); | ||
|
|
||
| it("returns known error message for classic assets", () => { | ||
| const result = getSwapErrorMessage( | ||
| new Error(ERROR_TO_DISPLAY.NO_PATH_FOUND), | ||
| classicAsset, | ||
| classicAsset, | ||
| ); | ||
| expect(result).toBe(ERROR_TO_DISPLAY.NO_PATH_FOUND); | ||
| }); | ||
|
|
||
| it("returns unknown error for unrecognized Error with classic assets", () => { | ||
| const result = getSwapErrorMessage( | ||
| new Error("something unexpected"), | ||
| classicAsset, | ||
| classicAsset, | ||
| ); | ||
| expect(result).toBe( | ||
| "We had an issue retrieving your transaction details. Please try again.", | ||
| ); | ||
| }); | ||
|
Comment on lines
+61
to
+65
|
||
|
|
||
| it("returns known error message for string errors with classic assets", () => { | ||
| const result = getSwapErrorMessage( | ||
| ERROR_TO_DISPLAY.NO_PATH_FOUND, | ||
| classicAsset, | ||
| classicAsset, | ||
| ); | ||
| expect(result).toBe(ERROR_TO_DISPLAY.NO_PATH_FOUND); | ||
| }); | ||
|
|
||
| it("returns unknown error for unrecognized string with classic assets", () => { | ||
| const result = getSwapErrorMessage( | ||
| "something unexpected", | ||
| classicAsset, | ||
| classicAsset, | ||
| ); | ||
| expect(result).toBe( | ||
| "We had an issue retrieving your transaction details. Please try again.", | ||
| ); | ||
| }); | ||
|
|
||
| it("returns unknown error for non-Error non-string with classic assets", () => { | ||
| const result = getSwapErrorMessage(42, classicAsset, classicAsset); | ||
| expect(result).toBe( | ||
| "We had an issue retrieving your transaction details. Please try again.", | ||
| ); | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These stubs are added after
stubAllExternalApis, which already registers route handlers for both**/asset?search**and**/account-balances/**. Without callingpage.unroute(...)first, these overrides may not take effect (other tests override default stubs by unrouting first). Unroute the existing handlers before registeringstubAssetSearchWithContractId/stubAccountBalancesE2eto avoid flaky or failing expectations.