diff --git a/tests/func-tests/checkout/widgets-nextjs/package.json b/tests/func-tests/checkout/widgets-nextjs/package.json index 3d5b362d8e..4d1709a706 100644 --- a/tests/func-tests/checkout/widgets-nextjs/package.json +++ b/tests/func-tests/checkout/widgets-nextjs/package.json @@ -7,14 +7,15 @@ "build": "next build", "start": "next start", "lint": "next lint", - "func-test:ci": "playwright test", + "test": "playwright test", + "func-test:ci": "yarn test", "test:ui": "playwright test --ui", "test:remotewidgets": "USE_REMOTE_WIDGETS=true playwright test" }, "dependencies": { "@biom3/react": "^0.25.21", "@ethersproject/providers": "^5.7.2", - "@imtbl/sdk": "1.61.0", + "@imtbl/sdk": "1.63.0", "next": "14.2.7", "react": "^18", "react-dom": "^18" diff --git a/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-add-tokens/page.tsx b/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-add-tokens/page.tsx new file mode 100644 index 0000000000..9774185e1e --- /dev/null +++ b/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-add-tokens/page.tsx @@ -0,0 +1,75 @@ +"use client"; +import { Box } from '@biom3/react'; +import { checkout } from '@imtbl/sdk'; +import { CommerceFlowType, ConnectionSuccess } from '@imtbl/sdk/checkout'; +import { useEffect } from 'react'; +import { useCommerceWidget } from '../../hooks/useCommerceWidget'; + +function CommerceAddTokens() { + + const { widget } = useCommerceWidget(); + + + useEffect(() => { + if (!widget) return; + widget.mount("widget-root", { + flow: CommerceFlowType.ADD_TOKENS, + }); + + widget.addListener( + checkout.CommerceEventType.SUCCESS, + (payload: checkout.CommerceSuccessEvent) => { + const { type, data } = payload; + + // capture transaction hash after user adds tokens + if (type === checkout.CommerceSuccessEventType.ADD_TOKENS_SUCCESS) { + const { transactionHash } = data; + console.log('Add Tokens Success: ', transactionHash); + } + } + ); + + // detect when user fails to add tokens + widget.addListener( + checkout.CommerceEventType.FAILURE, + (payload: checkout.CommerceFailureEvent) => { + const { type, data } = payload; + + if (type === checkout.CommerceFailureEventType.ADD_TOKENS_FAILED) { + console.log('failed to add tokens', data.reason); + } + } + ); + + // remove widget from view when closed + widget.addListener(checkout.CommerceEventType.CLOSE, () => { + widget.unmount(); + }); + + // clean up event listeners + return () => { + widget.removeListener(checkout.CommerceEventType.SUCCESS); + widget.removeListener(checkout.CommerceEventType.FAILURE); + widget.removeListener(checkout.CommerceEventType.CLOSE); + }; + + + }, [widget]); + + + return ( +
+ +
+ ) +} + +export default CommerceAddTokens; diff --git a/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-bridge/page.tsx b/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-bridge/page.tsx new file mode 100644 index 0000000000..e9f6e211ed --- /dev/null +++ b/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-bridge/page.tsx @@ -0,0 +1,75 @@ +"use client"; +import { Box } from '@biom3/react'; +import { checkout } from '@imtbl/sdk'; +import { CommerceFlowType } from '@imtbl/sdk/checkout'; +import { useEffect } from 'react'; +import { useCommerceWidget } from '../../hooks/useCommerceWidget'; + + +function CommerceBridge() { + + const { widget } = useCommerceWidget(); + + + useEffect(() => { + if (!widget) return; + widget.mount("widget-root", { + flow: CommerceFlowType.BRIDGE, + }); + + widget.addListener( + checkout.CommerceEventType.SUCCESS, + (payload: checkout.CommerceSuccessEvent) => { + const { type, data } = payload; + + // capture provider after user executes bridge + if (type === checkout.CommerceSuccessEventType.BRIDGE_SUCCESS) { + console.log('bridge success', data.transactionHash); + } + } + ); + + // detect when user fails to execute bridge + widget.addListener( + checkout.CommerceEventType.FAILURE, + (payload: checkout.CommerceFailureEvent) => { + const { type, data } = payload; + + if (type === checkout.CommerceFailureEventType.BRIDGE_FAILED) { + console.log('failed to execute bridge', data.reason); + } + } + ); + + // remove widget from view when closed + widget.addListener(checkout.CommerceEventType.CLOSE, () => { + widget.unmount(); + }); + + // clean up event listeners + return () => { + widget.removeListener(checkout.CommerceEventType.SUCCESS); + widget.removeListener(checkout.CommerceEventType.FAILURE); + widget.removeListener(checkout.CommerceEventType.CLOSE); + }; + + + }, [widget]); + + + return ( +
+ +
+ ) +} + +export default CommerceBridge; diff --git a/tests/func-tests/checkout/widgets-nextjs/src/app/widgets/page.tsx b/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-connect/page.tsx similarity index 66% rename from tests/func-tests/checkout/widgets-nextjs/src/app/widgets/page.tsx rename to tests/func-tests/checkout/widgets-nextjs/src/app/commerce-connect/page.tsx index fb3b674758..7510a8aac5 100644 --- a/tests/func-tests/checkout/widgets-nextjs/src/app/widgets/page.tsx +++ b/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-connect/page.tsx @@ -1,32 +1,19 @@ "use client"; import { Box } from '@biom3/react'; import { checkout } from '@imtbl/sdk'; -import { CommerceFlowType, ConnectionSuccess, Widget, WidgetType } from '@imtbl/sdk/checkout'; -import { useEffect, useState } from 'react'; +import { CommerceFlowType, ConnectionSuccess } from '@imtbl/sdk/checkout'; +import { useEffect } from 'react'; +import { useCommerceWidget } from '../../hooks/useCommerceWidget'; -const checkoutSDK = new checkout.Checkout(); +function CommerceConnect() { -function Widgets() { - - const [widget, setWidget] = useState>(); - - useEffect(() => { - - const loadWidgets = async () => { - const widgetsFactory = await checkoutSDK.widgets({ config: {} }); - - const widget = widgetsFactory.create(WidgetType.IMMUTABLE_COMMERCE, {}) - setWidget(widget); - } - - loadWidgets(); - }, []); + const { widget } = useCommerceWidget(); useEffect(() => { if (!widget) return; widget.mount("widget-root", { - flow: CommerceFlowType.WALLET, + flow: CommerceFlowType.CONNECT, }); widget.addListener( @@ -38,10 +25,6 @@ function Widgets() { if (type === checkout.CommerceSuccessEventType.CONNECT_SUCCESS) { const { walletProviderName } = data as ConnectionSuccess; console.log('connected to ', walletProviderName); - // setProvider(data.provider); - - // optional, immediately close the widget - // widget.unmount(); } } ); @@ -66,7 +49,7 @@ function Widgets() { // clean up event listeners return () => { widget.removeListener(checkout.CommerceEventType.SUCCESS); - widget.removeListener(checkout.CommerceEventType.DISCONNECTED); + widget.removeListener(checkout.CommerceEventType.FAILURE); widget.removeListener(checkout.CommerceEventType.CLOSE); }; @@ -89,4 +72,4 @@ function Widgets() { ) } -export default Widgets; +export default CommerceConnect; diff --git a/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-onramp/page.tsx b/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-onramp/page.tsx new file mode 100644 index 0000000000..afd3c292e7 --- /dev/null +++ b/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-onramp/page.tsx @@ -0,0 +1,79 @@ +"use client"; +import { Box } from '@biom3/react'; +import { Web3Provider } from '@ethersproject/providers'; +import { checkout } from '@imtbl/sdk'; +import { CommerceFlowType } from '@imtbl/sdk/checkout'; +import { useEffect, useMemo } from 'react'; +import { useCommerceWidget } from '../../hooks/useCommerceWidget'; +import { MockProvider } from '../utils/mockProvider'; + +function CommerceOnRamp() { + const { widget, factory } = useCommerceWidget(); + const provider = useMemo(() => new Web3Provider(new MockProvider()), []); + + useEffect(() => { + if (!widget || !factory) return; + + factory.updateProvider(provider); + + widget.mount("widget-root", { + flow: CommerceFlowType.ONRAMP, + }); + + widget.addListener( + checkout.CommerceEventType.SUCCESS, + (payload: checkout.CommerceSuccessEvent) => { + const { type, data } = payload; + + // capture provider after user onramp + if (type === checkout.CommerceSuccessEventType.ONRAMP_SUCCESS) { + const { transactionHash } = data; + console.log('onramp success', transactionHash); + } + } + ); + + // detect when user fails to onramp + widget.addListener( + checkout.CommerceEventType.FAILURE, + (payload: checkout.CommerceFailureEvent) => { + const { type, data } = payload; + + if (type === checkout.CommerceFailureEventType.ONRAMP_FAILED) { + console.log('failed to onramp', data.reason); + } + } + ); + + // remove widget from view when closed + widget.addListener(checkout.CommerceEventType.CLOSE, () => { + widget.unmount(); + }); + + // clean up event listeners + return () => { + widget.removeListener(checkout.CommerceEventType.SUCCESS); + widget.removeListener(checkout.CommerceEventType.FAILURE); + widget.removeListener(checkout.CommerceEventType.CLOSE); + }; + + + }, [widget, factory, provider]); + + + return ( +
+ +
+ ) +} + +export default CommerceOnRamp; diff --git a/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-sale/page.tsx b/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-sale/page.tsx new file mode 100644 index 0000000000..4c660b7c64 --- /dev/null +++ b/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-sale/page.tsx @@ -0,0 +1,79 @@ +"use client"; +import { Box } from '@biom3/react'; +import { Web3Provider } from '@ethersproject/providers'; +import { checkout } from '@imtbl/sdk'; +import { CommerceFlowType } from '@imtbl/sdk/checkout'; +import { useEffect, useMemo } from 'react'; +import { useCommerceWidget } from '../../hooks/useCommerceWidget'; +import { MockProvider } from '../utils/mockProvider'; + +function CommerceSale() { + const { widget, factory } = useCommerceWidget(); + const provider = useMemo(() => new Web3Provider(new MockProvider()), []); + + useEffect(() => { + if (!widget || !factory) return; + + factory.updateProvider(provider); + + widget.mount("widget-root", { + flow: CommerceFlowType.SALE, + }); + + widget.addListener( + checkout.CommerceEventType.SUCCESS, + (payload: checkout.CommerceSuccessEvent) => { + const { type, data } = payload; + + // capture provider after user completes sale + if (type === checkout.CommerceSuccessEventType.SALE_SUCCESS) { + const { transactionHash } = data; + console.log('sale success', transactionHash); + } + } + ); + + // detect when user fails to complete sale + widget.addListener( + checkout.CommerceEventType.FAILURE, + (payload: checkout.CommerceFailureEvent) => { + const { type, data } = payload; + + if (type === checkout.CommerceFailureEventType.SALE_FAILED) { + console.log('failed to sale', data.reason); + } + } + ); + + // remove widget from view when closed + widget.addListener(checkout.CommerceEventType.CLOSE, () => { + widget.unmount(); + }); + + // clean up event listeners + return () => { + widget.removeListener(checkout.CommerceEventType.SUCCESS); + widget.removeListener(checkout.CommerceEventType.FAILURE); + widget.removeListener(checkout.CommerceEventType.CLOSE); + }; + + + }, [widget, factory, provider]); + + + return ( +
+ +
+ ) +} + +export default CommerceSale; diff --git a/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-swap/page.tsx b/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-swap/page.tsx new file mode 100644 index 0000000000..7b1ce3709e --- /dev/null +++ b/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-swap/page.tsx @@ -0,0 +1,79 @@ +"use client"; +import { Box } from '@biom3/react'; +import { Web3Provider } from '@ethersproject/providers'; +import { checkout } from '@imtbl/sdk'; +import { CommerceFlowType } from '@imtbl/sdk/checkout'; +import { useEffect, useMemo } from 'react'; +import { useCommerceWidget } from '../../hooks/useCommerceWidget'; +import { MockProvider } from '../utils/mockProvider'; + +function CommerceSwap() { + const { widget, factory } = useCommerceWidget(); + const provider = useMemo(() => new Web3Provider(new MockProvider()), []); + + useEffect(() => { + if (!widget || !factory) return; + + factory.updateProvider(provider); + + widget.mount("widget-root", { + flow: CommerceFlowType.SWAP, + }); + + widget.addListener( + checkout.CommerceEventType.SUCCESS, + (payload: checkout.CommerceSuccessEvent) => { + const { type, data } = payload; + + // capture provider after user swaps + if (type === checkout.CommerceSuccessEventType.SWAP_SUCCESS) { + const { transactionHash } = data; + console.log('swap success', transactionHash); + } + } + ); + + // detect when user fails to swap + widget.addListener( + checkout.CommerceEventType.FAILURE, + (payload: checkout.CommerceFailureEvent) => { + const { type, data } = payload; + + if (type === checkout.CommerceFailureEventType.SWAP_FAILED) { + console.log('failed to swap', data.reason); + } + } + ); + + // remove widget from view when closed + widget.addListener(checkout.CommerceEventType.CLOSE, () => { + widget.unmount(); + }); + + // clean up event listeners + return () => { + widget.removeListener(checkout.CommerceEventType.SUCCESS); + widget.removeListener(checkout.CommerceEventType.FAILURE); + widget.removeListener(checkout.CommerceEventType.CLOSE); + }; + + + }, [widget, factory, provider]); + + + return ( +
+ +
+ ) +} + +export default CommerceSwap; diff --git a/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-wallet/page.tsx b/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-wallet/page.tsx new file mode 100644 index 0000000000..7edc65cc2b --- /dev/null +++ b/tests/func-tests/checkout/widgets-nextjs/src/app/commerce-wallet/page.tsx @@ -0,0 +1,82 @@ +"use client"; +import { Box } from '@biom3/react'; +import { checkout } from '@imtbl/sdk'; +import { CommerceFlowType, ConnectionSuccess } from '@imtbl/sdk/checkout'; +import { useEffect, useMemo } from 'react'; +import { useCommerceWidget } from '../../hooks/useCommerceWidget'; +import { Web3Provider } from '@ethersproject/providers'; +import { MockProvider } from '../utils/mockProvider'; + + + + +function CommerceWallet() { + const { widget, factory } = useCommerceWidget(); + const provider = useMemo(() => new Web3Provider(new MockProvider()), []); + + useEffect(() => { + if (!widget || !factory) return; + + factory.updateProvider(provider); + + widget.mount("widget-root", { + flow: CommerceFlowType.WALLET, + }); + + widget.addListener( + checkout.CommerceEventType.SUCCESS, + (payload: checkout.CommerceSuccessEvent) => { + const { type, data } = payload; + + // capture provider after user connects their wallet + if (type === checkout.CommerceSuccessEventType.CONNECT_SUCCESS) { + const { walletProviderName } = data as ConnectionSuccess; + console.log('connected to ', walletProviderName); + } + } + ); + + // detect when user fails to connect + widget.addListener( + checkout.CommerceEventType.FAILURE, + (payload: checkout.CommerceFailureEvent) => { + const { type, data } = payload; + + if (type === checkout.CommerceFailureEventType.CONNECT_FAILED) { + console.log('failed to connect', data.reason); + } + } + ); + + // remove widget from view when closed + widget.addListener(checkout.CommerceEventType.CLOSE, () => { + widget.unmount(); + }); + + // clean up event listeners + return () => { + widget.removeListener(checkout.CommerceEventType.SUCCESS); + widget.removeListener(checkout.CommerceEventType.FAILURE); + widget.removeListener(checkout.CommerceEventType.CLOSE); + }; + + + }, [widget, factory, provider]); + + + return ( +
+ +
+ ) +} + +export default CommerceWallet; diff --git a/tests/func-tests/checkout/widgets-nextjs/src/app/page.tsx b/tests/func-tests/checkout/widgets-nextjs/src/app/page.tsx index fa3b5ad652..32ac85b730 100644 --- a/tests/func-tests/checkout/widgets-nextjs/src/app/page.tsx +++ b/tests/func-tests/checkout/widgets-nextjs/src/app/page.tsx @@ -13,8 +13,44 @@ export default function Home() { + + + + + + ); } diff --git a/tests/func-tests/checkout/widgets-nextjs/src/app/test/commerce-wallet/page.tsx b/tests/func-tests/checkout/widgets-nextjs/src/app/test/commerce-wallet/page.tsx new file mode 100644 index 0000000000..2593ee92c9 --- /dev/null +++ b/tests/func-tests/checkout/widgets-nextjs/src/app/test/commerce-wallet/page.tsx @@ -0,0 +1,15 @@ +"use client"; +import CommerceWallet from '../../commerce-wallet/page'; +import { MockProvider } from '../../utils/mockProvider'; +import { ProviderContextProvider } from '../../../contexts/ProviderContext'; +import { Web3Provider } from '@ethersproject/providers'; + +export default function TestCommerceWallet() { + const mockProvider = new MockProvider(); + + return ( + + + + ); +} \ No newline at end of file diff --git a/tests/func-tests/checkout/widgets-nextjs/src/app/utils/mockProvider.ts b/tests/func-tests/checkout/widgets-nextjs/src/app/utils/mockProvider.ts new file mode 100644 index 0000000000..ef732f0863 --- /dev/null +++ b/tests/func-tests/checkout/widgets-nextjs/src/app/utils/mockProvider.ts @@ -0,0 +1,100 @@ +import { checkout } from '@imtbl/sdk'; +import { EventEmitter } from "events"; + +export class MockProvider extends EventEmitter { + private connected: boolean = true; + private chainId: string | checkout.ChainId = checkout.ChainId.IMTBL_ZKEVM_TESTNET; + private accounts: string[] = []; + + constructor( + chainId: string | checkout.ChainId = checkout.ChainId.IMTBL_ZKEVM_TESTNET, + accounts: string[] = ["0x1234567890123456789012345678901234567890"] + ) { + super(); + this.chainId = chainId; + this.accounts = accounts; + } + + async request({ + method, + params, + }: { + method: string; + params?: any[]; + }): Promise { + switch (method) { + case "eth_chainId": + return this.chainId; + + case "eth_accounts": + return this.accounts; + + case "eth_requestAccounts": + if (!this.connected) { + throw new Error("User rejected the request."); + } + return this.accounts; + + case "eth_getBalance": + return "0x1000000000000000000"; + + case "net_version": + return this.chainId + + case "eth_blockNumber": + return "0x1000000"; + + case "eth_getCode": + return "0x"; + + case "eth_call": + return "0x"; + + case "eth_estimateGas": + return "0x0"; + + case "eth_gasPrice": + return "0x0"; + + case "eth_sendTransaction": + return "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234"; + + default: + console.log( + `[MockWeb3Provider] Unmocked method called: ${method}`, + params + ); + return null; + } + } + + on(eventName: string, listener: (...args: any[]) => void): this { + super.on(eventName, listener); + return this; + } + + removeListener(eventName: string, listener: (...args: any[]) => void): this { + super.removeListener(eventName, listener); + return this; + } + + public async simulateAccountsChanged(accounts: string[]) { + this.accounts = accounts; + this.emit("accountsChanged", accounts); + } + + public async simulateChainChanged(chainId: string | checkout.ChainId) { + this.chainId = chainId; + this.emit("chainChanged", chainId); + } + + public async simulateConnect() { + this.connected = true; + this.emit("connect", { chainId: this.chainId }); + } + + public async simulateDisconnect() { + this.connected = false; + this.emit("disconnect"); + } +} diff --git a/tests/func-tests/checkout/widgets-nextjs/src/contexts/ProviderContext.tsx b/tests/func-tests/checkout/widgets-nextjs/src/contexts/ProviderContext.tsx new file mode 100644 index 0000000000..23d9fe8ae7 --- /dev/null +++ b/tests/func-tests/checkout/widgets-nextjs/src/contexts/ProviderContext.tsx @@ -0,0 +1,25 @@ +import { createContext, useContext, ReactNode } from 'react'; + +interface ProviderContextType { + provider: any | undefined; +} + +const ProviderContext = createContext({ provider: undefined }); + +export function ProviderContextProvider({ + children, + provider +}: { + children: ReactNode; + provider: any; +}) { + return ( + + {children} + + ); +} + +export function useProvider() { + return useContext(ProviderContext).provider; +} \ No newline at end of file diff --git a/tests/func-tests/checkout/widgets-nextjs/src/hooks/useCommerceWidget.ts b/tests/func-tests/checkout/widgets-nextjs/src/hooks/useCommerceWidget.ts new file mode 100644 index 0000000000..9339978322 --- /dev/null +++ b/tests/func-tests/checkout/widgets-nextjs/src/hooks/useCommerceWidget.ts @@ -0,0 +1,31 @@ +import { Web3Provider } from '@ethersproject/providers'; +import { checkout } from '@imtbl/sdk'; +import { Widget, WidgetType } from '@imtbl/sdk/checkout'; +import { useState, useEffect, useMemo } from 'react'; +import { MockProvider } from '../app/utils/mockProvider'; + +const checkoutSDK = new checkout.Checkout(); + +export function useCommerceWidget() { + const [widget, setWidget] = useState>(); + const [factory, setFactory] = useState(); + + + useEffect(() => { + const loadWidgets = async () => { + const widgetsFactory = await checkoutSDK.widgets({ + config: {}, + }); + + const widget = widgetsFactory.create(WidgetType.IMMUTABLE_COMMERCE, {}) + setWidget(widget); + setFactory(widgetsFactory); + } + + loadWidgets(); + }, []); + + + + return { widget, factory }; +} \ No newline at end of file diff --git a/tests/func-tests/checkout/widgets-nextjs/tests/commerceWidget.spec.ts b/tests/func-tests/checkout/widgets-nextjs/tests/commerceWidget.spec.ts new file mode 100644 index 0000000000..081f004441 --- /dev/null +++ b/tests/func-tests/checkout/widgets-nextjs/tests/commerceWidget.spec.ts @@ -0,0 +1,180 @@ +import { test, expect } from "@playwright/test"; +import { interceptWidgets } from "./utils/interceptWidgets"; +import { + CheckoutVersionConfig, + interceptCheckoutVersionConfig, +} from "./utils/interceptCheckoutConfig"; + +const USE_REMOTE_WIDGETS = process.env.USE_REMOTE_WIDGETS === "true"; + +const INTERCEPT_CHECKOUT_VERSION_CONFIG = + process.env.INTERCEPT_CHECKOUT_VERSION_CONFIG; + +test.describe.configure({ mode: 'parallel' }); + +test.beforeEach(async ({ page }) => { + if (!USE_REMOTE_WIDGETS) { + await interceptWidgets(page); + } + + if (INTERCEPT_CHECKOUT_VERSION_CONFIG) { + const checkoutWidgetsVersion: CheckoutVersionConfig = { + compatibleVersionMarkers: [INTERCEPT_CHECKOUT_VERSION_CONFIG], + }; + await interceptCheckoutVersionConfig(page, checkoutWidgetsVersion); + } +}); + +test.describe("widget mounting - connect flow", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/commerce-connect"); + }); + + test("should render connect widget and handle close", async ({ page }) => { + await page.waitForSelector("#widget-root"); + + const widgetRoot = page.locator("#widget-root"); + await expect(widgetRoot).not.toBeEmpty(); + + const connectWidget = page.getByTestId("connect-wallet"); + await expect(connectWidget).toBeVisible(); + + const closeButton = page.getByTestId("close-button"); + await expect(closeButton).toBeVisible(); + await closeButton.click(); + + await expect(widgetRoot).toBeEmpty(); + }); +}); + +test.describe("widget mounting - bridge flow", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/commerce-bridge"); + }); + + test("should render bridge widget and handle close", async ({ page }) => { + await page.waitForSelector("#widget-root"); + + const widgetRoot = page.locator("#widget-root"); + await expect(widgetRoot).not.toBeEmpty(); + + const connectWidget = page.getByTestId("bridge-view"); + await expect(connectWidget).toBeVisible(); + + const closeButton = page.getByTestId("close-button"); + await expect(closeButton).toBeVisible(); + await closeButton.click(); + + await expect(widgetRoot).toBeEmpty(); + }); +}); + +test.describe("widget mounting - wallet flow", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/commerce-wallet"); + }); + + test("should render connect widget and handle close", async ({ page }) => { + await page.waitForSelector("#widget-root"); + + const widgetRoot = page.locator("#widget-root"); + await expect(widgetRoot).not.toBeEmpty(); + + const walletWidget = page.getByTestId("wallet-balances"); + await expect(walletWidget).toBeVisible(); + + const closeButton = page.getByTestId("close-button"); + await expect(closeButton).toBeVisible(); + await closeButton.click(); + }); +}); + +/** + * Add Tokens is disabled in Sandbox, it will render a handover screen with error text. + */ +test.describe("widget mounting - add tokens flow", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/commerce-add-tokens"); + }); + + test("should render add tokens widget", async ({ page }) => { + await page.waitForSelector("#widget-root"); + const widgetRoot = page.locator("#widget-root"); + await expect(widgetRoot).not.toBeEmpty(); + + const closeButton = page.getByTestId("handover-secondary-button"); + await expect(closeButton).toBeVisible(); + + // Add Tokens events are not currently supported in Commerce + // await closeButton.click(); + + // await expect(widgetRoot).toBeEmpty(); + }); +}); + + + +// Without inputs Sale will render a handover screen with error text. +test.describe("widget mounting - sale flow", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/commerce-sale"); + }); + + test("should render sale widget and handle close", async ({ page }) => { + await page.waitForSelector("#widget-root"); + const widgetRoot = page.locator("#widget-root"); + await expect(widgetRoot).not.toBeEmpty(); + + const closeButton = page.getByTestId("handover-secondary-button"); + await expect(closeButton).toBeVisible(); + await closeButton.click(); + + await expect(widgetRoot).toBeEmpty(); + }); +}); + +test.describe("widget mounting - swap flow", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/commerce-swap"); + }); + + test("should render sale widget and handle close", async ({ page }) => { + await page.waitForSelector("#widget-root"); + const widgetRoot = page.locator("#widget-root"); + await expect(widgetRoot).not.toBeEmpty(); + + + // If the user has insufficient IMX, they will be shown a bottom sheet with a cancel button. + const notEnoughGasBottomSheet = page.getByTestId("not-enough-gas-bottom-sheet"); + await expect(notEnoughGasBottomSheet).toBeVisible(); + if (await notEnoughGasBottomSheet.isVisible()) { + const notEnoughGasCancelButton = page.getByTestId("not-enough-gas-cancel-button"); + await notEnoughGasCancelButton.click(); + } + + const swapButton = page.getByTestId("swap-button"); + await expect(swapButton).toBeVisible(); + + const closeButton = page.getByTestId("close-button"); + await expect(closeButton).toBeVisible(); + await closeButton.click(); + + await expect(widgetRoot).toBeEmpty(); + }); +}); + +test.describe("widget mounting - onramp flow", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/commerce-onramp"); + }); + + test("should render onramp widget", async ({ page }) => { + await page.waitForSelector("#widget-root"); + const widgetRoot = page.locator("#widget-root"); + await expect(widgetRoot).not.toBeEmpty(); + + const transakLoadingOverlay = page.getByTestId("LoadingOverlayContent__loopingText__text--Taking you to Transak__animatingSpan"); + await expect(transakLoadingOverlay).toBeVisible(); + + }); +}); \ No newline at end of file diff --git a/tests/func-tests/checkout/widgets-nextjs/tests/utils/intercept-checkout-config.ts b/tests/func-tests/checkout/widgets-nextjs/tests/utils/interceptCheckoutConfig.ts similarity index 100% rename from tests/func-tests/checkout/widgets-nextjs/tests/utils/intercept-checkout-config.ts rename to tests/func-tests/checkout/widgets-nextjs/tests/utils/interceptCheckoutConfig.ts diff --git a/tests/func-tests/checkout/widgets-nextjs/tests/utils/intercept-widgets.ts b/tests/func-tests/checkout/widgets-nextjs/tests/utils/interceptWidgets.ts similarity index 100% rename from tests/func-tests/checkout/widgets-nextjs/tests/utils/intercept-widgets.ts rename to tests/func-tests/checkout/widgets-nextjs/tests/utils/interceptWidgets.ts diff --git a/tests/func-tests/checkout/widgets-nextjs/tests/version.spec.ts b/tests/func-tests/checkout/widgets-nextjs/tests/version.spec.ts deleted file mode 100644 index 37f09d7ca1..0000000000 --- a/tests/func-tests/checkout/widgets-nextjs/tests/version.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { test, expect } from "@playwright/test"; -import { interceptWidgets } from "./utils/intercept-widgets"; -import { CheckoutVersionConfig, interceptCheckoutVersionConfig } from "./utils/intercept-checkout-config"; - -const USE_REMOTE_WIDGETS = process.env.USE_REMOTE_WIDGETS === 'true'; - -const INTERCEPT_CHECKOUT_VERSION_CONFIG = process.env.INTERCEPT_CHECKOUT_VERSION_CONFIG; - - - -test.beforeEach(async ({ page }) => { - - if (!USE_REMOTE_WIDGETS) { - await interceptWidgets(page); - } - - if (INTERCEPT_CHECKOUT_VERSION_CONFIG) { - const checkoutWidgetsVersion: CheckoutVersionConfig = { - compatibleVersionMarkers: [INTERCEPT_CHECKOUT_VERSION_CONFIG] - }; - await interceptCheckoutVersionConfig(page, checkoutWidgetsVersion); - } - - await page.goto("/widgets"); -}); - -test.describe("widget loading", () => { - test("loads widgets into root", async ({ page }) => { - // Wait for the widget-root element to be visible - await page.waitForSelector('#widget-root'); - - // Check if the widget-root element contains child elements - const widgetRoot = page.locator('#widget-root'); - await expect(widgetRoot).not.toBeEmpty(); - - const connectWidget = page.getByTestId('connect-wallet'); - await expect(connectWidget).toBeVisible(); - }); -}); diff --git a/yarn.lock b/yarn.lock index 278a1a3c1f..4923718735 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5039,9 +5039,9 @@ __metadata: languageName: unknown linkType: soft -"@imtbl/sdk@npm:1.61.0": - version: 1.61.0 - resolution: "@imtbl/sdk@npm:1.61.0" +"@imtbl/sdk@npm:1.63.0": + version: 1.63.0 + resolution: "@imtbl/sdk@npm:1.63.0" dependencies: "@0xsequence/abi": ^1.4.3 "@0xsequence/core": ^1.4.3 @@ -5114,7 +5114,7 @@ __metadata: optional: true prisma: optional: true - checksum: 93f3c3198842d5b4d7a44eb2b20091697c1dbc64fcc3e677fd7e8fa77c456611cb189aab874a8b8d4e8df27e83f189affbdfe40291e810f423d8f818da72b0c7 + checksum: 7c1695455fd060d38b145ce6539142a31c569bcfe5cea399f2bf68832439fcb4617473633df6c3e96ab9e99259ac8ef3830b13af4bf5a66c48c724d8f369c1a2 languageName: node linkType: hard @@ -12253,7 +12253,7 @@ __metadata: dependencies: "@biom3/react": ^0.25.21 "@ethersproject/providers": ^5.7.2 - "@imtbl/sdk": 1.61.0 + "@imtbl/sdk": 1.63.0 "@playwright/test": ^1.45.3 "@types/node": ^20 "@types/react": ^18.3.4