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() {
}>
- Mount Checkout Widgets
+ rc={}>
+ Mount Commerce Widget: Flow.Connect
+
+ }>
+ Mount Commerce Widget: Flow.Wallet
+
+ }>
+ Mount Commerce Widget: Flow.Bridge
+
+ }>
+ Mount Commerce Widget: Flow.AddFunds
+
+ }>
+ Mount Commerce Widget: Flow.OnRamp
+
+ }>
+ Mount Commerce Widget: Flow.Swap
+
+ }>
+ Mount Commerce Widget: Flow.Sale
>);
}
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