From b40ae78256b0671c5c8ccbeccd8a438c9ac855a1 Mon Sep 17 00:00:00 2001 From: Paul Launay Date: Mon, 16 Sep 2024 16:14:52 +0200 Subject: [PATCH] feat(react): test hooks (#457) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Add sdk react hook tests. ## What type of PR is this? (check all applicable) - [ ] 🍕 Feature (`feat:`) - [ ] 🐛 Bug Fix (`fix:`) - [ ] 📝 Documentation Update (`docs:`) - [ ] 🎨 Style (`style:`) - [ ] 🧑‍💻 Code Refactor (`refactor:`) - [ ] 🔥 Performance Improvements (`perf:`) - [x] ✅ Test (`test:`) - [ ] 🤖 Build (`build:`) - [ ] 🔁 CI (`ci:`) - [ ] 📦 Chore (`chore:`) - [ ] ⏩ Revert (`revert:`) - [ ] 🚀 Breaking Changes (`BREAKING CHANGE:`) --- packages/core/src/createConfig.ts | 2 +- packages/core/tests/utils/index.ts | 122 -------- packages/react/package.json | 1 + packages/react/src/components/ArkProvider.tsx | 74 ++--- packages/react/src/hooks/useArkFees.test.ts | 22 +- packages/react/src/hooks/useArkFees.ts | 9 +- .../react/src/hooks/useBrokerFees.test.ts | 26 ++ packages/react/src/hooks/useBrokerFees.ts | 9 +- packages/react/src/hooks/useCancel.test.ts | 63 ++++ packages/react/src/hooks/useCancel.ts | 43 +-- .../hooks/useCollectionCreatorFees.test.ts | 30 ++ .../src/hooks/useCollectionCreatorFees.ts | 5 +- packages/react/src/hooks/useConfig.ts | 9 +- .../react/src/hooks/useCreateAuction.test.ts | 48 +++ packages/react/src/hooks/useCreateAuction.ts | 47 +-- .../react/src/hooks/useCreateListing.test.ts | 47 +++ packages/react/src/hooks/useCreateListing.ts | 42 +-- .../react/src/hooks/useCreateOffer.test.ts | 59 ++++ packages/react/src/hooks/useCreateOffer.ts | 43 +-- .../src/hooks/useDefaultCreatorFees.test.ts | 24 ++ .../react/src/hooks/useDefaultCreatorFees.ts | 9 +- .../react/src/hooks/useFeesAmount.test.ts | 34 +++ packages/react/src/hooks/useFeesAmount.ts | 16 +- .../react/src/hooks/useFulfillAuction.test.ts | 73 +++++ packages/react/src/hooks/useFulfillAuction.ts | 43 +-- .../react/src/hooks/useFulfillListing.test.ts | 62 ++++ packages/react/src/hooks/useFulfillListing.ts | 43 +-- .../react/src/hooks/useFulfillOffer.test.ts | 73 +++++ packages/react/src/hooks/useFulfillOffer.ts | 43 +-- packages/react/src/hooks/useOrderType.ts | 21 +- packages/react/{src => }/test/setup.ts | 0 packages/react/tsconfig.json | 2 +- packages/react/vitest.config.ts | 2 +- packages/test/.eslintrc.js | 14 + packages/test/package.json | 39 +++ .../{react/src/test => test/src}/accounts.ts | 2 +- .../{react/src/test => test/src}/config.ts | 6 +- packages/test/src/exports/index.ts | 5 + .../{react/src/test => test/src}/react.tsx | 8 +- packages/test/src/setup.ts | 4 + packages/test/src/utils.ts | 144 +++++++++ packages/test/tsconfig.json | 12 + pnpm-lock.yaml | 287 ++++++++++++++++-- 43 files changed, 1196 insertions(+), 471 deletions(-) create mode 100644 packages/react/src/hooks/useBrokerFees.test.ts create mode 100644 packages/react/src/hooks/useCancel.test.ts create mode 100644 packages/react/src/hooks/useCollectionCreatorFees.test.ts create mode 100644 packages/react/src/hooks/useCreateAuction.test.ts create mode 100644 packages/react/src/hooks/useCreateListing.test.ts create mode 100644 packages/react/src/hooks/useCreateOffer.test.ts create mode 100644 packages/react/src/hooks/useDefaultCreatorFees.test.ts create mode 100644 packages/react/src/hooks/useFeesAmount.test.ts create mode 100644 packages/react/src/hooks/useFulfillAuction.test.ts create mode 100644 packages/react/src/hooks/useFulfillListing.test.ts create mode 100644 packages/react/src/hooks/useFulfillOffer.test.ts rename packages/react/{src => }/test/setup.ts (100%) create mode 100644 packages/test/.eslintrc.js create mode 100644 packages/test/package.json rename packages/{react/src/test => test/src}/accounts.ts (98%) rename packages/{react/src/test => test/src}/config.ts (59%) create mode 100644 packages/test/src/exports/index.ts rename packages/{react/src/test => test/src}/react.tsx (90%) create mode 100644 packages/test/src/setup.ts create mode 100644 packages/test/src/utils.ts create mode 100644 packages/test/tsconfig.json diff --git a/packages/core/src/createConfig.ts b/packages/core/src/createConfig.ts index 9c26a76ba..0ac92416d 100644 --- a/packages/core/src/createConfig.ts +++ b/packages/core/src/createConfig.ts @@ -33,7 +33,7 @@ export function createConfig({ starknetCurrencyContract = starknetEthContract }: CreateConfigParameters): Config { if (starknetNetwork === networks.dev && !starknetExecutorContract) { - throw new Error("starknetExecutorContract is required for dev network"); + throw new Error("Executor contract address is required for dev network"); } return { diff --git a/packages/core/tests/utils/index.ts b/packages/core/tests/utils/index.ts index fd10b600c..55e3b8005 100644 --- a/packages/core/tests/utils/index.ts +++ b/packages/core/tests/utils/index.ts @@ -2,8 +2,6 @@ import { Account, cairo, CairoCustomEnum, - Call, - CallData, Contract, ProviderInterface } from "starknet"; @@ -47,126 +45,6 @@ export function getTypeFromCairoCustomEnum(cairoCustomEnum: CairoCustomEnum) { throw new Error("No valid variant found in CairoCustomEnum"); } -const mintERC20ABI = [ - { - type: "function", - name: "mint", - inputs: [ - { - name: "recipient", - type: "core::starknet::contract_address::ContractAddress" - }, - { - name: "amount", - type: "core::integer::u256" - } - ], - outputs: [], - state_mutability: "external" - } -]; - -export const mintERC20 = async ({ - account, - amount = 1000 -}: { - account: Account; - amount: number; -}) => { - const mintERC20Call: Call = { - contractAddress: config.starknetCurrencyContract, - entrypoint: "mint", - calldata: CallData.compile([account.address, cairo.uint256(amount)]) - }; - - const result = await account.execute(mintERC20Call, [mintERC20ABI]); - - await config.starknetProvider.waitForTransaction(result.transaction_hash, { - retryInterval: 1000 - }); - - return result.transaction_hash; -}; - -const mintERC721ABI = [ - { - type: "function", - name: "mint", - inputs: [ - { - name: "recipient", - type: "core::starknet::contract_address::ContractAddress" - }, - { name: "token_uri", type: "core::felt252" } - ], - outputs: [], - state_mutability: "external" - }, - { - type: "function", - name: "get_current_token_id", - inputs: [], - outputs: [{ type: "core::felt252" }], - state_mutability: "view" - } -] as const; - -export async function mintERC721({ account }: { account: Account }) { - const contract = new Contract( - mintERC721ABI, - contracts.nftContract, - config.starknetProvider - ).typedv2(mintERC721ABI); - const tokenId = BigInt(await contract.get_current_token_id()); - - const mintCall: Call = { - contractAddress: contracts.nftContract, - entrypoint: "mint", - calldata: CallData.compile({ - recipient: account.address, - token_uri: `https://api.everai.xyz/m/1` - }) - }; - - const { transaction_hash } = await account.execute(mintCall, [mintERC721ABI]); - - await config.starknetProvider.waitForTransaction(transaction_hash, { - retryInterval: 1000 - }); - - return { - tokenId, - tokenAddress: contracts.nftContract - }; -} - -const balanceOfERC20ABI = [ - { - type: "function", - name: "balanceOf", - inputs: [ - { - name: "account", - type: "core::starknet::contract_address::ContractAddress" - } - ], - outputs: [{ type: "core::integer::u256" }], - state_mutability: "view" - } -] as const; - -export const getBalance = async ({ account }: { account: Account }) => { - const contract = new Contract( - balanceOfERC20ABI, - config.starknetCurrencyContract, - config.starknetProvider - ).typedv2(balanceOfERC20ABI); - - const balance = await contract.balanceOf(account.address); - - return balance as bigint; -}; - function fetchAccount( provider: ProviderInterface, address: string, diff --git a/packages/react/package.json b/packages/react/package.json index dd245d999..2e307dc35 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -21,6 +21,7 @@ "license": "ISC", "dependencies": { "@ark-project/core": "workspace:*", + "@ark-project/test": "workspace:*", "@starknet-react/chains": "^0.1.7", "@starknet-react/core": "^2.8.1", "@tanstack/react-query": "^5.55.4", diff --git a/packages/react/src/components/ArkProvider.tsx b/packages/react/src/components/ArkProvider.tsx index 6a46efdae..40cde2af3 100644 --- a/packages/react/src/components/ArkProvider.tsx +++ b/packages/react/src/components/ArkProvider.tsx @@ -1,25 +1,16 @@ "use client"; -import React, { - createContext, - PropsWithChildren, - useEffect, - useState -} from "react"; +import React, { createContext, PropsWithChildren, useMemo } from "react"; -import { useAccount, useNetwork, useProvider } from "@starknet-react/core"; +import { useNetwork, useProvider } from "@starknet-react/core"; import { Config, createConfig, - CreateConfigParameters, - Network + CreateConfigParameters } from "@ark-project/core"; -import { getOwner } from "../lib/getOwner"; - -const OwnerDataContext = createContext(undefined); -const ConfigDataContext = createContext(undefined); +const ArkContext = createContext(undefined); export type ArkProviderProviderProps = { config: CreateConfigParameters; @@ -27,51 +18,20 @@ export type ArkProviderProviderProps = { function ArkProvider(props: PropsWithChildren) { const { children, config: baseConfig } = props; - const [owner, setOwner] = useState(undefined); - const { provider: starknetProvider } = useProvider(); - const { chain: starknetChain } = useNetwork(); - const [config, setConfig] = useState( - createConfig({ - starknetProvider: starknetProvider, - starknetNetwork: baseConfig.starknetNetwork as Network - }) + const { provider } = useProvider(); + const { chain } = useNetwork(); + + const config = useMemo( + () => + createConfig({ + ...baseConfig, + starknetProvider: provider, + starknetNetwork: baseConfig.starknetNetwork + }), + [chain, provider, baseConfig.starknetNetwork] ); - useEffect(() => { - const newConfig = createConfig({ - starknetProvider: starknetProvider, - starknetNetwork: baseConfig.starknetNetwork as Network - }); - setConfig(newConfig); - }, [starknetProvider, starknetChain, baseConfig]); - - const { address, connector } = useAccount(); - - useEffect(() => { - const fetchOwner = async () => { - if (address && config.starknetProvider && connector?.id) { - const owner = await getOwner( - address, - config.starknetProvider, - connector?.id - ); - if (Array.isArray(owner) && owner[0]) { - setOwner(owner[0]); - } - } else { - setOwner(undefined); - } - }; - fetchOwner(); - }, [address, config.starknetProvider, connector]); - - return ( - - - {children} - - - ); + return {children}; } -export { ArkProvider, ConfigDataContext, OwnerDataContext }; +export { ArkContext, ArkProvider }; diff --git a/packages/react/src/hooks/useArkFees.test.ts b/packages/react/src/hooks/useArkFees.test.ts index 5e95407ba..3c909aeb3 100644 --- a/packages/react/src/hooks/useArkFees.test.ts +++ b/packages/react/src/hooks/useArkFees.test.ts @@ -1,12 +1,20 @@ -import { expect, it } from "vitest"; +import { describe, expect, it } from "vitest"; -import { renderHook } from "../test/react"; +import { renderHook, waitFor } from "../../../test/src/react"; import { useArkFees } from "./useArkFees"; -it("default", async () => { - const { result, rerender } = renderHook(() => useArkFees()); +describe("useArkFees", () => { + it("default", async () => { + const { result } = renderHook(() => useArkFees()); - expect(result.current.isSuccess).toBe(false); - rerender(); - expect(result.current.isSuccess).toBe(true); + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + expect(result.current.data).toMatchInlineSnapshot(` + { + "denominator": 1n, + "formatted": "0.00", + "numerator": 0n, + } + `); + }); }); diff --git a/packages/react/src/hooks/useArkFees.ts b/packages/react/src/hooks/useArkFees.ts index df1c4412d..7fd7f7bc7 100644 --- a/packages/react/src/hooks/useArkFees.ts +++ b/packages/react/src/hooks/useArkFees.ts @@ -8,20 +8,17 @@ import { useConfig } from "./useConfig"; function useArkFees() { const config = useConfig(); - const query = useQuery({ + + return useQuery({ queryKey: ["getArkFees"], queryFn: async () => { if (!config) { throw new Error("Config not found"); } - const fees = await getArkFees(config); - - return fees; + return getArkFees(config); } }); - - return query; } export { useArkFees }; diff --git a/packages/react/src/hooks/useBrokerFees.test.ts b/packages/react/src/hooks/useBrokerFees.test.ts new file mode 100644 index 000000000..96f6cbc91 --- /dev/null +++ b/packages/react/src/hooks/useBrokerFees.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from "vitest"; + +import { accounts } from "@ark-project/test"; + +import { renderHook, waitFor } from "../../../test/src/react"; +import { useBrokerFees } from "./useBrokerFees"; + +describe("useBrokerFees", () => { + it("default", async () => { + const { listingBroker } = accounts; + + const { result } = renderHook(() => + useBrokerFees({ brokerAdress: listingBroker.address }) + ); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + expect(result.current.data).toMatchInlineSnapshot(` + { + "denominator": 1n, + "formatted": "0.00", + "numerator": 0n, + } + `); + }); +}); diff --git a/packages/react/src/hooks/useBrokerFees.ts b/packages/react/src/hooks/useBrokerFees.ts index 79b07076f..59fdf1af1 100644 --- a/packages/react/src/hooks/useBrokerFees.ts +++ b/packages/react/src/hooks/useBrokerFees.ts @@ -12,20 +12,17 @@ interface UseBrokerFeesParams { function useBrokerFees({ brokerAdress }: UseBrokerFeesParams) { const config = useConfig(); - const query = useQuery({ + + return useQuery({ queryKey: ["getBrokerFees", brokerAdress], queryFn: async () => { if (!config) { throw new Error("Config not found"); } - const fees = await getBrokerFees(config, brokerAdress); - - return fees; + return getBrokerFees(config, brokerAdress); } }); - - return query; } export { useBrokerFees }; diff --git a/packages/react/src/hooks/useCancel.test.ts b/packages/react/src/hooks/useCancel.test.ts new file mode 100644 index 000000000..666dea22c --- /dev/null +++ b/packages/react/src/hooks/useCancel.test.ts @@ -0,0 +1,63 @@ +import { useAccount, useConnect, useDisconnect } from "@starknet-react/core"; +import { describe, expect, it } from "vitest"; + +import { + accounts, + defaultConnector, + mintERC20, + mintERC721 +} from "@ark-project/test"; + +import { act, renderHook, waitFor } from "../../../test/src/react"; +import useCreateOffer from "./useCreateOffer"; + +function useCreateOfferWithConnect() { + return { + createOffer: useCreateOffer(), + account: useAccount(), + connect: useConnect(), + disconnect: useDisconnect() + }; +} + +describe("useCancelOffer", () => { + it("default", async () => { + const { seller } = accounts; + const { result } = renderHook(() => useCreateOfferWithConnect()); + + await act(async () => { + await result.current.connect.connectAsync({ + connector: defaultConnector + }); + }); + + const buyer = result.current.account.account; + + expect(seller).toBeDefined(); + + if (!buyer) { + return; + } + + await act(async () => { + const { tokenId, tokenAddress } = await mintERC721({ account: seller }); + await mintERC20({ account: buyer, amount: 1000 }); + + await result.current.createOffer.createOfferAsync({ + account: buyer, + brokerAddress: accounts.listingBroker.address, + tokenAddress, + tokenId, + amount: BigInt(1000) + }); + }); + + await waitFor(() => + expect(result.current.createOffer.isSuccess).toBe(true) + ); + + await act(async () => { + await result.current.disconnect.disconnectAsync(); + }); + }, 50_000); +}); diff --git a/packages/react/src/hooks/useCancel.ts b/packages/react/src/hooks/useCancel.ts index 5b006a719..04035994f 100644 --- a/packages/react/src/hooks/useCancel.ts +++ b/packages/react/src/hooks/useCancel.ts @@ -1,46 +1,29 @@ "use client"; -import { useState } from "react"; +import { useMutation } from "@tanstack/react-query"; -import { - cancelOrder as cancelOrderCore, - CancelOrderParameters, - CancelOrderResult -} from "@ark-project/core"; +import { cancelOrder, CancelOrderParameters } from "@ark-project/core"; -import { Status } from "../types"; import { useConfig } from "./useConfig"; function useCancel() { - const [status, setStatus] = useState("idle"); - const [result, setResult] = useState(); const config = useConfig(); - async function cancel(parameters: CancelOrderParameters) { - if (!config) { - throw new Error("config not loaded"); - } - - setStatus("loading"); - - try { - const cancelResult = await cancelOrderCore(config, parameters); + const { mutate, mutateAsync, ...result } = useMutation({ + mutationKey: ["cancel"], + mutationFn: async (parameters: CancelOrderParameters) => { + if (!config) { + throw new Error("config not loaded"); + } - setStatus("success"); - setResult(cancelResult); - } catch (error) { - setStatus("error"); - console.error(error); + return cancelOrder(config, parameters); } - } + }); return { - cancel, - isLoading: status === "loading", - isError: status === "error", - isSuccess: status === "success", - status, - result + ...result, + cancel: mutate, + cancelAsync: mutateAsync }; } diff --git a/packages/react/src/hooks/useCollectionCreatorFees.test.ts b/packages/react/src/hooks/useCollectionCreatorFees.test.ts new file mode 100644 index 000000000..ff1a38d66 --- /dev/null +++ b/packages/react/src/hooks/useCollectionCreatorFees.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from "vitest"; + +import { accounts, mintERC721 } from "@ark-project/test"; +import { renderHook, waitFor } from "@ark-project/test/src/react"; + +import { useCollectionCreatorFees } from "./useCollectionCreatorFees"; + +describe("useCollectionCreatorFees", () => { + it("default", async () => { + const { seller } = accounts; + const { tokenAddress } = await mintERC721({ account: seller }); + + const { result } = renderHook(() => + useCollectionCreatorFees({ tokenAddress }) + ); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + expect(result.current.data).toMatchInlineSnapshot(` + { + "creator": "0x6162896d1d7ab204c7ccac6dd5f8e9e7c25ecd5ae4fcb4ad32e57786bb46e03", + "fees": { + "denominator": 1n, + "formatted": "0.00", + "numerator": 0n, + }, + } + `); + }); +}); diff --git a/packages/react/src/hooks/useCollectionCreatorFees.ts b/packages/react/src/hooks/useCollectionCreatorFees.ts index c28fa3f42..be60b53c2 100644 --- a/packages/react/src/hooks/useCollectionCreatorFees.ts +++ b/packages/react/src/hooks/useCollectionCreatorFees.ts @@ -14,7 +14,8 @@ function useCollectionCreatorFees({ tokenAddress }: UseCollectionCreatorFeesParams) { const config = useConfig(); - const query = useQuery({ + + return useQuery({ queryKey: ["getCollectionCreatorFees", tokenAddress], queryFn: async () => { if (!config) { @@ -26,8 +27,6 @@ function useCollectionCreatorFees({ return fees; } }); - - return query; } export { useCollectionCreatorFees }; diff --git a/packages/react/src/hooks/useConfig.ts b/packages/react/src/hooks/useConfig.ts index 5d2cd0ff3..4b3f79a2f 100644 --- a/packages/react/src/hooks/useConfig.ts +++ b/packages/react/src/hooks/useConfig.ts @@ -2,10 +2,15 @@ import { useContext } from "react"; -import { ConfigDataContext } from "../components/ArkProvider"; +import { ArkContext } from "../components/ArkProvider"; function useConfig() { - const context = useContext(ConfigDataContext); + const context = useContext(ArkContext); + + if (context === undefined) { + throw new Error("useConfig must be used within a ArkProvider"); + } + return context; } diff --git a/packages/react/src/hooks/useCreateAuction.test.ts b/packages/react/src/hooks/useCreateAuction.test.ts new file mode 100644 index 000000000..ef7cc120e --- /dev/null +++ b/packages/react/src/hooks/useCreateAuction.test.ts @@ -0,0 +1,48 @@ +import { useAccount, useConnect, useDisconnect } from "@starknet-react/core"; +import { describe, expect, it } from "vitest"; + +import { accounts, defaultConnector, mintERC721 } from "@ark-project/test"; +import { act, renderHook, waitFor } from "@ark-project/test/src/react"; + +import { useCreateAuction } from "./useCreateAuction"; + +function useCreateAuctionWithConnect() { + return { + mutation: useCreateAuction(), + account: useAccount(), + connect: useConnect(), + disconnect: useDisconnect() + }; +} + +describe("useCreateAuction", () => { + it("default", async () => { + const { result } = renderHook(() => useCreateAuctionWithConnect()); + + await act(async () => { + await result.current.connect.connectAsync({ + connector: defaultConnector + }); + }); + + await act(async () => { + const seller = await defaultConnector.account(); + const { tokenId, tokenAddress } = await mintERC721({ account: seller }); + + await result.current.mutation.createAuctionAsync({ + account: seller, + brokerAddress: accounts.listingBroker.address, + tokenAddress, + tokenId, + startAmount: BigInt(1000), + endAmount: BigInt(2000) + }); + }); + + await waitFor(() => expect(result.current.mutation.isSuccess).toBe(true)); + + await act(async () => { + await result.current.disconnect.disconnectAsync(); + }); + }, 20_000); +}); diff --git a/packages/react/src/hooks/useCreateAuction.ts b/packages/react/src/hooks/useCreateAuction.ts index 2e168e632..c29b6f487 100644 --- a/packages/react/src/hooks/useCreateAuction.ts +++ b/packages/react/src/hooks/useCreateAuction.ts @@ -1,50 +1,31 @@ "use client"; -import { useState } from "react"; +import { useMutation } from "@tanstack/react-query"; -import { - Config, - createAuction, - CreateAuctionParameters, - type CreateAuctionResult -} from "@ark-project/core"; +import { createAuction, CreateAuctionParameters } from "@ark-project/core"; -import { Status } from "../types"; import { useConfig } from "./useConfig"; export default function useCreateAuction() { - const [status, setStatus] = useState("idle"); - const [result, setResult] = useState(); const config = useConfig(); - async function create(parameters: CreateAuctionParameters) { - if (!config) { - throw new Error("config not loaded"); - } - - setStatus("loading"); + const { mutate, mutateAsync, ...result } = useMutation({ + mutationKey: ["createAuction"], + mutationFn: async (parameters: CreateAuctionParameters) => { + if (!config) { + throw new Error("config not loaded"); + } - try { - const createAuctionResult = await createAuction( - config as Config, - parameters - ); + const result = await createAuction(config, parameters); - setResult(createAuctionResult); - setStatus("success"); - } catch (error) { - console.error("error: failed to create auction", error); - setStatus("error"); + return result; } - } + }); return { - create, - isLoading: status === "loading", - isError: status === "error", - isSuccess: status === "success", - status, - result + ...result, + createAuction: mutate, + createAuctionAsync: mutateAsync }; } diff --git a/packages/react/src/hooks/useCreateListing.test.ts b/packages/react/src/hooks/useCreateListing.test.ts new file mode 100644 index 000000000..67e0674da --- /dev/null +++ b/packages/react/src/hooks/useCreateListing.test.ts @@ -0,0 +1,47 @@ +import { useAccount, useConnect, useDisconnect } from "@starknet-react/core"; +import { describe, expect, it } from "vitest"; + +import { accounts, defaultConnector, mintERC721 } from "@ark-project/test"; +import { act, renderHook, waitFor } from "@ark-project/test/src/react"; + +import { useCreateListing } from "./useCreateListing"; + +function useCreateListingWithConnect() { + return { + mutation: useCreateListing(), + account: useAccount(), + connect: useConnect(), + disconnect: useDisconnect() + }; +} + +describe("useCreateListing", () => { + it("default", async () => { + const { result } = renderHook(() => useCreateListingWithConnect()); + + await act(async () => { + await result.current.connect.connectAsync({ + connector: defaultConnector + }); + }); + + await act(async () => { + const seller = await defaultConnector.account(); + const { tokenId, tokenAddress } = await mintERC721({ account: seller }); + + await result.current.mutation.createListingAsync({ + account: seller, + brokerAddress: accounts.listingBroker.address, + tokenAddress, + tokenId, + amount: BigInt(1000) + }); + }); + + await waitFor(() => expect(result.current.mutation.isSuccess).toBe(true)); + + await act(async () => { + await result.current.disconnect.disconnectAsync(); + }); + }, 20_000); +}); diff --git a/packages/react/src/hooks/useCreateListing.ts b/packages/react/src/hooks/useCreateListing.ts index f10370ba1..70592a56d 100644 --- a/packages/react/src/hooks/useCreateListing.ts +++ b/packages/react/src/hooks/useCreateListing.ts @@ -1,13 +1,9 @@ "use client"; -import { useState } from "react"; +import { useMutation } from "@tanstack/react-query"; -import { - createListing as createListingCore, - CreateListingParameters -} from "@ark-project/core"; +import { createListing, CreateListingParameters } from "@ark-project/core"; -import { Status } from "../types"; import { useConfig } from "./useConfig"; export type CreateListingResult = { @@ -16,35 +12,25 @@ export type CreateListingResult = { }; function useCreateListing() { - const [status, setStatus] = useState("idle"); - const [result, setResult] = useState(); const config = useConfig(); - async function createListing(parameters: CreateListingParameters) { - if (!config) { - throw new Error("config not loaded"); - } - - setStatus("loading"); + const { mutate, mutateAsync, ...result } = useMutation({ + mutationKey: ["createListing"], + mutationFn: async (parameters: CreateListingParameters) => { + if (!config) { + throw new Error("config not loaded"); + } - try { - const createListingResult = await createListingCore(config, parameters); + const result = await createListing(config, parameters); - setResult(createListingResult); - setStatus("success"); - } catch (error) { - console.error(error); - setStatus("error"); + return result; } - } + }); return { - createListing, - isLoading: status === "loading", - isError: status === "error", - isSuccess: status === "success", - status, - result + ...result, + createListing: mutate, + createListingAsync: mutateAsync }; } diff --git a/packages/react/src/hooks/useCreateOffer.test.ts b/packages/react/src/hooks/useCreateOffer.test.ts new file mode 100644 index 000000000..37ee347ae --- /dev/null +++ b/packages/react/src/hooks/useCreateOffer.test.ts @@ -0,0 +1,59 @@ +import { useAccount, useConnect, useDisconnect } from "@starknet-react/core"; +import { describe, expect, it } from "vitest"; + +import { + accounts, + defaultConnector, + mintERC20, + mintERC721 +} from "@ark-project/test"; + +import { act, renderHook, waitFor } from "../../../test/src/react"; +import useCreateOffer from "./useCreateOffer"; + +function useCreateOfferWithConnect() { + return { + mutation: useCreateOffer(), + account: useAccount(), + connect: useConnect(), + disconnect: useDisconnect() + }; +} + +describe("useCreateOffer", () => { + it("default", async () => { + const { seller } = accounts; + const { result } = renderHook(() => useCreateOfferWithConnect()); + + await act(async () => { + await result.current.connect.connectAsync({ + connector: defaultConnector + }); + }); + + await act(async () => { + const buyer = result.current.account.account; + + if (!buyer) { + throw new Error("Account not connected"); + } + + const { tokenId, tokenAddress } = await mintERC721({ account: seller }); + await mintERC20({ account: buyer, amount: 1000 }); + + await result.current.mutation.createOfferAsync({ + account: buyer, + brokerAddress: accounts.listingBroker.address, + tokenAddress, + tokenId, + amount: BigInt(1000) + }); + }); + + await waitFor(() => expect(result.current.mutation.isSuccess).toBe(true)); + + await act(async () => { + await result.current.disconnect.disconnectAsync(); + }); + }, 10_000); +}); diff --git a/packages/react/src/hooks/useCreateOffer.ts b/packages/react/src/hooks/useCreateOffer.ts index 2e3d277a0..18d37e484 100644 --- a/packages/react/src/hooks/useCreateOffer.ts +++ b/packages/react/src/hooks/useCreateOffer.ts @@ -1,46 +1,31 @@ "use client"; -import { useState } from "react"; +import { useMutation } from "@tanstack/react-query"; -import { - createOffer as createOfferCore, - CreateOfferParameters, - CreateOfferResult -} from "@ark-project/core"; +import { createOffer, CreateOfferParameters } from "@ark-project/core"; -import { Status } from "../types"; import { useConfig } from "./useConfig"; export default function useCreateOffer() { - const [status, setStatus] = useState("idle"); - const [result, setResult] = useState(); const config = useConfig(); - async function createOffer(parameters: CreateOfferParameters) { - if (!config) { - throw new Error("config not loaded"); - } - - setStatus("loading"); + const { mutate, mutateAsync, ...result } = useMutation({ + mutationKey: ["cancel"], + mutationFn: async (parameters: CreateOfferParameters) => { + if (!config) { + throw new Error("config not loaded"); + } - try { - const createOfferResult = await createOfferCore(config, parameters); + const result = await createOffer(config, parameters); - setResult(createOfferResult); - setStatus("success"); - } catch (error) { - console.error(error); - setStatus("error"); + return result; } - } + }); return { - createOffer, - isLoading: status === "loading", - isError: status === "error", - isSuccess: status === "success", - status, - result + ...result, + createOffer: mutate, + createOfferAsync: mutateAsync }; } export { useCreateOffer }; diff --git a/packages/react/src/hooks/useDefaultCreatorFees.test.ts b/packages/react/src/hooks/useDefaultCreatorFees.test.ts new file mode 100644 index 000000000..440132ac4 --- /dev/null +++ b/packages/react/src/hooks/useDefaultCreatorFees.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from "vitest"; + +import { renderHook, waitFor } from "@ark-project/test/src/react"; + +import { useDefaultCreatorFees } from "./useDefaultCreatorFees"; + +describe("useDefaultCreatorFees", () => { + it("default", async () => { + const { result } = renderHook(useDefaultCreatorFees); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + expect(result.current.data).toMatchInlineSnapshot(` + { + "creator": "0x6162896d1d7ab204c7ccac6dd5f8e9e7c25ecd5ae4fcb4ad32e57786bb46e03", + "fees": { + "denominator": 1n, + "formatted": "0.00", + "numerator": 0n, + }, + } + `); + }); +}); diff --git a/packages/react/src/hooks/useDefaultCreatorFees.ts b/packages/react/src/hooks/useDefaultCreatorFees.ts index 9825c2019..06958e736 100644 --- a/packages/react/src/hooks/useDefaultCreatorFees.ts +++ b/packages/react/src/hooks/useDefaultCreatorFees.ts @@ -8,20 +8,17 @@ import { useConfig } from "./useConfig"; function useDefaultCreatorFees() { const config = useConfig(); - const query = useQuery({ + + return useQuery({ queryKey: ["getDefaultCreatorFees"], queryFn: async () => { if (!config) { throw new Error("Config not found"); } - const fees = await getDefaultCreatorFees(config); - - return fees; + return getDefaultCreatorFees(config); } }); - - return query; } export { useDefaultCreatorFees }; diff --git a/packages/react/src/hooks/useFeesAmount.test.ts b/packages/react/src/hooks/useFeesAmount.test.ts new file mode 100644 index 000000000..afbdd8413 --- /dev/null +++ b/packages/react/src/hooks/useFeesAmount.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "vitest"; + +import { accounts, mintERC721 } from "@ark-project/test"; +import { renderHook, waitFor } from "@ark-project/test/src/react"; + +import { useFeesAmount } from "./useFeesAmount"; + +describe("useFeesAmount", () => { + it("default", async () => { + const { seller, saleBroker, listingBroker } = accounts; + const { tokenId, tokenAddress } = await mintERC721({ account: seller }); + + const { result } = renderHook(() => + useFeesAmount({ + fulfillBroker: saleBroker.address, + listingBroker: listingBroker.address, + tokenAddress, + tokenId, + amount: BigInt(1000) + }) + ); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + expect(result.current.data).toMatchInlineSnapshot(` + { + "ark": 0n, + "creator": 0n, + "fulfillBroker": 0n, + "listingBroker": 0n, + } + `); + }); +}); diff --git a/packages/react/src/hooks/useFeesAmount.ts b/packages/react/src/hooks/useFeesAmount.ts index c47205f63..9c69c6182 100644 --- a/packages/react/src/hooks/useFeesAmount.ts +++ b/packages/react/src/hooks/useFeesAmount.ts @@ -22,29 +22,31 @@ function useFeesAmount({ amount }: UseFeesAmountProps) { const config = useConfig(); - const query = useQuery({ + + return useQuery({ queryKey: [ "getFeesAmount", - { fulfillBroker, listingBroker, tokenAddress, tokenId, amount } + { + fulfillBroker, + listingBroker, + tokenAddress, + tokenId: tokenId.toString() + } ], queryFn: async () => { if (!config) { throw new Error("Config not found"); } - const fees = await getFeesAmount(config, { + return getFeesAmount(config, { fulfillBroker, listingBroker, nftAddress: tokenAddress, nftTokenId: tokenId, paymentAmount: amount }); - - return fees; } }); - - return query; } export { useFeesAmount }; diff --git a/packages/react/src/hooks/useFulfillAuction.test.ts b/packages/react/src/hooks/useFulfillAuction.test.ts new file mode 100644 index 000000000..91ae46066 --- /dev/null +++ b/packages/react/src/hooks/useFulfillAuction.test.ts @@ -0,0 +1,73 @@ +import { useAccount, useConnect, useDisconnect } from "@starknet-react/core"; +import { describe, expect, it } from "vitest"; + +import { createAuction, createOffer } from "@ark-project/core"; +import { + accounts, + defaultConnector, + mintERC20, + mintERC721 +} from "@ark-project/test"; +import config from "@ark-project/test/src/config"; +import { act, renderHook, waitFor } from "@ark-project/test/src/react"; + +import { useFulfillAuction } from "./useFulfillAuction"; + +function useCreateAuctionWithConnect() { + return { + fulfillAuction: useFulfillAuction(), + account: useAccount(), + connect: useConnect(), + disconnect: useDisconnect() + }; +} + +describe("useFulfillAuction", () => { + it("default", async () => { + const { seller, buyer, listingBroker, saleBroker } = accounts; + const { tokenId, tokenAddress } = await mintERC721({ account: seller }); + await mintERC20({ account: buyer, amount: 1000 }); + const { orderHash: relatedOrderHash } = await createAuction(config, { + account: seller, + brokerAddress: listingBroker.address, + tokenAddress, + tokenId, + startAmount: BigInt(1000), + endAmount: BigInt(2000) + }); + const { orderHash } = await createOffer(config, { + account: buyer, + brokerAddress: listingBroker.address, + tokenAddress, + tokenId, + amount: BigInt(1000) + }); + + const { result } = renderHook(useCreateAuctionWithConnect); + + await act(async () => { + await result.current.connect.connectAsync({ + connector: defaultConnector + }); + }); + + await act(async () => { + await result.current.fulfillAuction.fulfillAuctionAsync({ + account: seller, + brokerAddress: saleBroker.address, + orderHash: relatedOrderHash, + relatedOrderHash: orderHash, + tokenAddress, + tokenId + }); + }); + + await waitFor(() => + expect(result.current.fulfillAuction.isSuccess).toBe(true) + ); + + await act(async () => { + await result.current.disconnect.disconnectAsync(); + }); + }, 20_000); +}); diff --git a/packages/react/src/hooks/useFulfillAuction.ts b/packages/react/src/hooks/useFulfillAuction.ts index 64f0aa60e..625f53afd 100644 --- a/packages/react/src/hooks/useFulfillAuction.ts +++ b/packages/react/src/hooks/useFulfillAuction.ts @@ -1,46 +1,29 @@ "use client"; -import { useState } from "react"; +import { useMutation } from "@tanstack/react-query"; -import { - fulfillAuction, - FulfillAuctionParameters, - FulfillAuctionResult -} from "@ark-project/core"; +import { fulfillAuction, FulfillAuctionParameters } from "@ark-project/core"; -import { Status } from "../types"; import { useConfig } from "./useConfig"; function useFulfillAuction() { - const [status, setStatus] = useState("idle"); - const [result, setResult] = useState(); const config = useConfig(); - async function fulfill(parameters: FulfillAuctionParameters) { - if (!config) { - throw new Error("Invalid config."); - } - - setStatus("loading"); - - try { - const fulfillAuctionResult = await fulfillAuction(config, parameters); + const { mutate, mutateAsync, ...result } = useMutation({ + mutationKey: ["fulfillAuction"], + mutationFn: async (parameters: FulfillAuctionParameters) => { + if (!config) { + throw new Error("config not loaded"); + } - setStatus("success"); - setResult(fulfillAuctionResult); - } catch (error) { - console.error(error); - setStatus("error"); + return fulfillAuction(config, parameters); } - } + }); return { - fulfill, - isLoading: status === "loading", - isError: status === "error", - isSuccess: status === "success", - status, - result + ...result, + fulfillAuction: mutate, + fulfillAuctionAsync: mutateAsync }; } diff --git a/packages/react/src/hooks/useFulfillListing.test.ts b/packages/react/src/hooks/useFulfillListing.test.ts new file mode 100644 index 000000000..3cfa06577 --- /dev/null +++ b/packages/react/src/hooks/useFulfillListing.test.ts @@ -0,0 +1,62 @@ +import { useAccount, useConnect, useDisconnect } from "@starknet-react/core"; +import { describe, expect, it } from "vitest"; + +import { createListing } from "@ark-project/core"; +import { accounts, defaultConnector, mintERC721 } from "@ark-project/test"; +import config from "@ark-project/test/src/config"; +import { act, renderHook, waitFor } from "@ark-project/test/src/react"; + +import { useCreateListing } from "./useCreateListing"; +import { useFulfillListing } from "./useFulfillListing"; + +function useCreateOfferWithConnect() { + return { + createListing: useCreateListing(), + fulfillListing: useFulfillListing(), + account: useAccount(), + connect: useConnect(), + disconnect: useDisconnect() + }; +} + +describe("useFulfillListing", () => { + it("default", async () => { + const { seller, buyer, listingBroker, saleBroker } = accounts; + const { tokenId, tokenAddress } = await mintERC721({ account: seller }); + // await mintERC20({ account: buyer, amount: 1000 }); + const { orderHash } = await createListing(config, { + account: seller, + brokerAddress: listingBroker.address, + tokenAddress, + tokenId, + amount: BigInt(1000) + }); + + const { result } = renderHook(useCreateOfferWithConnect); + + await act(async () => { + await result.current.connect.connectAsync({ + connector: defaultConnector + }); + }); + + await act(async () => { + await result.current.fulfillListing.fulfillListingAsync({ + account: buyer, + brokerAddress: saleBroker.address, + orderHash, + tokenAddress, + tokenId, + amount: BigInt(1000) + }); + }); + + await waitFor(() => + expect(result.current.fulfillListing.isSuccess).toBe(true) + ); + + await act(async () => { + await result.current.disconnect.disconnectAsync(); + }); + }, 10_000); +}); diff --git a/packages/react/src/hooks/useFulfillListing.ts b/packages/react/src/hooks/useFulfillListing.ts index e29882410..7c1450054 100644 --- a/packages/react/src/hooks/useFulfillListing.ts +++ b/packages/react/src/hooks/useFulfillListing.ts @@ -1,46 +1,29 @@ "use client"; -import { useState } from "react"; +import { useMutation } from "@tanstack/react-query"; -import { - fulfillListing as fulfillListingCore, - FulfillListingParameters, - FulfillListingResult -} from "@ark-project/core"; +import { fulfillListing, FulfillListingParameters } from "@ark-project/core"; -import { Status } from "../types"; import { useConfig } from "./useConfig"; function useFulfillListing() { - const [status, setStatus] = useState("idle"); - const [result, setResult] = useState(); const config = useConfig(); - async function fulfillListing(parameters: FulfillListingParameters) { - if (!config) { - throw new Error("Invalid config."); - } - - setStatus("loading"); - - try { - const fulfillListingResult = await fulfillListingCore(config, parameters); + const { mutate, mutateAsync, ...result } = useMutation({ + mutationKey: ["fulfillListing"], + mutationFn: async (parameters: FulfillListingParameters) => { + if (!config) { + throw new Error("config not loaded"); + } - setStatus("success"); - setResult(fulfillListingResult); - } catch (error) { - console.error(error); - setStatus("error"); + return fulfillListing(config, parameters); } - } + }); return { - fulfillListing, - isLoading: status === "loading", - isError: status === "error", - isSuccess: status === "success", - status, - result + ...result, + fulfillListing: mutate, + fulfillListingAsync: mutateAsync }; } diff --git a/packages/react/src/hooks/useFulfillOffer.test.ts b/packages/react/src/hooks/useFulfillOffer.test.ts new file mode 100644 index 000000000..b434f8d71 --- /dev/null +++ b/packages/react/src/hooks/useFulfillOffer.test.ts @@ -0,0 +1,73 @@ +import { useAccount, useConnect, useDisconnect } from "@starknet-react/core"; +import { describe, expect, it } from "vitest"; + +import { + accounts, + defaultConnector, + mintERC20, + mintERC721 +} from "@ark-project/test"; +import { act, renderHook, waitFor } from "@ark-project/test/src/react"; + +import useCreateOffer from "./useCreateOffer"; +import { useFulfillOffer } from "./useFulfillOffer"; + +function useCreateOfferWithConnect() { + return { + createOffer: useCreateOffer(), + fulfillOffer: useFulfillOffer(), + account: useAccount(), + connect: useConnect(), + disconnect: useDisconnect() + }; +} + +describe("useFulfillOffer", () => { + it("default", async () => { + const { buyer, listingBroker, saleBroker } = accounts; + const { result } = renderHook(useCreateOfferWithConnect); + + await act(async () => { + await result.current.connect.connectAsync({ + connector: defaultConnector + }); + }); + + const seller = result.current.account.account; + + expect(seller).toBeDefined(); + + if (!seller) { + return; + } + + await act(async () => { + const { tokenId, tokenAddress } = await mintERC721({ account: seller }); + await mintERC20({ account: buyer, amount: 1000 }); + + const { orderHash } = await result.current.createOffer.createOfferAsync({ + account: buyer, + brokerAddress: listingBroker.address, + tokenAddress, + tokenId, + amount: BigInt(1000) + }); + + await result.current.fulfillOffer.fulfillOfferAsync({ + account: seller, + brokerAddress: saleBroker.address, + orderHash, + tokenAddress, + tokenId + }); + }); + + await waitFor(() => + expect(result.current.fulfillOffer.isSuccess).toBe(true) + ); + + await act(async () => { + await result.current.disconnect.disconnectAsync(); + }); + }, 10_000); +}); diff --git a/packages/react/src/hooks/useFulfillOffer.ts b/packages/react/src/hooks/useFulfillOffer.ts index e84c1770e..0cc41d1c9 100644 --- a/packages/react/src/hooks/useFulfillOffer.ts +++ b/packages/react/src/hooks/useFulfillOffer.ts @@ -1,46 +1,29 @@ "use client"; -import { useState } from "react"; +import { useMutation } from "@tanstack/react-query"; -import { - fulfillOffer as fulfillOfferCore, - FulfillOfferParameters, - FulfillOfferResult -} from "@ark-project/core"; +import { fulfillOffer, FulfillOfferParameters } from "@ark-project/core"; -import { Status } from "../types"; import { useConfig } from "./useConfig"; function useFulfillOffer() { - const [status, setStatus] = useState("idle"); - const [result, setResult] = useState(); const config = useConfig(); - async function fulfillOffer(parameters: FulfillOfferParameters) { - if (!config) { - throw new Error("Invalid config."); - } - - setStatus("loading"); - - try { - const fulfillOfferResult = await fulfillOfferCore(config, parameters); + const { mutate, mutateAsync, ...result } = useMutation({ + mutationKey: ["fulfillOffer"], + mutationFn: async (parameters: FulfillOfferParameters) => { + if (!config) { + throw new Error("config not loaded"); + } - setStatus("success"); - setResult(fulfillOfferResult); - } catch (error) { - console.error(error); - setStatus("error"); + return fulfillOffer(config, parameters); } - } + }); return { - fulfillOffer, - isLoading: status === "loading", - isError: status === "error", - isSuccess: status === "success", - status, - result + ...result, + fulfillOffer: mutate, + fulfillOfferAsync: mutateAsync }; } diff --git a/packages/react/src/hooks/useOrderType.ts b/packages/react/src/hooks/useOrderType.ts index b55dd3b66..a1ec78015 100644 --- a/packages/react/src/hooks/useOrderType.ts +++ b/packages/react/src/hooks/useOrderType.ts @@ -1,7 +1,6 @@ "use client"; -import { useEffect, useState } from "react"; - +import { useQuery } from "@tanstack/react-query"; import { CairoCustomEnum } from "starknet"; import { getOrderType } from "@ark-project/core"; @@ -28,25 +27,21 @@ export function getTypeFromCairoCustomEnum(cairoCustomEnum: CairoCustomEnum) { } function useOrderType({ orderHash }: { orderHash: bigint }) { - const [type, setType] = useState(null); const config = useConfig(); - useEffect(() => { - const run = async () => { + return useQuery({ + queryKey: ["orderType", orderHash], + queryFn: async () => { if (!config) { - return; + throw new Error("Config not found"); } const orderTypeCairo = await getOrderType(config, { orderHash }); const orderType = getTypeFromCairoCustomEnum(orderTypeCairo.orderType); - setType(orderType); - }; - - run(); - }, [config, orderHash]); - - return type; + return orderType; + } + }); } export { useOrderType }; diff --git a/packages/react/src/test/setup.ts b/packages/react/test/setup.ts similarity index 100% rename from packages/react/src/test/setup.ts rename to packages/react/test/setup.ts diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json index 36c235d29..608a4d9df 100644 --- a/packages/react/tsconfig.json +++ b/packages/react/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "@ark-project/typescript-config/react-library.json", - "include": ["."], + "include": [".", "../test/src/react.tsx"], "exclude": ["dist", "build", "node_modules"], "compilerOptions": { "jsx": "react", diff --git a/packages/react/vitest.config.ts b/packages/react/vitest.config.ts index 86cb3c191..00cde9e95 100644 --- a/packages/react/vitest.config.ts +++ b/packages/react/vitest.config.ts @@ -4,6 +4,6 @@ export default defineConfig({ test: { globals: true, environment: "jsdom", - setupFiles: ["./src/test/setup.ts"] + setupFiles: ["./test/setup.ts"] } }); diff --git a/packages/test/.eslintrc.js b/packages/test/.eslintrc.js new file mode 100644 index 000000000..b5cf4b054 --- /dev/null +++ b/packages/test/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").Linter.Config} */ +module.exports = { + root: true, + env: { + browser: true, + es6: true, + node: false + }, + extends: ["@ark-project/eslint-config/react-internal.js"], + parser: "@typescript-eslint/parser", + parserOptions: { + project: true + } +}; diff --git a/packages/test/package.json b/packages/test/package.json new file mode 100644 index 000000000..056e1a941 --- /dev/null +++ b/packages/test/package.json @@ -0,0 +1,39 @@ +{ + "name": "@ark-project/test", + "version": "1.0.0", + "main": "./src/exports/index.js", + "types": "./src/exports/index.js", + "scripts": { + "clean": "rm -rf .turbo dist node_modules tsconfig.tsbuildinfo", + "lint": "eslint --ext .ts,.tsx .", + "lint:fix": "eslint --ext .ts,.tsx . --fix", + "test": "vitest", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@ark-project/core": "workspace:*", + "@starknet-react/chains": "^0.1.7", + "@starknet-react/core": "^2.8.1", + "@tanstack/react-query": "^5.55.4", + "get-starknet-core": "^3.3.0", + "react": "^18", + "react-dom": "^18", + "starknet": "^6.9.0" + }, + "devDependencies": { + "@ark-project/eslint-config": "workspace:*", + "@ark-project/typescript-config": "workspace:*", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.5.0", + "@testing-library/react": "^16.0.1", + "@testing-library/react-hooks": "^8.0.1", + "@types/jest": "^29.5.5", + "@types/react": "^18", + "dotenv": "^16.4.5", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "react-test-renderer": "^18.3.1", + "ts-jest": "^29.1.1", + "typescript": "^5.3.3" + } +} diff --git a/packages/react/src/test/accounts.ts b/packages/test/src/accounts.ts similarity index 98% rename from packages/react/src/test/accounts.ts rename to packages/test/src/accounts.ts index 765ab4d77..9527f69dc 100644 --- a/packages/react/src/test/accounts.ts +++ b/packages/test/src/accounts.ts @@ -1,7 +1,7 @@ import { MockConnector } from "@starknet-react/core"; import { Account, AccountInterface, ProviderInterface } from "starknet"; -import { config } from "./config"; +import config from "./config"; function fetchAccount( provider: ProviderInterface, diff --git a/packages/react/src/test/config.ts b/packages/test/src/config.ts similarity index 59% rename from packages/react/src/test/config.ts rename to packages/test/src/config.ts index a1416a47d..cc280940f 100644 --- a/packages/react/src/test/config.ts +++ b/packages/test/src/config.ts @@ -1,10 +1,8 @@ import { createConfig } from "@ark-project/core"; -import contracts from "../../../../contracts.dev.json"; +import contracts from "../../../contracts.dev.json"; -export { contracts }; - -export const config = createConfig({ +export default createConfig({ starknetNetwork: "dev", starknetExecutorContract: contracts.executor, starknetCurrencyContract: contracts.eth diff --git a/packages/test/src/exports/index.ts b/packages/test/src/exports/index.ts new file mode 100644 index 000000000..d5647dbec --- /dev/null +++ b/packages/test/src/exports/index.ts @@ -0,0 +1,5 @@ +export * from "../accounts"; +export * from "../config"; +export * from "../react"; +export * from "../setup"; +export * from "../utils"; diff --git a/packages/react/src/test/react.tsx b/packages/test/src/react.tsx similarity index 90% rename from packages/react/src/test/react.tsx rename to packages/test/src/react.tsx index 197871c24..d6d8d179e 100644 --- a/packages/react/src/test/react.tsx +++ b/packages/test/src/react.tsx @@ -15,7 +15,9 @@ import { RenderResult } from "@testing-library/react"; +import { ArkProvider } from "../../react/src/components"; import { defaultConnector } from "./accounts"; +import config from "./config"; function rpc() { return { @@ -74,12 +76,13 @@ function customRender( } = {} ): RenderResult { const { connectorOptions, ...renderOptions } = options; + queryClient.clear(); return render(ui, { wrapper: ({ children }) => ( - {children} + {children} ), @@ -94,12 +97,13 @@ function customRenderHook( } = {} ) { const { connectorOptions, hydrate, ...renderOptions } = options; + queryClient.clear(); return renderHook(render, { wrapper: ({ children }) => ( - {children} + {children} ), diff --git a/packages/test/src/setup.ts b/packages/test/src/setup.ts new file mode 100644 index 000000000..c5693c9f5 --- /dev/null +++ b/packages/test/src/setup.ts @@ -0,0 +1,4 @@ +import * as dotenv from "dotenv"; + +// eslint-disable-next-line no-undef +dotenv.config({ path: `${__dirname}/../../../.env` }); diff --git a/packages/test/src/utils.ts b/packages/test/src/utils.ts new file mode 100644 index 000000000..5f6d44ff5 --- /dev/null +++ b/packages/test/src/utils.ts @@ -0,0 +1,144 @@ +import { + Account, + AccountInterface, + cairo, + Call, + CallData, + Contract +} from "starknet"; + +import contracts from "../../../contracts.dev.json"; +import config from "./config"; + +export async function wait(time: number) { + return new Promise((res) => setTimeout(res, time)); +} + +const mintERC20ABI = [ + { + type: "function", + name: "mint", + inputs: [ + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress" + }, + { + name: "amount", + type: "core::integer::u256" + } + ], + outputs: [], + state_mutability: "external" + } +]; + +export async function mintERC20({ + account, + amount = 1000 +}: { + account: Account | AccountInterface; + amount: number; +}) { + const mintERC20Call: Call = { + contractAddress: config.starknetCurrencyContract, + entrypoint: "mint", + calldata: CallData.compile([account.address, cairo.uint256(amount)]) + }; + + const result = await account.execute(mintERC20Call, [mintERC20ABI]); + + await config.starknetProvider.waitForTransaction(result.transaction_hash, { + retryInterval: 1000 + }); + + return { + transactionHash: result.transaction_hash + }; +} + +const mintERC721ABI = [ + { + type: "function", + name: "mint", + inputs: [ + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress" + }, + { name: "token_uri", type: "core::felt252" } + ], + outputs: [], + state_mutability: "external" + }, + { + type: "function", + name: "get_current_token_id", + inputs: [], + outputs: [{ type: "core::felt252" }], + state_mutability: "view" + } +] as const; + +export async function mintERC721({ + account +}: { + account: Account | AccountInterface; +}) { + const contract = new Contract( + mintERC721ABI, + contracts.nftContract, + config.starknetProvider + ).typedv2(mintERC721ABI); + const tokenId = BigInt(await contract.get_current_token_id()); + const mintCall: Call = { + contractAddress: contracts.nftContract, + entrypoint: "mint", + calldata: CallData.compile({ + recipient: account.address, + token_uri: `https://api.everai.xyz/m/1` + }) + }; + + const { transaction_hash } = await account.execute(mintCall, [mintERC721ABI]); + + await config.starknetProvider.waitForTransaction(transaction_hash, { + retryInterval: 1000 + }); + + return { + tokenId, + tokenAddress: contracts.nftContract + }; +} + +const balanceOfERC20ABI = [ + { + type: "function", + name: "balanceOf", + inputs: [ + { + name: "account", + type: "core::starknet::contract_address::ContractAddress" + } + ], + outputs: [{ type: "core::integer::u256" }], + state_mutability: "view" + } +] as const; + +export async function getBalance({ + account +}: { + account: Account | AccountInterface; +}) { + const contract = new Contract( + balanceOfERC20ABI, + config.starknetCurrencyContract, + config.starknetProvider + ).typedv2(balanceOfERC20ABI); + + const balance = await contract.balanceOf(account.address); + + return balance as bigint; +} diff --git a/packages/test/tsconfig.json b/packages/test/tsconfig.json new file mode 100644 index 000000000..36c235d29 --- /dev/null +++ b/packages/test/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@ark-project/typescript-config/react-library.json", + "include": ["."], + "exclude": ["dist", "build", "node_modules"], + "compilerOptions": { + "jsx": "react", + "declarationDir": "types", + "sourceMap": true, + "outDir": "dist", + "allowSyntheticDefaultImports": true + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c95909a53..ca63afd21 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,7 +58,7 @@ importers: version: 6.11.0 ts-node: specifier: ^10.9.1 - version: 10.9.2(@types/node@20.16.1)(typescript@5.5.4) + version: 10.9.2(@types/node@22.5.5)(typescript@5.5.4) typescript: specifier: ^5.0.0 version: 5.5.4 @@ -134,8 +134,8 @@ importers: packages/deployer: dependencies: commander: - specifier: ^11.1.0 - version: 11.1.0 + specifier: ^12.1.0 + version: 12.1.0 dotenv: specifier: ^16.3.1 version: 16.4.5 @@ -159,8 +159,8 @@ importers: specifier: workspace:* version: link:../typescript-config '@types/node': - specifier: ^20.10.7 - version: 20.16.1 + specifier: ^22.5.4 + version: 22.5.5 tsx: specifier: ^4.11.0 version: 4.17.0 @@ -201,6 +201,9 @@ importers: '@ark-project/core': specifier: workspace:* version: link:../core + '@ark-project/test': + specifier: workspace:* + version: link:../test '@starknet-react/chains': specifier: ^0.1.7 version: 0.1.7 @@ -252,7 +255,7 @@ importers: version: 16.4.5 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.16.1)(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) + version: 29.7.0(@types/node@22.5.5) jest-environment-jsdom: specifier: ^29.7.0 version: 29.7.0 @@ -264,13 +267,83 @@ importers: version: 18.3.1(react@18.3.1) ts-jest: specifier: ^29.1.1 - version: 29.2.5(@babel/core@7.25.2)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(esbuild@0.20.2)(jest@29.7.0(@types/node@20.16.1)(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)))(typescript@5.5.4) + version: 29.2.5(@babel/core@7.25.2)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@22.5.5))(typescript@5.5.4) typescript: specifier: ^5.3.3 version: 5.5.4 vitest: specifier: ^2.0.5 - version: 2.0.5(@types/node@20.16.1)(jsdom@20.0.3) + version: 2.0.5(@types/node@22.5.5)(jsdom@20.0.3) + + packages/test: + dependencies: + '@ark-project/core': + specifier: workspace:* + version: link:../core + '@starknet-react/chains': + specifier: ^0.1.7 + version: 0.1.7 + '@starknet-react/core': + specifier: ^2.8.1 + version: 2.9.0(get-starknet-core@3.3.3(starknet@6.11.0))(react@18.3.1)(starknet@6.11.0) + '@tanstack/react-query': + specifier: ^5.55.4 + version: 5.55.4(react@18.3.1) + get-starknet-core: + specifier: ^3.3.0 + version: 3.3.3(starknet@6.11.0) + react: + specifier: ^18 + version: 18.3.1 + react-dom: + specifier: ^18 + version: 18.3.1(react@18.3.1) + starknet: + specifier: ^6.9.0 + version: 6.11.0 + devDependencies: + '@ark-project/eslint-config': + specifier: workspace:* + version: link:../eslint-config + '@ark-project/typescript-config': + specifier: workspace:* + version: link:../typescript-config + '@testing-library/dom': + specifier: ^10.4.0 + version: 10.4.0 + '@testing-library/jest-dom': + specifier: ^6.5.0 + version: 6.5.0 + '@testing-library/react': + specifier: ^16.0.1 + version: 16.0.1(@testing-library/dom@10.4.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@testing-library/react-hooks': + specifier: ^8.0.1 + version: 8.0.1(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react-test-renderer@18.3.1(react@18.3.1))(react@18.3.1) + '@types/jest': + specifier: ^29.5.5 + version: 29.5.12 + '@types/react': + specifier: ^18 + version: 18.3.4 + dotenv: + specifier: ^16.4.5 + version: 16.4.5 + jest: + specifier: ^29.7.0 + version: 29.7.0 + jest-environment-jsdom: + specifier: ^29.7.0 + version: 29.7.0 + react-test-renderer: + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) + ts-jest: + specifier: ^29.1.1 + version: 29.2.5(@babel/core@7.25.2)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0)(typescript@5.5.4) + typescript: + specifier: ^5.3.3 + version: 5.5.4 packages/typescript-config: {} @@ -1433,6 +1506,9 @@ packages: '@types/node@20.16.1': resolution: {integrity: sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==} + '@types/node@22.5.5': + resolution: {integrity: sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==} + '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -1945,9 +2021,9 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - commander@11.1.0: - resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} - engines: {node: '>=16'} + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} @@ -5823,6 +5899,10 @@ snapshots: dependencies: undici-types: 6.19.8 + '@types/node@22.5.5': + dependencies: + undici-types: 6.19.8 + '@types/normalize-package-data@2.4.4': {} '@types/prop-types@15.7.12': {} @@ -5842,7 +5922,7 @@ snapshots: '@types/ws@8.5.12': dependencies: - '@types/node': 20.12.14 + '@types/node': 20.16.1 '@types/yargs-parser@21.0.3': {} @@ -6462,7 +6542,7 @@ snapshots: dependencies: delayed-stream: 1.0.0 - commander@11.1.0: {} + commander@12.1.0: {} commander@4.1.1: {} @@ -6487,6 +6567,21 @@ snapshots: - supports-color - ts-node + create-jest@29.7.0(@types/node@22.5.5): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@22.5.5) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + create-require@1.1.1: {} cross-spawn@5.1.0: @@ -6972,7 +7067,7 @@ snapshots: eslint: 8.57.0 optionalDependencies: '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4) - jest: 29.7.0(@types/node@20.16.1)(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) + jest: 29.7.0 transitivePeerDependencies: - supports-color - typescript @@ -7789,6 +7884,25 @@ snapshots: - babel-plugin-macros - supports-color + jest-cli@29.7.0: + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@20.16.1)(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@20.16.1)(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest-cli@29.7.0(@types/node@20.16.1)(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) @@ -7808,6 +7922,25 @@ snapshots: - supports-color - ts-node + jest-cli@29.7.0(@types/node@22.5.5): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@22.5.5) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@22.5.5) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest-config@29.7.0(@types/node@20.16.1)(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)): dependencies: '@babel/core': 7.25.2 @@ -7839,6 +7972,36 @@ snapshots: - babel-plugin-macros - supports-color + jest-config@29.7.0(@types/node@22.5.5): + dependencies: + '@babel/core': 7.25.2 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.25.2) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.5.5 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-diff@29.7.0: dependencies: chalk: 4.1.2 @@ -8069,6 +8232,18 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 + jest@29.7.0: + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest@29.7.0(@types/node@20.16.1)(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) @@ -8081,6 +8256,18 @@ snapshots: - supports-color - ts-node + jest@29.7.0(@types/node@22.5.5): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4)) + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@22.5.5) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jju@1.4.0: {} joycon@3.1.1: {} @@ -9179,6 +9366,44 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.25.2) esbuild: 0.20.2 + ts-jest@29.2.5(@babel/core@7.25.2)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@22.5.5))(typescript@5.5.4): + dependencies: + bs-logger: 0.2.6 + ejs: 3.1.10 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@22.5.5) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.3 + typescript: 5.5.4 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.25.2 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.25.2) + + ts-jest@29.2.5(@babel/core@7.25.2)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0)(typescript@5.5.4): + dependencies: + bs-logger: 0.2.6 + ejs: 3.1.10 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0 + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.3 + typescript: 5.5.4 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.25.2 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.25.2) + ts-mixer@6.0.4: {} ts-node@10.9.2(@types/node@20.16.1)(typescript@5.5.4): @@ -9199,6 +9424,24 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + ts-node@10.9.2(@types/node@22.5.5)(typescript@5.5.4): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.5.5 + acorn: 8.12.1 + acorn-walk: 8.3.3 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.5.4 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -9393,13 +9636,13 @@ snapshots: - utf-8-validate - zod - vite-node@2.0.5(@types/node@20.16.1): + vite-node@2.0.5(@types/node@22.5.5): dependencies: cac: 6.7.14 debug: 4.3.6(supports-color@5.5.0) pathe: 1.1.2 tinyrainbow: 1.2.0 - vite: 5.4.3(@types/node@20.16.1) + vite: 5.4.3(@types/node@22.5.5) transitivePeerDependencies: - '@types/node' - less @@ -9411,16 +9654,16 @@ snapshots: - supports-color - terser - vite@5.4.3(@types/node@20.16.1): + vite@5.4.3(@types/node@22.5.5): dependencies: esbuild: 0.21.5 postcss: 8.4.45 rollup: 4.21.0 optionalDependencies: - '@types/node': 20.16.1 + '@types/node': 22.5.5 fsevents: 2.3.3 - vitest@2.0.5(@types/node@20.16.1)(jsdom@20.0.3): + vitest@2.0.5(@types/node@22.5.5)(jsdom@20.0.3): dependencies: '@ampproject/remapping': 2.3.0 '@vitest/expect': 2.0.5 @@ -9438,11 +9681,11 @@ snapshots: tinybench: 2.9.0 tinypool: 1.0.1 tinyrainbow: 1.2.0 - vite: 5.4.3(@types/node@20.16.1) - vite-node: 2.0.5(@types/node@20.16.1) + vite: 5.4.3(@types/node@22.5.5) + vite-node: 2.0.5(@types/node@22.5.5) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 20.16.1 + '@types/node': 22.5.5 jsdom: 20.0.3 transitivePeerDependencies: - less