Example: Tests


import assert from "node:assert/strict";
import { describe, it } from "node:test";

import { network } from "hardhat";
import { parseEther } from "viem";

const { viem } = await network.connect();
const publicClient = await viem.getPublicClient();

describe("Crowdfunding", function () {
  it("Accounts can donate and vote", async function () {
    const [owner, beneficiary1, beneficiary2, donor1, donor2] = await viem.getWalletClients();
    const crowdfunding = await viem.deployContract("Crowdfunding");

    await crowdfunding.write.addProject(["Project 1", beneficiary1.account.address]);
    await crowdfunding.write.addProject(["Project 2", beneficiary2.account.address]);
    
    await crowdfunding.write.donate({ value: parseEther("1"), account: donor1.account.address });
    await crowdfunding.write.donate({ value: parseEther("3"), account: donor2.account.address });
    
    await crowdfunding.write.vote([1n], { account: donor1.account.address });
    await crowdfunding.write.vote([1n], { account: donor2.account.address });

    await crowdfunding.write.closeVoting({ account: owner.account.address });

    const winningProject = await crowdfunding.read.getWinningProject();
    assert.deepStrictEqual(winningProject, [1n, "Project 2"]);
    await crowdfunding.write.withdraw({ account: beneficiary2.account.address });
  });

  it("Only the owner can add projects", async function () {
    const [owner, otherUser] = await viem.getWalletClients();
    const crowdfunding = await viem.deployContract("Crowdfunding");

    await crowdfunding.write.addProject(
      ["Project 1", otherUser.account.address], 
      { account: owner.account.address }
    );

    await assert.rejects(
      crowdfunding.write.addProject(["Project 2", otherUser.account.address], { account: otherUser.account.address })
    );
  });

  it("Only donors can vote", async function () {
    const [owner, beneficiary, donor] = await viem.getWalletClients();
    const crowdfunding = await viem.deployContract("Crowdfunding");

    await crowdfunding.write.addProject(["Project 1", beneficiary.account.address]);
    
    await crowdfunding.write.donate({ value: parseEther("1"), account: donor.account.address });
    await crowdfunding.write.vote([0n], { account: donor.account.address });

    await assert.rejects(
      crowdfunding.write.vote([0n], { account: beneficiary.account.address })
    );

    await crowdfunding.write.closeVoting({ account: owner.account.address });
  });

  it("Donors can only vote once", async function () {
    const [owner, beneficiary, donor] = await viem.getWalletClients();
    const crowdfunding = await viem.deployContract("Crowdfunding");

    await crowdfunding.write.addProject(["Project 1", beneficiary.account.address]);
    await crowdfunding.write.addProject(["Project 2", beneficiary.account.address]);
    
    await crowdfunding.write.donate({ value: parseEther("1"), account: donor.account.address });
    await crowdfunding.write.vote([0n], { account: donor.account.address });

    // can't vote for the same project again
    await assert.rejects(
      crowdfunding.write.vote([0n], { account: donor.account.address })
    );

    // can't vote for a different project
    await assert.rejects(
      crowdfunding.write.vote([1n], { account: donor.account.address })
    );

    await crowdfunding.write.closeVoting({ account: owner.account.address });
  });

  it("Only the owner can close the vote", async function () {
    const [owner, beneficiary] = await viem.getWalletClients();
    const crowdfunding = await viem.deployContract("Crowdfunding");

    await crowdfunding.write.addProject(["Project 1", beneficiary.account.address]);

    await assert.rejects(
      crowdfunding.write.closeVoting({ account: beneficiary.account.address })
    );
    
    await crowdfunding.write.closeVoting({ account: owner.account.address });
  });

  it("Event should fire when a new project is added", async function () {
    const [owner, user1] = await viem.getWalletClients();
    const crowdfunding = await viem.deployContract("Crowdfunding");

    await viem.assertions.emitWithArgs(
      crowdfunding.write.addProject(["Project 1", user1.account.address]),
      crowdfunding,
      "NewProjectAdded",
      [0n, "Project 1"]
    );
  });

  it("Event should fire when a new project is added (second approach)", async function () {
    const [owner, user1] = await viem.getWalletClients();
    const crowdfunding = await viem.deployContract("Crowdfunding");

    const blockNumber = await publicClient.getBlockNumber();
    await crowdfunding.write.addProject(["Project 1", user1.account.address]);

    const events = await publicClient.getContractEvents({
      address: crowdfunding.address,
      abi: crowdfunding.abi,
      eventName: "NewProjectAdded",
      fromBlock: blockNumber
    });

    assert.equal(events.length, 1);
    assert.equal(events[0].args.name, "Project 1");
  });
});