Skip to content

Commit

Permalink
Configured testing environment (#20)
Browse files Browse the repository at this point in the history
* Configured vitest to work with snarkyjs and worker

* Added NPM license

* Defined basic tests, improved worker for tests
  • Loading branch information
silimarius authored Aug 21, 2023
1 parent e767089 commit ff3d316
Show file tree
Hide file tree
Showing 10 changed files with 365 additions and 43 deletions.
11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"eslint:ci": "eslint --no-eslintrc --c .eslintrc.json '*.{js,json}' '{src,tests}/**/*.{ts,tsx}'",
"pretest": "tsc --noEmit",
"test": "vitest --ui --coverage",
"test:ci": "vitest"
"test:ci": "vitest",
"test:debug": "DEBUG=vitest:web-worker vitest"
},
"engines": {
"node": ">=12.7.0"
Expand All @@ -57,13 +58,15 @@
"@rollup/plugin-commonjs": "^25.0.3",
"@rollup/plugin-node-resolve": "^15.1.0",
"@rollup/plugin-typescript": "^11.1.2",
"@testing-library/jest-dom": "^6.0.1",
"@testing-library/react": "^14.0.0",
"@types/react": "^18.2.15",
"@types/rollup-plugin-peer-deps-external": "^2.2.1",
"@typescript-eslint/eslint-plugin": "^6.1.0",
"@typescript-eslint/parser": "^6.1.0",
"@vitest/coverage-v8": "^0.34.1",
"@vitest/ui": "^0.34.1",
"@vitest/web-worker": "^0.34.2",
"eslint": "^8.21.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-alias": "^1.1.2",
Expand All @@ -79,9 +82,12 @@
"rollup-plugin-peer-deps-external": "^2.2.4",
"snarkyjs": "^0.12.1",
"typescript": "^5.1.6",
"vite": "^4.4.9",
"vite-plugin-top-level-await": "^1.3.1",
"vite-plugin-wasm": "^3.2.2",
"vitest": "^0.34.1",
"vitest-canvas-mock": "^0.3.3",
"vitest-fetch-mock": "^0.2.2",
"zod": "^3.21.4",
"zustand": "^4.3.9"
},
Expand All @@ -91,5 +97,6 @@
"snarkyjs": ">=0.11.0",
"zod": ">=3.21.0",
"zustand": ">=4.3.9"
}
},
"license": "MIT"
}
11 changes: 11 additions & 0 deletions setupVitest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import matchers from "@testing-library/jest-dom/matchers";
import { expect, vi } from "vitest";
import createFetchMock from "vitest-fetch-mock";
import "vitest-canvas-mock";

expect.extend(matchers);

const fetchMocker = createFetchMock(vi);

// sets globalThis.fetch and globalThis.fetchMock to our mocked version
fetchMocker.enableMocks();
14 changes: 12 additions & 2 deletions src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const createZKState = <T extends object>(
const useZKStore = create<T>(createState);
const zkAppWorkerClient = new ZkAppWorkerClient(worker);

const useInitZkStore = () => {
const useInitZKStore = () => {
const isInitialized = useContractStore((state) => state.isInitialized);

const setProof = useContractStore((state) => state.setProof);
Expand Down Expand Up @@ -80,5 +80,15 @@ export const createZKState = <T extends object>(
};
};

return { useInitZkStore, useZKStore, useGetLatestProof };
const useProof = () => useContractStore((state) => state.proof);
const useIsInitialized = () =>
useContractStore((state) => state.isInitialized);

return {
useInitZKStore,
useZKStore,
useGetLatestProof,
useProof,
useIsInitialized,
};
};
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export * from "./store";
export * from "./hooks";
export { initZKWorker } from "./zkAppWorker";
51 changes: 30 additions & 21 deletions src/zkAppWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
} from "./types";
import { INITIAL_STATE, MERKLE_TREE_HEIGHT, MerkleWitness20 } from "./utils";

let post = postMessage;

const state: WorkerState = {
tree: new MerkleTree(MERKLE_TREE_HEIGHT),
transitionIndex: 0n, // should increment on state transition and is used to index the tree
Expand Down Expand Up @@ -43,7 +45,7 @@ setInterval(() => {
id: 0,
data: proof.toJSON(),
};
postMessage(message);
post(message);

state.executingUpdate = false;
})
Expand Down Expand Up @@ -136,28 +138,35 @@ export interface ZkappWorkerReponse {
data: unknown;
}

export const initZKWorker = () => {
const onMessage = async (event: MessageEvent<ZkappWorkerRequest>) => {
try {
const returnData = await workerFunctions[event.data.fn](event.data.args);

const message: ZkappWorkerReponse = {
resType: "function-call",
id: event.data.id,
data: returnData,
};
post(message);
} catch (error) {
console.warn("Worker Error:", error);
}
};

/**
* Sets the message listener in the worker. Read the README for informations on how to use in your own code.
*
* @param testRef reference to the worker file when testing the library, do not use when developing
*/
export const initZKWorker = (testRef?: Window & typeof globalThis) => {
console.info("[zk-states worker] adding event listener");

addEventListener(
"message",
async (event: MessageEvent<ZkappWorkerRequest>) => {
try {
const returnData = await workerFunctions[event.data.fn](
event.data.args,
);

const message: ZkappWorkerReponse = {
resType: "function-call",
id: event.data.id,
data: returnData,
};
postMessage(message);
} catch (error) {
console.warn("Worker Error:", error);
}
},
);
if (testRef) {
post = testRef.postMessage;
testRef.onmessage = onMessage;
} else {
onmessage = onMessage;
}

console.info("[zk-states worker] added worker service listener.");
};
75 changes: 66 additions & 9 deletions tests/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,71 @@
import React from "react";
import { render } from "@testing-library/react";
import { it } from "vitest";
import "@vitest/web-worker";
import { render, waitFor } from "@testing-library/react";
import type { JsonProof } from "snarkyjs";
import { describe, expect, it } from "vitest";
import { createZKState } from "zk-states";

// TODO: fix wasm error when importing zk-states library
it("initializes state in a react component", async () => {
const Component = () => {
return <>hello world!</>;
};
interface ZKState {
num: number;
incNum: () => void;
}

const { findByText } = render(<Component />);
const {
useInitZKStore,
useGetLatestProof,
useZKStore,
useProof,
useIsInitialized,
} = createZKState<ZKState>(
new Worker(new URL("./worker.ts", import.meta.url), {
type: "module",
}),
(set) => ({
num: 0,
incNum: () => set((state) => ({ num: state.num + 1 })),
}),
);

await findByText("hello world!");
describe("createZKState", () => {
it("returns the expected hooks", () => {
expect(useInitZKStore).toBeDefined();
expect(useGetLatestProof).toBeDefined();
expect(useProof).toBeDefined();
expect(useIsInitialized).toBeDefined();
});

it("renders the correct initial global state value in a React component", async () => {
const expectedValue = 0;

const Component = () => {
const num = useZKStore((state) => state.num);
return <div>num: {num}</div>;
};

const { findByText } = render(<Component />);

await findByText(`num: ${expectedValue}`);
});

it("correctly initializes ZK store with useInitZKStore", async () => {
let proof: JsonProof | undefined;

const Component = () => {
const isInitialized = useIsInitialized();
proof = useProof();
useInitZKStore();
return <div>isInitialized: {isInitialized ? "true" : "false"}</div>;
};

const { getByText } = render(<Component />);

await waitFor(
() => {
expect(getByText("isInitialized: true")).toBeInTheDocument();
},
{ timeout: 300000 },
);

expect(proof).toBeDefined();
});
});
5 changes: 4 additions & 1 deletion tests/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"extends": "../tsconfig.json",
"include": ["./**/*"]
"include": ["./**/*"],
"compilerOptions": {
"types": ["@testing-library/jest-dom"]
}
}
3 changes: 3 additions & 0 deletions tests/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { initZKWorker } from "zk-states";

initZKWorker(self);
37 changes: 34 additions & 3 deletions vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
import { resolve } from "path";
import topLevelAwait from "vite-plugin-top-level-await";
import wasm from "vite-plugin-wasm";
import { defineConfig } from "vitest/config";

export default defineConfig({
plugins: [wasm(), topLevelAwait()],
plugins: [
wasm(),
topLevelAwait(),
{
name: "isolation",
configureServer(server) {
server.middlewares.use((_req, res, next) => {
res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
next();
});
},
},
],
resolve: {
alias: [
{ find: /^zk-states$/, replacement: "./src/index.ts" },
{ find: /^zk-states(.*)$/, replacement: "./src/$1.ts" },
{
find: /^zk-states$/,
replacement: resolve(__dirname, "src/index.ts"),
},
{ find: /^zk-states(.*)$/, replacement: resolve(__dirname, "src/$1.ts") },
],
},
test: {
Expand All @@ -22,5 +39,19 @@ export default defineConfig({
reporter: ["text", "json", "html", "text-summary"],
reportsDirectory: "./coverage/",
},
setupFiles: ["./setupVitest.js", "@vitest/web-worker"],
server: {
deps: {
inline: ["vitest-canvas-mock"],
},
},
// For this config, check https://github.com/vitest-dev/vitest/issues/740
threads: false,
environmentOptions: {
jsdom: {
resources: "usable",
},
},
testTimeout: 600000, // 10 minutes (some operations are pretty slow, so keep it high)
},
});
Loading

0 comments on commit ff3d316

Please sign in to comment.