From 4322e8f8eb72e0396e35ebfe007ec6928dd24e44 Mon Sep 17 00:00:00 2001 From: Nadai2010 Date: Thu, 10 Oct 2024 10:51:31 +0100 Subject: [PATCH] Update Test ch-2 in SC Cairo and small fixed UI refresh data --- packages/nextjs/app/token-vendor/page.tsx | 2 + .../__tests__/useAutoConnect.test.ts | 104 --- .../__tests__/useDeployedContractInfo.test.ts | 638 ------------------ .../__tests__/useOutsideClick.test.ts | 64 -- .../__tests__/useScaffoldContract.test.ts | 112 --- .../useScaffoldMultiWriteContract.test.ts | 243 ------- .../__tests__/useScaffoldReadContract.test.ts | 103 --- packages/snfoundry/contracts/Scarb.toml | 1 + .../contracts/src/test/TestContract.cairo | 14 +- 9 files changed, 11 insertions(+), 1270 deletions(-) delete mode 100644 packages/nextjs/hooks/scaffold-stark/__tests__/useAutoConnect.test.ts delete mode 100644 packages/nextjs/hooks/scaffold-stark/__tests__/useDeployedContractInfo.test.ts delete mode 100644 packages/nextjs/hooks/scaffold-stark/__tests__/useOutsideClick.test.ts delete mode 100644 packages/nextjs/hooks/scaffold-stark/__tests__/useScaffoldContract.test.ts delete mode 100644 packages/nextjs/hooks/scaffold-stark/__tests__/useScaffoldMultiWriteContract.test.ts delete mode 100644 packages/nextjs/hooks/scaffold-stark/__tests__/useScaffoldReadContract.test.ts diff --git a/packages/nextjs/app/token-vendor/page.tsx b/packages/nextjs/app/token-vendor/page.tsx index 0ef1108e..61f46c9e 100644 --- a/packages/nextjs/app/token-vendor/page.tsx +++ b/packages/nextjs/app/token-vendor/page.tsx @@ -26,6 +26,7 @@ const TokenVendor: NextPage = () => { const { data: yourTokenSymbol } = useScaffoldReadContract({ contractName: "YourToken", functionName: "symbol", + args: [], }); const { data: yourTokenBalance } = useScaffoldReadContract({ @@ -46,6 +47,7 @@ const TokenVendor: NextPage = () => { const { data: tokensPerEth } = useScaffoldReadContract({ contractName: "Vendor", functionName: "tokens_per_eth", + args: [], }); const { sendAsync: transferTokens } = useScaffoldWriteContract({ diff --git a/packages/nextjs/hooks/scaffold-stark/__tests__/useAutoConnect.test.ts b/packages/nextjs/hooks/scaffold-stark/__tests__/useAutoConnect.test.ts deleted file mode 100644 index 244d05ea..00000000 --- a/packages/nextjs/hooks/scaffold-stark/__tests__/useAutoConnect.test.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { renderHook } from "@testing-library/react"; -import { useAutoConnect } from "../useAutoConnect"; -import { useConnect } from "@starknet-react/core"; -import { useReadLocalStorage } from "usehooks-ts"; -import scaffoldConfig from "~~/scaffold.config"; -import { burnerAccounts } from "~~/utils/devnetAccounts"; -import type { BurnerConnector } from "~~/services/web3/stark-burner/BurnerConnector"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; - -// Mock the dependencies -vi.mock("usehooks-ts", () => ({ - useReadLocalStorage: vi.fn(), -})); - -vi.mock("@starknet-react/core", () => ({ - useConnect: vi.fn(), -})); - -vi.mock("~~/scaffold.config", () => ({ - default: { - walletAutoConnect: true, - }, -})); - -vi.mock("~~/utils/devnetAccounts", () => ({ - burnerAccounts: [{ address: "0x123" }, { address: "0x456" }], -})); - -describe("useAutoConnect", () => { - let mockConnect: ReturnType; - let mockConnectors: any[]; - - beforeEach(() => { - mockConnect = vi.fn(); - mockConnectors = [ - { id: "wallet-1" }, - { id: "burner-wallet", burnerAccount: null }, - ]; - (useConnect as ReturnType).mockReturnValue({ - connect: mockConnect, - connectors: mockConnectors, - }); - vi.spyOn(scaffoldConfig, "walletAutoConnect", "get").mockReturnValue(true); - }); - - afterEach(() => { - vi.clearAllMocks(); - }); - - it("should auto-connect if walletAutoConnect is enabled and a saved connector exists", () => { - vi.mocked(useReadLocalStorage).mockReturnValue({ id: "wallet-1" }); - - renderHook(() => useAutoConnect()); - - expect(mockConnect).toHaveBeenCalledWith({ - connector: expect.objectContaining({ id: "wallet-1" }), - }); - }); - - it("should not auto-connect if walletAutoConnect is disabled", () => { - vi.spyOn(scaffoldConfig, "walletAutoConnect", "get").mockReturnValue( - false as true, - ); - vi.mocked(useReadLocalStorage).mockReturnValue({ id: "wallet-1" }); - - renderHook(() => useAutoConnect()); - - expect(mockConnect).not.toHaveBeenCalled(); - }); - - it("should auto-connect to the burner wallet and set burnerAccount if savedConnector exists", () => { - vi.mocked(useReadLocalStorage).mockReturnValue({ - id: "burner-wallet", - ix: 1, - }); - - renderHook(() => useAutoConnect()); - - expect(mockConnect).toHaveBeenCalledWith({ - connector: expect.objectContaining({ - id: "burner-wallet", - burnerAccount: burnerAccounts[1], - }), - }); - }); - - it("should not connect if there is no saved connector", () => { - vi.mocked(useReadLocalStorage).mockReturnValue(null); - - renderHook(() => useAutoConnect()); - - expect(mockConnect).not.toHaveBeenCalled(); - }); - - it("should not connect if saved connector is not found in the connectors list", () => { - vi.mocked(useReadLocalStorage).mockReturnValue({ - id: "non-existent-connector", - }); - - renderHook(() => useAutoConnect()); - - expect(mockConnect).not.toHaveBeenCalled(); - }); -}); diff --git a/packages/nextjs/hooks/scaffold-stark/__tests__/useDeployedContractInfo.test.ts b/packages/nextjs/hooks/scaffold-stark/__tests__/useDeployedContractInfo.test.ts deleted file mode 100644 index 4bbed397..00000000 --- a/packages/nextjs/hooks/scaffold-stark/__tests__/useDeployedContractInfo.test.ts +++ /dev/null @@ -1,638 +0,0 @@ -import { act, renderHook, waitFor } from "@testing-library/react"; -import { beforeEach, describe, expect, it, vi, type Mock } from "vitest"; -import { useDeployedContractInfo } from "../useDeployedContractInfo"; -import { ContractCodeStatus } from "~~/utils/scaffold-stark/contract"; -import { RpcProvider } from "starknet"; -import { useTargetNetwork } from "../useTargetNetwork"; -import { useIsMounted } from "usehooks-ts"; - -const mockGetClassHashAt = vi - .fn() - .mockImplementation(async (): Promise => "0x1234567"); - -// Mock the dependencies -vi.mock("../useTargetNetwork", () => ({ - useTargetNetwork: vi.fn(), -})); - -vi.mock("usehooks-ts", () => ({ - useIsMounted: vi.fn(), -})); - -vi.mock("starknet", () => ({ - RpcProvider: vi.fn().mockImplementation(() => ({ - getClassHashAt: mockGetClassHashAt, - })), -})); - -vi.mock("~~/utils/scaffold-stark/contract", () => ({ - // these are hoisted to the top, so the variable has to re-declared into the test suite - contracts: { - someNetwork: { - YourContract: { - address: - "0x6e0d97cfd6ad07cea15de51b1761b70cc648948fd183ce03cac9e3a1f8c7f26", - abi: [ - { - type: "impl", - name: "YourContractImpl", - interface_name: "contracts::YourContract::IYourContract", - }, - { - type: "struct", - name: "core::byte_array::ByteArray", - members: [ - { - name: "data", - type: "core::array::Array::", - }, - { - name: "pending_word", - type: "core::felt252", - }, - { - name: "pending_word_len", - type: "core::integer::u32", - }, - ], - }, - { - type: "struct", - name: "core::integer::u256", - members: [ - { - name: "low", - type: "core::integer::u128", - }, - { - name: "high", - type: "core::integer::u128", - }, - ], - }, - { - type: "enum", - name: "core::bool", - variants: [ - { - name: "False", - type: "()", - }, - { - name: "True", - type: "()", - }, - ], - }, - { - type: "interface", - name: "contracts::YourContract::IYourContract", - items: [ - { - type: "function", - name: "greeting", - inputs: [], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "set_greeting", - inputs: [ - { - name: "new_greeting", - type: "core::byte_array::ByteArray", - }, - { - name: "amount_eth", - type: "core::integer::u256", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "withdraw", - inputs: [], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "premium", - inputs: [], - outputs: [ - { - type: "core::bool", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "OwnableImpl", - interface_name: "openzeppelin_access::ownable::interface::IOwnable", - }, - { - type: "interface", - name: "openzeppelin_access::ownable::interface::IOwnable", - items: [ - { - type: "function", - name: "owner", - inputs: [], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "transfer_ownership", - inputs: [ - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "renounce_ownership", - inputs: [], - outputs: [], - state_mutability: "external", - }, - ], - }, - { - type: "constructor", - name: "constructor", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - }, - { - type: "event", - name: "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin_access::ownable::ownable::OwnableComponent::Event", - kind: "enum", - variants: [ - { - name: "OwnershipTransferred", - type: "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "nested", - }, - { - name: "OwnershipTransferStarted", - type: "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "nested", - }, - ], - }, - { - type: "event", - name: "contracts::YourContract::YourContract::GreetingChanged", - kind: "struct", - members: [ - { - name: "greeting_setter", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_greeting", - type: "core::byte_array::ByteArray", - kind: "key", - }, - { - name: "premium", - type: "core::bool", - kind: "data", - }, - { - name: "value", - type: "core::integer::u256", - kind: "data", - }, - ], - }, - { - type: "event", - name: "contracts::YourContract::YourContract::Event", - kind: "enum", - variants: [ - { - name: "OwnableEvent", - type: "openzeppelin_access::ownable::ownable::OwnableComponent::Event", - kind: "flat", - }, - { - name: "GreetingChanged", - type: "contracts::YourContract::YourContract::GreetingChanged", - kind: "nested", - }, - ], - }, - ], - classHash: - "0x15981f4687739d007cf4d6ec112dc72f2e46026c1d1e031ec698fb282d43399", - }, - }, - }, - ContractCodeStatus: { - LOADING: "loading", - DEPLOYED: "deployed", - NOT_FOUND: "not_found", - }, -})); - -describe("useDeployedContractInfo", () => { - const mockContracts = { - someNetwork: { - YourContract: { - address: - "0x6e0d97cfd6ad07cea15de51b1761b70cc648948fd183ce03cac9e3a1f8c7f26", - abi: [ - { - type: "impl", - name: "YourContractImpl", - interface_name: "contracts::YourContract::IYourContract", - }, - { - type: "struct", - name: "core::byte_array::ByteArray", - members: [ - { - name: "data", - type: "core::array::Array::", - }, - { - name: "pending_word", - type: "core::felt252", - }, - { - name: "pending_word_len", - type: "core::integer::u32", - }, - ], - }, - { - type: "struct", - name: "core::integer::u256", - members: [ - { - name: "low", - type: "core::integer::u128", - }, - { - name: "high", - type: "core::integer::u128", - }, - ], - }, - { - type: "enum", - name: "core::bool", - variants: [ - { - name: "False", - type: "()", - }, - { - name: "True", - type: "()", - }, - ], - }, - { - type: "interface", - name: "contracts::YourContract::IYourContract", - items: [ - { - type: "function", - name: "greeting", - inputs: [], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "set_greeting", - inputs: [ - { - name: "new_greeting", - type: "core::byte_array::ByteArray", - }, - { - name: "amount_eth", - type: "core::integer::u256", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "withdraw", - inputs: [], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "premium", - inputs: [], - outputs: [ - { - type: "core::bool", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "OwnableImpl", - interface_name: "openzeppelin_access::ownable::interface::IOwnable", - }, - { - type: "interface", - name: "openzeppelin_access::ownable::interface::IOwnable", - items: [ - { - type: "function", - name: "owner", - inputs: [], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "transfer_ownership", - inputs: [ - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "renounce_ownership", - inputs: [], - outputs: [], - state_mutability: "external", - }, - ], - }, - { - type: "constructor", - name: "constructor", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - }, - { - type: "event", - name: "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin_access::ownable::ownable::OwnableComponent::Event", - kind: "enum", - variants: [ - { - name: "OwnershipTransferred", - type: "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "nested", - }, - { - name: "OwnershipTransferStarted", - type: "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "nested", - }, - ], - }, - { - type: "event", - name: "contracts::YourContract::YourContract::GreetingChanged", - kind: "struct", - members: [ - { - name: "greeting_setter", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_greeting", - type: "core::byte_array::ByteArray", - kind: "key", - }, - { - name: "premium", - type: "core::bool", - kind: "data", - }, - { - name: "value", - type: "core::integer::u256", - kind: "data", - }, - ], - }, - { - type: "event", - name: "contracts::YourContract::YourContract::Event", - kind: "enum", - variants: [ - { - name: "OwnableEvent", - type: "openzeppelin_access::ownable::ownable::OwnableComponent::Event", - kind: "flat", - }, - { - name: "GreetingChanged", - type: "contracts::YourContract::YourContract::GreetingChanged", - kind: "nested", - }, - ], - }, - ], - classHash: - "0x15981f4687739d007cf4d6ec112dc72f2e46026c1d1e031ec698fb282d43399", - }, - }, - }; - - const mockIsMounted = vi.fn(); - const mockUseTargetNetwork = { - targetNetwork: { - network: "someNetwork", - rpcUrls: { - public: { - http: ["http://public-node-url"], - }, - }, - }, - }; - - beforeEach(() => { - vi.clearAllMocks(); - (useTargetNetwork as Mock).mockReturnValue(mockUseTargetNetwork); - (useIsMounted as Mock).mockReturnValue(mockIsMounted); - }); - - it("should initially set the status to LOADING", () => { - const { result } = renderHook(() => - //@ts-ignore using ts ignore so wont error in other devices - useDeployedContractInfo("YourContract"), - ); - - expect(result.current.isLoading).toBe(true); - expect(result.current.data).toBeUndefined(); - }); - - it("should set the status to NOT_FOUND if no deployed contract is found", async () => { - mockGetClassHashAt.mockImplementationOnce(async () => undefined); - mockIsMounted.mockReturnValue(true); - - const { result } = renderHook(() => - //@ts-ignore using ts ignore so wont error in other devices - useDeployedContractInfo("SomeContract"), - ); - - // Wait for the hook to update - await waitFor(() => { - expect(result.current.isLoading).toBe(false); - expect(result.current.data).toBeUndefined(); - }); - }); - - it("should set the status to DEPLOYED if the contract is found", async () => { - mockIsMounted.mockReturnValue(true); - - const { result } = renderHook(() => - //@ts-ignore using ts ignore so wont error in other devices - useDeployedContractInfo("YourContract"), - ); - - // Wait for the hook to update - act(async () => { - await waitFor(() => { - expect(result.current.isLoading).toBe(false); - expect(result.current.data?.address).toEqual( - mockContracts.someNetwork.YourContract.address, - ); - }); - }); - }); - - it("should not update status if component is unmounted", async () => { - mockIsMounted.mockReturnValue(false); // Simulate unmount - - const { result } = renderHook(() => - //@ts-ignore using ts ignore so wont error in other devices - useDeployedContractInfo("YourContract"), - ); - - // Wait for the hook to update - act(async () => { - await waitFor(() => { - expect(result.current.isLoading).toBe(true); - expect(result.current.data).toBeUndefined(); // Should not update the data - }); - }); - }); -}); diff --git a/packages/nextjs/hooks/scaffold-stark/__tests__/useOutsideClick.test.ts b/packages/nextjs/hooks/scaffold-stark/__tests__/useOutsideClick.test.ts deleted file mode 100644 index 12bf3489..00000000 --- a/packages/nextjs/hooks/scaffold-stark/__tests__/useOutsideClick.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { renderHook } from "@testing-library/react"; -import { useOutsideClick } from "../useOutsideClick"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; - -describe("useOutsideClick", () => { - let refElement: HTMLElement; - let callback: () => void; - - beforeEach(() => { - // Create a mock div element to be used as the ref - refElement = document.createElement("div"); - document.body.appendChild(refElement); - - // Create a mock callback function - callback = vi.fn(); - }); - - afterEach(() => { - document.body.removeChild(refElement); - vi.clearAllMocks(); - }); - - it("should call the callback when clicking outside the ref element", () => { - const ref = { current: refElement }; - renderHook(() => useOutsideClick(ref, callback)); - - // Simulate a click outside the element - const outsideElement = document.createElement("div"); - document.body.appendChild(outsideElement); - - outsideElement.click(); - - expect(callback).toHaveBeenCalledTimes(1); - - document.body.removeChild(outsideElement); - }); - - it("should not call the callback when clicking inside the ref element", () => { - const ref = { current: refElement }; - renderHook(() => useOutsideClick(ref, callback)); - - // Simulate a click inside the ref element - refElement.click(); - - expect(callback).not.toHaveBeenCalled(); - }); - - it("should remove event listener on unmount", () => { - const ref = { current: refElement }; - const { unmount } = renderHook(() => useOutsideClick(ref, callback)); - - const removeEventListenerSpy = vi.spyOn(document, "removeEventListener"); - - // Unmount the hook - unmount(); - - expect(removeEventListenerSpy).toHaveBeenCalledWith( - "click", - expect.any(Function), - ); - - removeEventListenerSpy.mockRestore(); - }); -}); diff --git a/packages/nextjs/hooks/scaffold-stark/__tests__/useScaffoldContract.test.ts b/packages/nextjs/hooks/scaffold-stark/__tests__/useScaffoldContract.test.ts deleted file mode 100644 index f727337a..00000000 --- a/packages/nextjs/hooks/scaffold-stark/__tests__/useScaffoldContract.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { vi, describe, it, expect, beforeEach } from "vitest"; -import { renderHook } from "@testing-library/react"; -import { useScaffoldContract } from "~~/hooks/scaffold-stark/useScaffoldContract"; -import { useDeployedContractInfo } from "~~/hooks/scaffold-stark/useDeployedContractInfo"; -import { useTargetNetwork } from "~~/hooks/scaffold-stark/useTargetNetwork"; -import { useAccount } from "~~/hooks/useAccount"; -import { Contract, RpcProvider } from "starknet"; - -import type { Mock } from "vitest"; -import { ContractName } from "~~/utils/scaffold-stark/contract"; - -//Using vitest's functionality to mock modules from different paths -vi.mock("~~/hooks/scaffold-stark/useDeployedContractInfo"); -vi.mock("~~/hooks/scaffold-stark/useTargetNetwork"); -vi.mock("~~/hooks/useAccount"); -vi.mock("starknet", () => { - const actualStarknet = vi.importActual("starknet"); - return { - ...actualStarknet, - Contract: vi.fn(), - RpcProvider: vi.fn(), - }; -}); - -describe("useScaffoldContract", () => { - const mockAbi = [ - { type: "function", name: "mockFunction", inputs: [], outputs: [] }, - ]; - const mockAddress = "0x12345"; - const contractName: ContractName = "Strk"; - - //Some necessary mocks - const mockedUseDeployedContractInfo = - useDeployedContractInfo as unknown as Mock; - const mockedUseTargetNetwork = useTargetNetwork as unknown as Mock; - const mockedUseAccount = useAccount as unknown as Mock; - const MockedContract = Contract as unknown as Mock; - const MockedRpcProvider = RpcProvider as unknown as Mock; - - beforeEach(() => { - vi.resetAllMocks(); - - mockedUseDeployedContractInfo.mockReturnValue({ - data: { - abi: mockAbi, - address: mockAddress, - }, - isLoading: false, - }); - - mockedUseTargetNetwork.mockReturnValue({ - targetNetwork: { - rpcUrls: { public: { http: ["https://mock-rpc-url"] } }, - }, - }); - - mockedUseAccount.mockReturnValue({ - account: { - address: "0x129846", - }, - }); - - MockedContract.mockImplementation(() => ({ - address: mockAddress, - abi: mockAbi, - })); - - MockedRpcProvider.mockImplementation(() => ({ - nodeAddress: "https://mock-rpc-url", - })); - }); - - it("should return a contract when deployedContractData is available", () => { - const { result } = renderHook(() => useScaffoldContract({ contractName })); - - expect(result.current.data).toBeDefined(); - expect(result.current.data?.address).toBe(mockAddress); - expect(result.current.data?.abi).toEqual(mockAbi); - }); - - it("should return undefined contract when deployedContractData is not available", () => { - mockedUseDeployedContractInfo.mockReturnValueOnce({ - data: undefined, - isLoading: false, - }); - - const { result } = renderHook(() => useScaffoldContract({ contractName })); - - expect(result.current.data).toBeUndefined(); - expect(result.current.isLoading).toBe(false); - }); - - it("should create RpcProvider with the correct public URL", () => { - renderHook(() => useScaffoldContract({ contractName })); - - expect(MockedRpcProvider).toHaveBeenCalledWith({ - nodeUrl: "https://mock-rpc-url", - }); - }); - - it("should set isLoading to true when deployed contract is loading", () => { - mockedUseDeployedContractInfo.mockReturnValueOnce({ - data: undefined, - isLoading: true, - }); - - const { result } = renderHook(() => useScaffoldContract({ contractName })); - - expect(result.current.isLoading).toBe(true); - expect(result.current.data).toBeUndefined(); - }); -}); diff --git a/packages/nextjs/hooks/scaffold-stark/__tests__/useScaffoldMultiWriteContract.test.ts b/packages/nextjs/hooks/scaffold-stark/__tests__/useScaffoldMultiWriteContract.test.ts deleted file mode 100644 index 6e879807..00000000 --- a/packages/nextjs/hooks/scaffold-stark/__tests__/useScaffoldMultiWriteContract.test.ts +++ /dev/null @@ -1,243 +0,0 @@ -import { renderHook, act } from "@testing-library/react"; - -import { vi, describe, it, expect, beforeEach, type Mock } from "vitest"; - -import { - useScaffoldMultiWriteContract, - createContractCall, -} from "../useScaffoldMultiWriteContract"; - -import { useTargetNetwork } from "../useTargetNetwork"; - -import { useNetwork, useSendTransaction } from "@starknet-react/core"; - -import { useTransactor } from "../useTransactor"; - -import { useDeployedContractInfo } from "~~/hooks/scaffold-stark"; - -import { Contract, RpcProvider } from "starknet"; - -// Mock the external dependencies - -vi.mock("~~/hooks/scaffold-stark/useTargetNetwork", () => ({ - useTargetNetwork: vi.fn(), -})); - -vi.mock("@starknet-react/core", () => ({ - useSendTransaction: vi.fn(), - - useNetwork: vi.fn().mockReturnValue({ chain: { id: 1 } }), -})); - -vi.mock("starknet", () => { - const actualStarknet = vi.importActual("starknet"); - - return { - ...actualStarknet, - - Contract: vi.fn(), - - RpcProvider: vi.fn(), - }; -}); - -vi.mock("../useTransactor"); - -vi.mock("~~/hooks/scaffold-stark", () => ({ - useDeployedContractInfo: vi.fn(), - - useTransactor: vi.fn(), -})); - -const mockSendTransaction = vi.fn(); - -const mockTransactor = vi.fn((fn) => fn()); - -const mockedUseNetwork = useNetwork as Mock; - -const useTargetNetworkMock = useTargetNetwork as Mock; -const useSendTransactionMock = useSendTransaction as Mock; -const useTransactorMock = useTransactor as Mock; -const useDeployedContractInfoMock = useDeployedContractInfo as Mock; -const ContractMock = Contract as Mock; -const useNetworkMock = useNetwork as Mock; - -describe("useScaffoldMultiWriteContract Hook", () => { - const mockAbi = [ - { type: "function", name: "mockFunction", inputs: [], outputs: [] }, - ]; - - const mockAddress = "0x12345"; - - beforeEach(() => { - vi.resetAllMocks(); - - useTargetNetworkMock.mockReturnValue({ - targetNetwork: { network: "testNetwork", id: 1 }, - }); - - mockedUseNetwork.mockReturnValue({ chain: { id: 1 } }); - - useSendTransactionMock.mockReturnValue({ - sendAsync: mockSendTransaction, - }); - - useTransactorMock.mockReturnValue(mockTransactor); - - useDeployedContractInfoMock.mockReturnValue({ - data: { - address: "0x123", - - abi: [{ name: "testFunction" }], - }, - }); - - ContractMock.mockImplementation(() => ({ - address: mockAddress, - - abi: mockAbi, - })); - }); - - it("should correctly parse contract calls", () => { - // Mock contract and ABI - - const { result } = renderHook(() => - useScaffoldMultiWriteContract({ - calls: [ - { contractName: "Strk", functionName: "transfer", args: ["arg1", 1] }, - ], - }), - ); - - expect(result.current.sendAsync).toBeInstanceOf(Function); - - expect(mockSendTransaction).not.toHaveBeenCalled(); - }); - - it("should return error if wallet is not connected", async () => { - useNetworkMock.mockReturnValueOnce({ chain: null }); - - const { result } = renderHook(() => - useScaffoldMultiWriteContract({ - calls: [ - { contractName: "Strk", functionName: "transfer", args: ["arg1", 1] }, - ], - }), - ); - - await act(async () => { - await result.current.sendAsync(); - }); - - vi.spyOn(result.current, "sendAsync").mockRejectedValue( - new Error("Please connect your wallet"), - ); - - await expect(result.current.sendAsync).rejects.toThrowError( - "Please connect your wallet", - ); - }); - - it("should handle wrong network", async () => { - useNetworkMock.mockReturnValueOnce({ chain: { id: 2 } }); - - const { result } = renderHook(() => - useScaffoldMultiWriteContract({ - calls: [ - { contractName: "Strk", functionName: "transfer", args: ["arg1", 1] }, - ], - }), - ); - - await act(async () => { - await result.current.sendAsync(); - }); - - vi.spyOn(result.current, "sendAsync").mockRejectedValue( - new Error("You are on the wrong network"), - ); - - await expect(result.current.sendAsync).rejects.toThrowError( - "You are on the wrong network", - ); - }); - - it("should show error if contract ABI is missing", async () => { - const { result } = renderHook(() => - useScaffoldMultiWriteContract({ - calls: [ - { contractName: "Strk", functionName: "transfer", args: ["arg1", 1] }, - ], - }), - ); - - await act(async () => { - await result.current.sendAsync(); - }); - - vi.spyOn(result.current, "sendAsync").mockRejectedValue( - new Error("Function myFunction not found in contract ABI"), - ); - - await expect(result.current.sendAsync).rejects.toThrowError( - "Function myFunction not found in contract ABI", - ); - }); - - it("should send contract write transaction", async () => { - useTransactorMock.mockReturnValue((fn: any) => fn()); - - const { result } = renderHook(() => - useScaffoldMultiWriteContract({ - calls: [ - { contractName: "Strk", functionName: "transfer", args: ["arg1", 1] }, - ], - }), - ); - - await act(async () => { - await result.current.sendAsync(); - }); - - expect(mockSendTransaction).toHaveBeenCalled(); - }); - - it("should show error notification if sendAsync is not available", async () => { - useSendTransactionMock.mockReturnValueOnce({ sendAsync: null }); - - const { result } = renderHook(() => - useScaffoldMultiWriteContract({ - calls: [ - { contractName: "Strk", functionName: "transfer", args: ["arg1", 1] }, - ], - }), - ); - - await act(async () => { - await result.current.sendAsync(); - }); - - vi.spyOn(result.current, "sendAsync").mockRejectedValue( - new Error("Contract writer error. Try again."), - ); - - await expect(result.current.sendAsync).rejects.toThrowError( - "Contract writer error. Try again.", - ); - }); -}); - -describe("createContractCall Function", () => { - it("should create a contract call object", () => { - const contractCall = createContractCall("Strk", "transfer", ["arg1", 1]); - - expect(contractCall).toEqual({ - contractName: "Strk", - - functionName: "transfer", - - args: ["arg1", 1], - }); - }); -}); diff --git a/packages/nextjs/hooks/scaffold-stark/__tests__/useScaffoldReadContract.test.ts b/packages/nextjs/hooks/scaffold-stark/__tests__/useScaffoldReadContract.test.ts deleted file mode 100644 index 077da37c..00000000 --- a/packages/nextjs/hooks/scaffold-stark/__tests__/useScaffoldReadContract.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { renderHook } from "@testing-library/react"; -import { useScaffoldReadContract } from "../useScaffoldReadContract"; -import { useReadContract } from "@starknet-react/core"; -import { vi, describe, it, expect, Mock } from "vitest"; - -// Mocking dependencies using Vitest -vi.mock("~~/hooks/scaffold-stark", () => ({ - useDeployedContractInfo: vi.fn(() => ({ - data: { - address: "0x123", - abi: [{ name: "symbol" }], - }, - })), -})); - -vi.mock("@starknet-react/core", () => ({ - useReadContract: vi.fn(), -})); - -describe("useScaffoldReadContract", () => { - const contractName = "Eth"; // Using a valid contract name. we could use 'TestContract' here - const functionName = "symbol"; // Using a valid function name. we could actually use 'testFunction' here - - const mockUseReadContract = useReadContract as unknown as Mock; - - it("should call useReadContract with correct parameters when deployedContract is defined", () => { - mockUseReadContract.mockReturnValue({ - data: "mockedData", - }); - - const filteredArgs = [1, undefined, 3].filter((arg) => arg !== undefined); - - renderHook(() => - useScaffoldReadContract({ - contractName, - functionName, - args: filteredArgs, // Pass filtered args - }), - ); - - expect(mockUseReadContract).toHaveBeenCalledWith({ - functionName: "symbol", - address: "0x123", - abi: [{ name: "symbol" }], - watch: true, - args: filteredArgs, - enabled: true, - blockIdentifier: "pending", - }); - }); - - it("should disable read when args contain undefined", () => { - renderHook(() => - useScaffoldReadContract({ - contractName, - functionName, - args: [1, undefined, 3], // args with undefined - }), - ); - - expect(mockUseReadContract).toHaveBeenCalledWith( - expect.objectContaining({ - enabled: false, // The read should be disabled if args contain undefined - }), - ); - }); - - it("should enable read when args do not contain undefined", () => { - const filteredArgs = [1, 2, 3]; - - renderHook(() => - useScaffoldReadContract({ - contractName, - functionName, - args: filteredArgs, - }), - ); - - expect(mockUseReadContract).toHaveBeenCalledWith( - expect.objectContaining({ - enabled: true, // The read should be enabled since args do not contain undefined, args was filtered - }), - ); - }); - - it("should pass blockIdentifier as 'pending'", () => { - const filteredArgs = [1, 2]; - - renderHook(() => - useScaffoldReadContract({ - contractName, - functionName, - args: filteredArgs, - }), - ); - - expect(mockUseReadContract).toHaveBeenCalledWith( - expect.objectContaining({ - blockIdentifier: "pending", // Ensure blockIdentifier is passed as 'pending'. using the default which is 'pending' - }), - ); - }); -}); diff --git a/packages/snfoundry/contracts/Scarb.toml b/packages/snfoundry/contracts/Scarb.toml index a91ea7db..b70f4e59 100644 --- a/packages/snfoundry/contracts/Scarb.toml +++ b/packages/snfoundry/contracts/Scarb.toml @@ -14,6 +14,7 @@ openzeppelin_token = "0.17.0" [dev-dependencies] openzeppelin_utils = "0.17.0" snforge_std = "0.31.0" +assert_macros = "2.8.2" [[target.starknet-contract]] casm = true diff --git a/packages/snfoundry/contracts/src/test/TestContract.cairo b/packages/snfoundry/contracts/src/test/TestContract.cairo index 93eb4eab..2c9540dd 100644 --- a/packages/snfoundry/contracts/src/test/TestContract.cairo +++ b/packages/snfoundry/contracts/src/test/TestContract.cairo @@ -1,10 +1,11 @@ use contracts::Vendor::{IVendorDispatcher, IVendorDispatcherTrait}; use contracts::YourToken::{IYourTokenDispatcher, IYourTokenDispatcherTrait}; use contracts::mock_contracts::MockETHToken; -use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; -use openzeppelin::utils::serde::SerializedAppend; +use openzeppelin_token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; +use openzeppelin_utils::serde::SerializedAppend; use snforge_std::{ - declare, ContractClassTrait, cheat_caller_address, cheat_block_timestamp, CheatSpan + declare, cheat_caller_address, start_cheat_block_timestamp_global, CheatSpan, + DeclareResultTrait, ContractClassTrait, }; use starknet::{ContractAddress, contract_address_const, get_block_timestamp}; @@ -15,9 +16,10 @@ fn RECIPIENT() -> ContractAddress { fn OTHER() -> ContractAddress { contract_address_const::<'OTHER'>() } + // Should deploy the MockETHToken contract fn deploy_mock_eth_token() -> ContractAddress { - let erc20_class_hash = declare("MockETHToken").unwrap(); + let erc20_class_hash = declare("MockETHToken").unwrap().contract_class(); let INITIAL_SUPPLY: u256 = 100000000000000000000; // 100_ETH_IN_WEI let mut calldata = array![]; calldata.append_serde(INITIAL_SUPPLY); @@ -28,7 +30,7 @@ fn deploy_mock_eth_token() -> ContractAddress { // Should deploy the YourToken contract fn deploy_your_token_token() -> ContractAddress { - let erc20_class_hash = declare("YourToken").unwrap(); + let erc20_class_hash = declare("YourToken").unwrap().contract_class(); let mut calldata = array![]; calldata.append_serde(RECIPIENT()); let (your_token_address, _) = erc20_class_hash.deploy(@calldata).unwrap(); @@ -40,7 +42,7 @@ fn deploy_your_token_token() -> ContractAddress { fn deploy_vendor_contract() -> ContractAddress { let eth_token_address = deploy_mock_eth_token(); let your_token_address = deploy_your_token_token(); - let vendor_class_hash = declare("Vendor").unwrap(); + let vendor_class_hash = declare("Vendor").unwrap().contract_class(); let tester_address = RECIPIENT(); let mut calldata = array![]; calldata.append_serde(eth_token_address);