Skip to content

Conversation

@jojedev
Copy link
Collaborator

@jojedev jojedev commented Oct 13, 2025

Summary by CodeRabbit

  • New Features

    • Interactive 5‑star rating UI for job approvals.
  • Improvements

    • Switched server data layer to a new GraphQL client and enabled dynamic rendering for key pages.
    • Refined chat message styling, simplified SVG icons, reduced console noise, and added cache invalidation for fresher data.
  • Documentation

    • README updated with npm instructions and new status badges; added a PR template.
  • Tests

    • Large set of new unit and integration tests across APIs, hooks, components, and pages.
  • Chores

    • Automated dependency updates, coverage reporting, and Lighthouse CI workflows added.

kasumi-1 and others added 28 commits October 1, 2025 23:50
- Remove unnecessary console.log statements from production code
- Reorder and organize package.json dependencies
- Add missing dev dependency @rollup/rollup-darwin-arm64
- Upgrade React, Next.js, and related dependencies to React 19 RC versions
- Update type definitions and component implementations for React 19 compatibility
- Remove deprecated experimental instrumentation hook from Next config
- Fix ref initialization and type issues in several components
- Update params handling in server components to support async params
- Add GitHub Actions workflow for website testing with coverage reporting
- Add pull request template with comprehensive checklist
- Add Jest configuration and setup for frontend testing
- Add extensive test coverage for components, hooks, and API routes
- Add mock implementations for wagmi, Apollo, and contract dependencies
- Update README with testing instructions and coverage badge
- Add test utilities and mock data for consistent test execution
- Add GitHub Actions workflow for running contract tests with coverage
- Update EACCToken tests to use correct import paths and remove unused imports
- Remove obsolete EACCBar and MarketplaceV2 test files
- Fix publishMediaToIpfs function calls in utils tests
- Remove IPFS upload secret check from utils tests
Moved all test files to __tests__ directory and updated import paths accordingly. This change restructures the testing organization to be more consistent and maintainable.
- Configure Vitest for unit and integration testing with coverage support
- Add mock event data fixtures for job-related events and entities
- Create unit tests for data models, event types, and job event processing
- Add skipped integration tests for database operations with setup instructions
- Update package.json with test scripts and dev dependencies
This commit replaces Apollo Client with URQL across the application for several key reasons:
- Better integration with Next.js server components
- Automatic cache invalidation without manual resets
- Improved performance for server-side rendering
- Reduced bundle size by removing Apollo dependencies

The migration includes:
- Removed @apollo/client and related dependencies
- Added urql, wonka, and graphql-tag as dependencies
- Updated all GraphQL hooks and components to use URQL
- Rewrote server-side data fetching with createServerUrqlClient
- Updated testing utilities to mock URQL instead of Apollo
- Enhanced cache configuration with ssrExchange for better SSR support

Key benefits:
- Eliminates manual cache reset logic previously required with Apollo
- Server components now fetch data more efficiently
- Simplified testing setup with URQL's more straightforward mocking
- Better TypeScript support with automatic BigInt handling
- Upgrade multiple package dependencies including React, Next.js, and UI libraries
- Fix Blob construction in useFetchAvatar and useMediaDownloadHandler hooks
- Refactor Markdown components in job chat event displays
- Update class names for consistent styling in message components
- Remove @mui/material and @mui/icons-material packages
- Replace MUI Rating component with custom star rating implementation
- Remove MUI LinearProgress component
- Replace MUI SvgIcon wrappers with direct SVG elements
- Update package.json to reflect removed dependencies
- Removed @radix-ui/react-separator and @radix-ui/react-slider
- Removed @urql/exchange-graphcache
- Dependencies are no longer used in the project and can be safely removed to reduce bundle size
- Replace separate test workflows with single coverage workflow
- Use matrix strategy to handle both packages
- Update Node.js version to 22
- Handle different coverage file locations
- Enable parallel coverage uploads to Coveralls
- Add workflow completion step for Coveralls
- Removed conditional logic for running coverage commands
- Standardized on `npm run test:coverage` for both packages
- Reduced complexity in workflow step execution
The workflow now only runs on master branch pushes and pull requests.
This change ensures Lighthouse tests are only triggered for the primary
branch, avoiding duplicate checks on feature branches.
- Configure .solcover.js to skip specific contracts and directories
- Migrate jest.config.ts to .js format with commonjs exports
- Update @types/react dependencies to stable versions
- Configure mocha-junit-reporter for contract tests in CI
- Set up jest-junit reporter for website tests
- Update coverage scripts to create test-results directories
- Add GitHub Actions step to publish test results
- Ignore test-results directories in both packages
- Replace all Jest mocks with Vitest equivalents
- Update test utilities and setup files
- Configure Vitest with proper aliases and coverage settings
- Remove Jest configuration and dependencies
- Update package scripts to use Vitest commands
- Replace MockedProvider with custom URQL mock implementation
- Remove redundant Apollo imports and ReactNode wrappers
- Standardize mock query structure and wrapper creation
- Update test assertions to match new mock data structure
- Remove overly specific assertions that duplicated mock data
- Fix localStorage mock implementation in useUserNotifications.test.tsx
- Update NextResponse mocks to properly handle text() and json() methods
- Simplify hook tests by removing unnecessary loading state checks
- Fix parameter handling in subsquid hooks tests
- Update component tests to use direct prop passing instead of hooks
- Improve urql mock client implementation for better query matching
- Skip several tests that require complex mocking setups
- Fix type mismatches in test data structures
- Update test utility providers and wrappers
 This commit restores real-time UI updates that were lost during the Apollo Client to URQL
   migration (b9bc664). The original migration incorrectly assumed URQL would
  automatically invalidate cache on external events, but URQL requires explicit cache
  invalidation triggers.

  Root Cause

  The Apollo to URQL migration removed three critical cache invalidation mechanisms:
  1. 60-second periodic cache reset timer
  2. Push notification-triggered cache resets (client.resetStore())
  3. Post-transaction cache resets with 3s + 6s delays

  All were removed with comments claiming "URQL handles this automatically" - which was
  false.

  Solution

  Implemented context-based cache invalidation system using React Context:

  Created:
  - CacheInvalidationContext.tsx - Provides timestamp-based invalidation via invalidate()
  function

  Modified:
  - providers.tsx - Wrapped app with CacheInvalidationProvider
  - useSwResetMessage.tsx - Calls invalidate() when push notifications arrive
  - useWriteContractWithNotifications.ts - Calls invalidate() 3s after transaction confirms
   (allows Subsquid indexing time)
  - useUserNotifications.tsx, useJobEvents.tsx, useJob.tsx - Added cache invalidation
  context with cache-and-network request policy

  Key Benefits:
  - Push notifications now trigger UI updates without page refresh
  - Own messages appear 3 seconds after sending (Subsquid indexing delay)
  - Cleaner architecture using React Context vs manual cache resets
  - Works across all browsers (Chrome requires notification permissions enabled)

  Technical Details

  When invalidate() is called, it updates a timestamp in React Context. URQL queries
  include this timestamp in their context parameter. When the timestamp changes, URQL
  treats it as a new query request and refetches with network-only behavior, bypassing the
  cache.
@vercel
Copy link

vercel bot commented Oct 13, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
docs-effectiveacceleration Ready Ready Preview Comment Oct 13, 2025 6:36pm
effectiveacceleration.ai Ready Ready Preview Comment Oct 13, 2025 6:36pm

@coderabbitai
Copy link

coderabbitai bot commented Oct 13, 2025

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • contract/package-lock.json is excluded by !**/package-lock.json

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Replace Apollo with server-side URQL across website pages, providers, and hooks; add a cache-invalidation context; introduce CI workflows (coverage, Lighthouse) and Dependabot; expand test infra with many unit/integration tests and mocks; update contracts test tooling and several UI/typing/styling tweaks.

Changes

Cohort / File(s) Summary
Repo meta & CI
README.md, .github/PULL_REQUEST_TEMPLATE.md, .github/dependabot.yml, .github/workflows/coverage.yml, .github/workflows/lighthouse.yml
Add PR template and badges; add Dependabot config; add Coverage and Lighthouse CI workflows.
Contracts config & tests
contract/.solcover.js, contract/hardhat.config.base.ts, contract/package.json, contract/.gitignore, contract/README.md
Add solcover skip list, mocha-junit reporter support and test:coverage script, ignore test-results, and switch README install commands to npm.
Contracts tests removed/updated
contract/test/eaccbar.test.ts (deleted), contract/test/marketplacev2.test.ts (deleted), contract/test/eacctoken.test.ts, contract/test/utils.test.ts
Remove large EACCBar & MarketplaceV2 suites; refactor/streamline EACCToken tests; update utils tests to changed publishMediaToIpfs signature.
Subsquid tests & config
subsquid/package.json, subsquid/vitest.config.ts, subsquid/tests/*, subsquid/tests/fixtures/mock-events.ts
Add Vitest config and scripts; add test scaffolds and fixtures including mock event payloads.
Website deps & runtime config
website/package.json, website/next.config.mjs, website/lighthouserc.js, website/.gitignore, website/README.md
Major dependency/devDependency updates and new test scripts; add transpilePackages, Lighthouse config, ignore test-results, and npm-based README edits.
Providers & cache invalidation
website/src/app/providers.tsx, website/src/contexts/CacheInvalidationContext.tsx
Remove Apollo setup; add Urql provider and CacheInvalidationProvider with useCacheInvalidation hook.
Server pages → URQL
website/src/app/arbitrators/.../page.tsx, website/src/app/jobs/[id]/page.tsx, website/src/app/users/.../page.tsx, website/src/app/users/page.tsx, website/src/app/arbitrators/page.tsx, website/src/app/open-job-list/page.tsx, website/src/app/post-job/page.tsx
Replace Apollo client calls with server URQL client (.query(...).toPromise()); adjust generateMetadata and page component params to Promise-based where applicable; add dynamic='force-dynamic' exports for some pages.
Hooks → URQL & return-shape changes
website/src/hooks/subsquid/*.tsx, website/src/hooks/subsquid/useJob.tsx, website/src/hooks/subsquid/queries.ts
Migrate many hooks from Apollo useQuery to urql useQuery (object API), replace skip→pause, map result.fetching/error, standardize returns to { data, loading, error }, and add cache invalidation usage in useJob.
UI, components & small typings
website/src/app/jobs/[id]/JobActions/ApproveButton.tsx, .../PostMessageButton.tsx, .../UpdateButton.tsx, JobChat/Events/*, StatusStates/*, JobChatDetails.tsx, src/components/*, src/components/TokenSelector/icons/*
Replace MUI Rating with custom star UI; move Markdown styling to parent containers; small ref/typing adjustments; remove console logs; replace MUI SvgIcon wrappers with inline SVGs.
Website tests & mocks
website/__tests__/**/*, website/__tests__/setup/mocks/*, website/__tests__/setup/test-utils.tsx
Add many unit and integration tests (API endpoints, components, hooks, pages) and test utilities/mocks for URQL, Apollo, wagmi, and contracts plus a custom test render wrapper.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant NextServer as Next.js (server page)
  participant Cache as unstable_cache
  participant URQL as Server URQL Client
  participant GQL as GraphQL API

  User->>NextServer: Request page (e.g. /jobs/[id])
  NextServer->>Cache: getCachedJobData(id)
  alt cache miss
    Cache->>URQL: query(GET_JOB, { id }).toPromise()
    URQL->>GQL: GraphQL request
    GQL-->>URQL: data
    URQL-->>Cache: return result.data
    Cache-->>NextServer: store + return data
  else cache hit
    Cache-->>NextServer: cached data
  end
  NextServer-->>User: Rendered HTML with data
  Note over URQL,NextServer: Hooks may include _invalidationTimestamp in query context for invalidation
Loading
sequenceDiagram
  autonumber
  participant GitHub
  participant Workflow as GA Coverage Matrix
  participant Runner as Ubuntu runner
  participant Coveralls
  participant UTR as Test Results Action

  GitHub->>Workflow: push / pull_request
  Workflow->>Runner: start matrix job (per-package)
  Runner->>Runner: checkout + setup node
  Runner->>Runner: npm ci (package)
  Runner->>Runner: npm run test:coverage
  Runner->>UTR: publish unit test results (continue-on-error)
  Runner->>Runner: locate lcov.info (multiple candidate paths)
  Runner->>Coveralls: upload coverage (parallel)
  Workflow->>Coveralls: parallel-finished
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Poem

I’m a rabbit with a tiny key,
Swapped Apollo for Urql under the tree.
Tests like clover sprout near my trail,
CI hums while badges tell the tale.
Hop, commit, release — nibble the scale. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The title “Test pr 253” is overly generic and does not describe any of the extensive CI workflows, test scaffolding, or configuration changes introduced in this pull request, so it fails to convey the primary purpose of the changes. Please update the title to a concise, clear summary of the main change, for example “Add GitHub Actions workflows for coverage and Lighthouse CI with test scaffolding across modules.”
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 43

🧹 Nitpick comments (47)
website/__tests__/api/total_supply.test.ts (1)

17-37: Remove redundant ethers mocking.

The mock defines identical implementations at both the nested level (ethers.Contract, lines 20-22) and top level (Contract, lines 29-31). This duplication is unnecessary and makes the mock harder to maintain.

Apply this diff to remove the redundant nested mocking:

-// Mock ethers
-vi.mock('ethers', () => ({
-  ethers: {
-    Contract: vi.fn().mockImplementation(() => ({
-      totalSupply: vi.fn().mockResolvedValue(BigInt('10000000000000000000000')),
-      balanceOf: vi.fn().mockResolvedValue(BigInt('1000000000000000000000')),
-    })),
-    JsonRpcProvider: vi.fn(),
-    formatUnits: vi.fn((value) => (Number(value) / 1e18).toString()),
-    formatEther: vi.fn((value) => (Number(value) / 1e18).toString()),
-    parseEther: vi.fn((value) => BigInt(value) * BigInt(10 ** 18)),
-  },
+// Mock ethers
+vi.mock('ethers', () => ({
   Contract: vi.fn().mockImplementation(() => ({
     totalSupply: vi.fn().mockResolvedValue(BigInt('10000000000000000000000')),
     balanceOf: vi.fn().mockResolvedValue(BigInt('1000000000000000000000')),
   })),
   JsonRpcProvider: vi.fn(),
   formatUnits: vi.fn((value) => (Number(value) / 1e18).toString()),
   formatEther: vi.fn((value) => (Number(value) / 1e18).toString()),
   parseEther: vi.fn((value) => BigInt(value) * BigInt(10 ** 18)),
 }));
contract/test/eacctoken.test.ts (2)

16-27: Avoid unknown cast; use typed factory and avoid name shadowing.

unknown as EACCToken hides type errors and const EACCToken = ... shadows the imported type. Prefer the generated factory for type safety.

Apply:

-import { EACCToken } from "../typechain-types";
+import { EACCToken, EACCToken__factory } from "../typechain-types";
@@
-  async function deployTokenFixture(): Promise<FixtureReturnType> {
+  async function deployTokenFixture(): Promise<FixtureReturnType> {
     const [deployer, alice, bob, charlie] = await ethers.getSigners();
 
-    const EACCToken = await ethers.getContractFactory("EACCToken");
-    const eaccToken = await EACCToken.deploy(
+    const factory = (await ethers.getContractFactory("EACCToken")) as EACCToken__factory;
+    const eaccToken = await factory.deploy(
       "Effective Acceleration Token",
       "EACC",
       ethers.parseEther("1000000")
-    ) as unknown as EACCToken;
+    );

167-170: Confirm revert reason string for multitransfer length check.

Assert uses "Invalid length". Ensure the contract reverts with that exact message (not a custom error or different text). If not, switch to a generic revert or the precise reason.

Alternative:

-).to.be.revertedWith("Invalid length");
+).to.be.reverted; // or .to.be.revertedWithCustomError(eaccToken, "YourCustomError")
website/__tests__/hooks/subsquid/useWorkerApplications.test.tsx (1)

1-44: LGTM! Consider enhancing test assertions.

The test correctly validates the basic functionality of the useWorkerApplications hook using URQL mocks. The synchronous assertion pattern (checking loading=false immediately) aligns with the fromValue mock strategy used in the test utilities.

To improve test robustness, consider adding assertions for:

  • Job data structure validation (e.g., expect(result.current.data[0].id).toBe('1'))
  • Error handling scenarios
  • Empty results case
  it('should fetch jobs worker has applied to', async () => {
    const { result } = renderHook(
      () => useWorkerApplications('0xWorker1'),
      { wrapper }
    );

    // With synchronous mocks using fromValue, data is available immediately
    expect(result.current.loading).toBe(false);
    expect(result.current.data).toHaveLength(1);
+   expect(result.current.data[0].id).toBe('1');
+   expect(result.current.data[0].title).toBe('Job Title');
+   expect(result.current.error).toBeUndefined();
  });
website/__tests__/hooks/subsquid/useReviews.test.tsx (1)

33-44: Consider testing the computed review statistics.

The useReviews hook includes review statistics calculation (totalReviews, positiveReviews, negativeReviews, positiveReviewPercentage, actualAverageRating) that are part of the returned value. The current test only verifies basic data presence but doesn't validate these computed fields.

Consider adding assertions to verify the statistics:

     expect(result.current.loading).toBe(false);
     expect(result.current.data).toHaveLength(1);
     expect(result.current.data?.[0].rating).toBe(5);
+    expect(result.current.totalReviews).toBe(1);
+    expect(result.current.positiveReviews).toBe(1);
+    expect(result.current.actualAverageRating).toBe(5);
   });
 });

You might also add test cases for edge scenarios like empty reviews or mixed ratings to ensure the statistics computation is correct.

website/__tests__/lib/tokens.test.ts (1)

40-45: LGTM with optional URL validation improvement.

The test suite is comprehensive and well-structured. The ID validation regex on Line 43 correctly matches Ethereum addresses. The URL pattern (^https?:\/\/) is permissive and will match any string starting with http:// or https://, which may be intentional for flexibility.

If stricter URL validation is desired, consider using a more complete pattern:

-      expect(token.id).toMatch(/^0x[a-fA-F0-9]{40}$|^https?:\/\//);
+      expect(token.id).toMatch(/^0x[a-fA-F0-9]{40}$|^https?:\/\/.+/);

This ensures URLs have at least one character after the protocol.

website/__tests__/api/circulating_supply.test.ts (1)

69-77: Incomplete error handling test.

The test acknowledges it doesn't properly validate error paths (line 70-71) and only checks that a response exists. This provides minimal value and could create a false sense of coverage.

Either implement the error path test or remove it to avoid misleading coverage metrics:

-  it('should handle errors gracefully', async () => {
-    // This test requires mocking the shared module which is complex
-    // Skip for now as it tests error handling paths
-    const response = await GET();
-
-    // Should return valid response even in error cases
-    expect(response).toBeDefined();
-    expect(response.status).toBeDefined();
-  });
website/__tests__/lib/utils.test.ts (2)

66-68: absoluteUrl should normalize slashes (test currently encodes a bug)

Concatenate base + path without ensuring a slash yields https://example.comabout. Prefer normalization and update the test to expect https://example.com/about.

Apply this test change:

-  it('should handle paths without leading slash', () => {
-    expect(absoluteUrl('about')).toBe('https://example.comabout');
-  });
+  it('should handle paths without leading slash', () => {
+    expect(absoluteUrl('about')).toBe('https://example.com/about');
+  });

And implement normalization in website/src/lib/utils.ts (outside this diff):

export function absoluteUrl(path: string) {
  const base = process.env.NEXT_PUBLIC_APP_URL || '';
  const baseNormalized = base.endsWith('/') ? base.slice(0, -1) : base;
  const pathNormalized = path.startsWith('/') ? path : `/${path}`;
  return `${baseNormalized}${pathNormalized}`;
}

151-183: Mock Sentry to avoid noisy/erroring calls during tests

isImageValid calls Sentry.captureException on fetch errors. Mock it to prevent side effects/log noise.

Add near the top of this file:

vi.mock('@sentry/nextjs', () => ({ captureException: vi.fn() }));
website/src/app/users/[address]/page.tsx (1)

83-89: URQL result handling: check for errors before using data

Guard against GraphQL/Network errors to avoid silent null metadata on upstream failures; log or handle result.error.

-      const result = await client
+      const result = await client
         .query(GET_USER_QUERY, { address: checksummedAddress })
         .toPromise();
-      const data = result.data;
+      if (result.error) {
+        console.error('URQL error fetching user data:', result.error);
+        return null;
+      }
+      const data = result.data;
website/src/app/arbitrators/[address]/page.tsx (1)

49-53: URQL error handling

Check result.error before reading data to avoid masking failures.

-      const result = await client
+      const result = await client
         .query(GET_ARBITRATOR_QUERY, { address: checksummedAddress })
         .toPromise();
-      const data = result.data;
+      if (result.error) {
+        console.error('URQL error fetching arbitrator data:', result.error);
+        return null;
+      }
+      const data = result.data;
website/src/app/jobs/[id]/page.tsx (1)

53-57: URQL error handling

Handle result.error explicitly.

-      const result = await client
+      const result = await client
         .query(GET_JOB_BY_ID_QUERY, { jobId })
         .toPromise();
-      const data = result.data;
+      if (result.error) {
+        console.error('URQL error fetching job data:', result.error);
+        return null;
+      }
+      const data = result.data;
website/__tests__/hooks/subsquid/useCreatorCompletedJobs.test.tsx (2)

1-4: Minor: remove unused imports

waitFor isn't used.

-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';

6-18: State in mock doesn't match query filter semantics

GET_CREATOR_COMPLETED_JOBS filters for completed jobs (state_eq: 2 per query). The mock uses state: 3. Align to reduce confusion for future readers.

-  state: 3,
+  state: 2,
website/__tests__/hooks/subsquid/useCreatorOpenJobs.test.tsx (1)

1-1: Remove unused import.

The waitFor utility is imported but never used in this test. With synchronous mocks (fromValue), data is available immediately and no waiting is required.

Apply this diff:

-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
website/__tests__/components/JobsList.test.tsx (5)

2-2: Remove unused imports.

The afterEach utility is imported but never used in this test suite.

Apply this diff:

-import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
+import { vi, describe, it, expect, beforeEach } from 'vitest';

5-5: Remove unused import.

The userEvent utility is imported but never used in this test suite.

Apply this diff:

-import userEvent from '@testing-library/user-event';

37-39: Remove unnecessary cleanup.

The beforeEach hook calls vi.clearAllMocks(), but this test suite doesn't use any mocks. This cleanup is unnecessary.

Apply this diff:

-  beforeEach(() => {
-    vi.clearAllMocks();
-  });

48-58: Remove duplicate test cases.

Lines 48-52 and 54-58 test the same behavior (rendering an empty div when jobs are not provided or empty). Consider consolidating into a single test with multiple assertions.

Apply this diff:

-  it('should render empty div when no jobs provided', () => {
-    const { container } = render(<JobsList />);
-
-    expect(container.querySelector('.space-y-3')).toBeInTheDocument();
-  });
-
-  it('should render empty div when empty array provided', () => {
+  it('should render empty div when no jobs or empty array provided', () => {
+    const { container: container1 } = render(<JobsList />);
+    expect(container1.querySelector('.space-y-3')).toBeInTheDocument();
+
     const { container } = render(<JobsList jobs={[]} />);
-
     expect(container.querySelector('.space-y-3')).toBeInTheDocument();
   });

60-65: Remove duplicate test case.

Lines 60-65 duplicate the test at lines 41-46. Both verify that all jobs from the array are rendered. Remove this duplicate.

Apply this diff:

-  it('should render all jobs from array', () => {
-    render(<JobsList jobs={mockJobs} />);
-
-    expect(screen.getByText('AI Model Training')).toBeInTheDocument();
-    expect(screen.getByText('Data Analysis')).toBeInTheDocument();
-  });
website/__tests__/components/Button.test.tsx (1)

2-2: Remove unused import.

The afterEach utility is imported but never used in this test suite.

Apply this diff:

-import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
+import { vi, describe, it, expect, beforeEach } from 'vitest';
website/__tests__/hooks/subsquid/useWorkerTakenJobs.test.tsx (1)

1-1: Remove unused import.

The waitFor utility is imported but never used in this test. With synchronous mocks (fromValue), data is available immediately and no waiting is required.

Apply this diff:

-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
website/__tests__/hooks/subsquid/useJobEvents.test.tsx (1)

1-1: Remove unused import.

The waitFor utility is imported but never used in this test. With synchronous mocks (fromValue), data is available immediately and no waiting is required.

Apply this diff:

-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
website/__tests__/hooks/subsquid/useMarketplace.test.tsx (1)

1-1: Remove unused import.

The waitFor utility is imported but never used in this test. With synchronous mocks (fromValue), data is available immediately and no waiting is required.

Apply this diff:

-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
website/__tests__/hooks/subsquid/useCreatorTakenJobs.test.tsx (1)

1-1: Remove unused import.

The waitFor utility is imported but never used in this test. With synchronous mocks (fromValue), data is available immediately and no waiting is required.

Apply this diff:

-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
website/__tests__/hooks/subsquid/useJobs.test.tsx (1)

55-67: Prefer the existing createUrqlWrapper helper for more reliable mocking.

The custom createMockClient function returns the same data for all queries regardless of which query is executed. This can lead to false positives if the hook accidentally calls the wrong query or if future changes introduce additional queries.

Other test files in this PR (e.g., useWorkerDisputedJobs.test.tsx, useJobsByIds.test.tsx) use the existing createUrqlWrapper helper from website/__tests__/setup/mocks/urql.tsx, which properly matches queries to their expected variables and data. This pattern is more robust and consistent across the test suite.

Consider refactoring to use the shared helper:

-const createMockClient = (data: any) => {
-  return {
-    executeQuery: () => fromValue({ data }),
-    executeMutation: () => fromValue({}),
-    executeSubscription: () => fromValue({}),
-  } as unknown as Client;
-};
-
-const wrapper = (data: any) => ({ children }: { children: ReactNode }) => (
-  <UrqlProvider value={createMockClient(data)}>
-    {children}
-  </UrqlProvider>
-);
+import { createUrqlWrapper } from '../../setup/mocks/urql';
+
+const createWrapper = (data: any) => {
+  const mocks = [
+    {
+      query: GET_JOBS(),
+      variables: { offset: 0, limit: 1000 },
+      data,
+    },
+  ];
+  return createUrqlWrapper(mocks);
+};

Then update the test cases to use createWrapper({ jobs: mockJobs }) instead of wrapper({ jobs: mockJobs }).

website/__tests__/hooks/subsquid/useUsers.test.tsx (1)

1-1: Remove unused import

waitFor isn’t used in this test. Drop it to keep the test lean.

-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
website/__tests__/hooks/subsquid/useUser.test.tsx (2)

6-16: Verify field names align with UserFields (address vs address_)

This mock uses address_, while other tests and the query use address. If UserFields doesn’t alias addressaddress_, update the mock to address to avoid drift.

-const mockUser = {
-  address_: '0x123',
+const mockUser = {
+  address: '0x123',

1-1: Remove unused import

waitFor isn’t used here. Remove it.

-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
website/__tests__/hooks/useConfig.test.ts (1)

2-2: Optional: drop unused import

afterEach isn’t used.

-import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
+import { vi, describe, it, expect, beforeEach } from 'vitest';
website/__tests__/setup/test-utils.tsx (1)

10-15: Let tests compose wrappers; don’t block wrapper or hard‑lock the URQL client

Your override prevents passing a custom wrapper/client per test. Enable wrapper composition so tests can inject providers (e.g., theme/router/urql variants).

Apply:

-import { render, RenderOptions } from '@testing-library/react';
+import { render, RenderOptions } from '@testing-library/react';
@@
-// Create a default urql client for tests
+// Create a default urql client for tests
 const mockClient = {
   executeQuery: vi.fn(() => fromValue({ data: undefined, error: undefined })),
   executeMutation: vi.fn(() => fromValue({ data: undefined, error: undefined })),
   executeSubscription: vi.fn(() => fromValue({ data: undefined, error: undefined })),
 } as any;
@@
-export function customRender(
-  ui: ReactElement,
-  options?: Omit<RenderOptions, 'wrapper'>
-) {
-  return render(
-    <Provider value={mockClient}>
-      {ui}
-    </Provider>,
-    { ...options }
-  );
-}
+export function customRender(
+  ui: ReactElement,
+  options: RenderOptions = {}
+) {
+  const UserWrapper = options.wrapper;
+  const Wrapper = ({ children }: { children: React.ReactNode }) => (
+    <Provider value={mockClient}>
+      {UserWrapper ? <UserWrapper>{children}</UserWrapper> : children}
+    </Provider>
+  );
+  return render(ui, { ...options, wrapper: Wrapper });
+}
@@
 // Override the default render
 export { customRender as render };

If needed later, expose a way to inject a per-test urql client override.

Also applies to: 18-28, 30-31

website/src/hooks/subsquid/useCreatorOpenJobs.tsx (1)

11-13: Type the query result and tighten memo deps

Avoid as Job[] by typing useQuery, and make the memo depend on primitive fields to reduce churn.

-  const [result] = useQuery({
+  const [result] = useQuery<{ jobs: Job[] }>({
     query: GET_CREATOR_OPEN_JOBS,
     variables: { creatorAddress, ...(orderBy && { orderBy }) },
   });
...
-  return useMemo(
-    () => ({
-      data: result.data ? (result.data?.jobs as Job[]) : undefined,
-      loading: result.fetching,
-      error: result.error
-    }),
-    [result]
-  );
+  return useMemo(
+    () => ({
+      data: result.data?.jobs,
+      loading: result.fetching,
+      error: result.error,
+    }),
+    [result.data, result.fetching, result.error]
+  );

Additionally, consider using GraphQL codegen types to avoid accidental shape drift between Subsquid and contract Job types.

Also applies to: 17-22

website/__tests__/integration/job-creation-flow.test.tsx (2)

87-89: Fix the index or the comment

You're clicking index [1] but the comment says “first arbitrator”. Use [0] or update the comment.

-    await user.click(screen.getAllByText(/arbitrator/i)[1]); // Select first arbitrator
+    await user.click(screen.getAllByText(/arbitrator/i)[0]); // Select first arbitrator

158-161: Avoid mixing ESM with require in tests

Prefer consistent ESM imports for mocked modules to keep typings and tooling happy.

-    const { useBalance } = require('wagmi');
-    (useBalance as any).mockReturnValue({
+    import { useBalance } from 'wagmi';
+    (useBalance as unknown as { mockReturnValue: Function }).mockReturnValue({
       data: { value: BigInt(0) },
     });
website/src/hooks/subsquid/useArbitratorsByAddresses.tsx (4)

19-28: Reset state on input change to avoid stale arbitrators

Merging into previous state can retain arbitrators no longer requested when arbitratorAddresses shrinks.

-  useEffect(() => {
-    if (result.data) {
-      const results: Record<string, Arbitrator> = {};
-      for (const arbitrator of result.data.arbitrators) {
-        results[arbitrator.address_] = arbitrator;
-      }
-      setArbitrators((prev) => ({ ...prev, ...results }));
-    }
-  }, [result.data]);
+  useEffect(() => {
+    if (!result.data) return;
+    const next: Record<string, Arbitrator> = {};
+    for (const a of result.data.arbitrators) {
+      next[a.address_] = a;
+    }
+    setArbitrators(next);
+  }, [result.data, arbitratorAddresses]);

13-17: Type the query result to avoid any and shape drift

Use generics so result.data.arbitrators is typed, and you can map fields safely.

-  const [result] = useQuery({
+  const [result] = useQuery<{ arbitrators: Array<Pick<Arbitrator, 'address_'>> | any[] }>({
     query: GET_ARBITRATORS_BY_ADDRESSES,
     variables: { arbitratorAddresses: arbitratorAddresses ?? [] },
     pause: !arbitratorAddresses?.length,
   });

Better: adopt GraphQL codegen and use the generated GetArbitratorsByAddressesQuery type.


30-37: Tighten memo deps

Depend on primitives used inside the memo to reduce recomputes.

-  return useMemo(
+  return useMemo(
     () => ({
       data: result.data ? arbitrators : undefined,
       loading: result.fetching,
       error: result.error
     }),
-    [arbitratorAddresses, result, arbitrators]
+    [result.data, result.fetching, result.error, arbitrators]
   );

9-11: Type mismatch between Subsquid and contract Arbitrator

You’re storing Subsquid arbitrator objects (with address_) into Record<string, Arbitrator> where Arbitrator is a contract read type. That’s misleading and can mask field differences.

Either:

  • define a GraphQL Arbitrator shape for Subsquid responses, or
  • convert the Subsquid object to the contract Arbitrator shape before storing.

Also applies to: 22-24

website/__tests__/setup/mocks/urql.tsx (2)

44-51: Improve “no mock found” error message

Printing the raw DocumentNode results in “[object Object]”. Use String() for clarity.

-      error: new Error(`No mock found for query: ${request.query}`),
+      error: new Error(`No mock found for query: ${String(request.query)}`),

6-11: Tighten types for mocks

If you use GraphQL documents, type query as DocumentNode and narrow variables for better DX. Optional but helpful.

subsquid/tests/integration/database.test.ts (1)

1-55: LGTM! Well-documented integration test scaffold.

This is a good pattern for introducing integration test infrastructure. The file includes:

  • Clear documentation on requirements
  • Proper use of describe.skip to prevent CI failures
  • Comprehensive guidance for enabling tests
  • Proper setup/teardown patterns in the commented code

When you're ready to enable these integration tests, I can help you:

  1. Set up the test database configuration
  2. Create test fixtures and factories for models
  3. Write comprehensive integration test cases

Would you like me to generate the implementation?

website/lighthouserc.js (1)

1-23: LGTM! Reasonable Lighthouse CI configuration for initial setup.

The configuration provides a solid foundation:

  • Multiple runs (3) for reliable averages
  • Documented audit skip for http2
  • Appropriate thresholds for different categories
  • Temporary public storage for CI visibility

Consider these optional improvements for the future:

  1. Increase the performance threshold to 0.8 to match other categories
  2. Change assertion level from warn to error once the site consistently meets thresholds to prevent performance regressions

Apply this diff when ready to enforce stricter standards:

       assertions: {
-        'categories:performance': ['warn', { minScore: 0.7 }],
+        'categories:performance': ['error', { minScore: 0.8 }],
-        'categories:accessibility': ['warn', { minScore: 0.8 }],
+        'categories:accessibility': ['error', { minScore: 0.8 }],
-        'categories:best-practices': ['warn', { minScore: 0.8 }],
+        'categories:best-practices': ['error', { minScore: 0.8 }],
-        'categories:seo': ['warn', { minScore: 0.8 }],
+        'categories:seo': ['error', { minScore: 0.8 }],
       },
website/package.json (1)

58-92: Remove unused Jest dependencies
Jest packages (jest, jest-environment-jsdom, jest-junit, jsdom) aren’t referenced by any config or tests, and all test scripts invoke Vitest. Remove these leftover devDependencies.

.github/workflows/coverage.yml (1)

8-12: Add a timeout to prevent indefinite workflow runs.

The workflow lacks a timeout-minutes setting, which can cause it to run indefinitely if tests hang.

Apply this diff:

   coverage:
     runs-on: ubuntu-latest
+    timeout-minutes: 30
     strategy:
       matrix:
         package: [contract, website]
subsquid/tests/fixtures/mock-events.ts (1)

1-1: Remove unused imports.

ZeroAddress and ZeroHash are imported but never used in this file.

Apply this diff:

-import { ZeroAddress, ZeroHash } from 'ethers';
+// No imports needed for this file
subsquid/tests/unit/job-event-processing.test.ts (1)

43-80: Strengthen decoder tests to validate actual decoding behavior.

The current tests only verify that decoder functions exist and are callable, which provides minimal value. To properly test the decoders, you should:

  1. Encode sample data using the contract's encoding logic
  2. Pass the encoded data to the decoder
  3. Assert that the decoded output matches the expected structure and values

Example pattern for a more thorough test:

it('should decode JobCreated event data correctly', () => {
  // This would require encoding the mock data first
  const encodedData = encodeJobCreatedEvent(mockJobCreatedEventData);
  const decoded = decodeJobCreatedEvent(encodedData);
  
  expect(decoded.title).toBe(mockJobCreatedEventData.title);
  expect(decoded.amount).toBe(mockJobCreatedEventData.amount);
  expect(decoded.tags).toEqual(mockJobCreatedEventData.tags);
  // ... assert other fields
});

Note: This requires the mock data in mock-events.ts to be fixed first (see comments on that file).

subsquid/tests/unit/data-models.test.ts (1)

14-214: Add imports or remove placeholder tests.

The test suite references User, Arbitrator, Job, Review, Marketplace, JobRoles, and JobTimes classes without importing them. While the tests are currently skipped, they would fail immediately if unskipped due to undefined references.

Consider one of these approaches:

  1. Add proper imports from your TypeORM models (if you plan to enable these tests soon)
  2. Remove the placeholder tests until you're ready to implement proper integration tests with database setup
  3. Replace with factory-based tests that don't rely on TypeORM decorators, as suggested in the comment

Additionally, TypeORM entities typically don't accept object literals in their constructors as shown in these tests. You'll need to either:

  • Use factory functions that create and populate entities
  • Use Object.assign or similar approaches
  • Test with a real database connection where TypeORM's repository methods handle instantiation

If you want to keep these as documentation/planning, add the imports with a comment:

import { describe, it, expect } from 'vitest';
import { ZeroAddress, ZeroHash, getAddress } from 'ethers';
// TODO: Uncomment when TypeORM models are properly set up for testing
// import { User, Arbitrator, Job, Review, Marketplace, JobRoles, JobTimes } from '../path/to/models';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62c39ab and 50ece02.

⛔ Files ignored due to path filters (6)
  • contract/package-lock.json is excluded by !**/package-lock.json
  • contract/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • subsquid/package-lock.json is excluded by !**/package-lock.json
  • website/package-lock.json is excluded by !**/package-lock.json
  • website/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (107)
  • .github/PULL_REQUEST_TEMPLATE.md (1 hunks)
  • .github/dependabot.yml (1 hunks)
  • .github/workflows/coverage.yml (1 hunks)
  • .github/workflows/lighthouse.yml (1 hunks)
  • README.md (2 hunks)
  • contract/.gitignore (1 hunks)
  • contract/.solcover.js (1 hunks)
  • contract/README.md (1 hunks)
  • contract/hardhat.config.base.ts (1 hunks)
  • contract/package.json (2 hunks)
  • contract/test/eaccbar.test.ts (0 hunks)
  • contract/test/eacctoken.test.ts (1 hunks)
  • contract/test/marketplacev2.test.ts (0 hunks)
  • contract/test/utils.test.ts (1 hunks)
  • subsquid/package.json (2 hunks)
  • subsquid/tests/fixtures/mock-events.ts (1 hunks)
  • subsquid/tests/integration/database.test.ts (1 hunks)
  • subsquid/tests/unit/data-models.test.ts (1 hunks)
  • subsquid/tests/unit/event-types.test.ts (1 hunks)
  • subsquid/tests/unit/job-event-processing.test.ts (1 hunks)
  • subsquid/vitest.config.ts (1 hunks)
  • website/.gitignore (1 hunks)
  • website/README.md (2 hunks)
  • website/__tests__/api/circulating_supply.test.ts (1 hunks)
  • website/__tests__/api/market_cap.test.ts (1 hunks)
  • website/__tests__/api/total_supply.test.ts (1 hunks)
  • website/__tests__/components/Button.test.tsx (1 hunks)
  • website/__tests__/components/Input.test.tsx (1 hunks)
  • website/__tests__/components/JobsList.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useArbitrator.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useArbitrators.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useCreatorClosedJobs.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useCreatorCompletedJobs.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useCreatorDisputedJobs.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useCreatorOpenJobs.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useCreatorTakenJobs.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useJob.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useJobEvents.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useJobSearch.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useJobs.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useJobsByIds.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useMarketplace.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useReviews.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useSquidStatus.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useUser.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useUserNotifications.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useUsers.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useUsersByAddresses.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useWorkerApplications.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useWorkerCompletedJobs.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useWorkerDisputedJobs.test.tsx (1 hunks)
  • website/__tests__/hooks/subsquid/useWorkerTakenJobs.test.tsx (1 hunks)
  • website/__tests__/hooks/useConfig.test.ts (1 hunks)
  • website/__tests__/hooks/useDimensions.test.tsx (1 hunks)
  • website/__tests__/hooks/useToast.test.ts (1 hunks)
  • website/__tests__/integration/job-creation-flow.test.tsx (1 hunks)
  • website/__tests__/lib/tokens.test.ts (1 hunks)
  • website/__tests__/lib/utils.test.ts (1 hunks)
  • website/__tests__/pages/AcceptButton.test.tsx (1 hunks)
  • website/__tests__/pages/ConnectWallet.test.tsx (1 hunks)
  • website/__tests__/pages/DeliverResultButton.test.tsx (1 hunks)
  • website/__tests__/pages/PostJobPage.test.tsx (1 hunks)
  • website/__tests__/pages/TakeJobButton.test.tsx (1 hunks)
  • website/__tests__/setup/mocks/apollo.ts (1 hunks)
  • website/__tests__/setup/mocks/contracts.ts (1 hunks)
  • website/__tests__/setup/mocks/urql.tsx (1 hunks)
  • website/__tests__/setup/mocks/wagmi.ts (1 hunks)
  • website/__tests__/setup/test-utils.tsx (1 hunks)
  • website/lighthouserc.js (1 hunks)
  • website/next.config.mjs (1 hunks)
  • website/package.json (1 hunks)
  • website/src/app/arbitrators/[address]/page.tsx (4 hunks)
  • website/src/app/arbitrators/page.tsx (2 hunks)
  • website/src/app/jobs/[id]/JobActions/ApproveButton.tsx (2 hunks)
  • website/src/app/jobs/[id]/JobActions/PostMessageButton.tsx (1 hunks)
  • website/src/app/jobs/[id]/JobActions/UpdateButton.tsx (1 hunks)
  • website/src/app/jobs/[id]/JobChat/Events/DeliveredEvent.tsx (1 hunks)
  • website/src/app/jobs/[id]/JobChat/Events/OwnerMessageEvent.tsx (1 hunks)
  • website/src/app/jobs/[id]/JobChat/Events/WorkerMessageEvent.tsx (1 hunks)
  • website/src/app/jobs/[id]/JobChat/JobChatStatus.tsx (0 hunks)
  • website/src/app/jobs/[id]/JobChat/StatusStates/ResultAccepted.tsx (1 hunks)
  • website/src/app/jobs/[id]/JobChat/StatusStates/ResultVerification.tsx (1 hunks)
  • website/src/app/jobs/[id]/JobChatDetails.tsx (0 hunks)
  • website/src/app/jobs/[id]/page.tsx (4 hunks)
  • website/src/app/open-job-list/page.tsx (1 hunks)
  • website/src/app/post-job/PostJobPage.tsx (1 hunks)
  • website/src/app/post-job/page.tsx (1 hunks)
  • website/src/app/providers.tsx (2 hunks)
  • website/src/app/users/[address]/page.tsx (5 hunks)
  • website/src/app/users/page.tsx (2 hunks)
  • website/src/components/AddToHomescreen.tsx (0 hunks)
  • website/src/components/Dashboard/Navbar/NotificationsButton.tsx (1 hunks)
  • website/src/components/TokenSelector/icons/EthereumIcon.tsx (1 hunks)
  • website/src/components/TokenSelector/icons/ExternalLinkIcon.tsx (1 hunks)
  • website/src/components/TokenSelector/icons/PinIcon.tsx (1 hunks)
  • website/src/contexts/CacheInvalidationContext.tsx (1 hunks)
  • website/src/hooks/subsquid/queries.ts (1 hunks)
  • website/src/hooks/subsquid/useArbitrator.tsx (2 hunks)
  • website/src/hooks/subsquid/useArbitratorPublicKeys.tsx (1 hunks)
  • website/src/hooks/subsquid/useArbitrators.tsx (1 hunks)
  • website/src/hooks/subsquid/useArbitratorsByAddresses.tsx (2 hunks)
  • website/src/hooks/subsquid/useCreatorClosedJobs.tsx (1 hunks)
  • website/src/hooks/subsquid/useCreatorCompletedJobs.tsx (1 hunks)
  • website/src/hooks/subsquid/useCreatorDisputedJobs.tsx (1 hunks)
  • website/src/hooks/subsquid/useCreatorOpenJobs.tsx (1 hunks)
  • website/src/hooks/subsquid/useCreatorTakenJobs.tsx (1 hunks)
  • website/src/hooks/subsquid/useJob.tsx (1 hunks)
⛔ Files not processed due to max files limit (24)
  • website/src/hooks/subsquid/useJobEvents.tsx
  • website/src/hooks/subsquid/useJobSearch.tsx
  • website/src/hooks/subsquid/useJobs.tsx
  • website/src/hooks/subsquid/useJobsByIds.tsx
  • website/src/hooks/subsquid/useMarketplace.tsx
  • website/src/hooks/subsquid/useReviews.tsx
  • website/src/hooks/subsquid/useSquidStatus.tsx
  • website/src/hooks/subsquid/useUser.tsx
  • website/src/hooks/subsquid/useUserJobNotifications.tsx
  • website/src/hooks/subsquid/useUserNotifications.tsx
  • website/src/hooks/subsquid/useUsers.tsx
  • website/src/hooks/subsquid/useUsersByAddresses.tsx
  • website/src/hooks/subsquid/useWorkerApplications.tsx
  • website/src/hooks/subsquid/useWorkerCompletedJobs.tsx
  • website/src/hooks/subsquid/useWorkerDisputedJobs.tsx
  • website/src/hooks/subsquid/useWorkerTakenJobs.tsx
  • website/src/hooks/useFetchAvatar.tsx
  • website/src/hooks/useMediaDownloadHandler.ts
  • website/src/hooks/useSwResetMessage.tsx
  • website/src/hooks/useWriteContractWithNotifications.ts
  • website/src/lib/urql-client.ts
  • website/src/lib/urql-server.ts
  • website/vitest.config.ts
  • website/vitest.setup.ts
💤 Files with no reviewable changes (5)
  • website/src/app/jobs/[id]/JobChat/JobChatStatus.tsx
  • website/src/components/AddToHomescreen.tsx
  • website/src/app/jobs/[id]/JobChatDetails.tsx
  • contract/test/marketplacev2.test.ts
  • contract/test/eaccbar.test.ts
🧰 Additional context used
🧬 Code graph analysis (55)
website/src/app/arbitrators/page.tsx (1)
website/src/lib/urql-server.ts (1)
  • createServerUrqlClient (4-14)
website/src/app/open-job-list/page.tsx (1)
website/src/app/post-job/page.tsx (1)
  • dynamic (6-6)
website/src/components/Dashboard/Navbar/NotificationsButton.tsx (1)
website/src/hooks/subsquid/useUserNotifications.tsx (1)
  • useUserNotifications (30-135)
website/__tests__/hooks/subsquid/useUserNotifications.test.tsx (3)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (2)
  • GET_USER_NOTIFICATIONS (231-251)
  • GET_JOBS_BY_IDS (68-74)
website/src/hooks/subsquid/useUserNotifications.tsx (1)
  • useUserNotifications (30-135)
website/__tests__/hooks/subsquid/useWorkerCompletedJobs.test.tsx (2)
website/__tests__/setup/mocks/urql.tsx (2)
  • mockJob (70-90)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (1)
  • GET_WORKER_COMPLETED_JOBS (215-221)
website/__tests__/hooks/subsquid/useJobs.test.tsx (2)
website/__tests__/setup/mocks/apollo.ts (1)
  • mockJob (5-25)
website/src/hooks/subsquid/useJobs.tsx (1)
  • useJobs (23-49)
website/__tests__/hooks/useConfig.test.ts (1)
website/src/hooks/useConfig.ts (1)
  • useConfig (5-19)
website/__tests__/lib/utils.test.ts (1)
website/src/lib/utils.ts (9)
  • cn (7-9)
  • formatDate (10-17)
  • absoluteUrl (18-20)
  • EventTextMap (21-90)
  • isImageValid (92-102)
  • shortenText (104-124)
  • convertToSeconds (126-143)
  • getUnitAndValueFromSeconds (145-171)
  • formatTimeLeft (182-191)
website/__tests__/hooks/useToast.test.ts (1)
website/src/hooks/useToast.ts (1)
  • useToast (3-35)
website/__tests__/hooks/subsquid/useJobSearch.test.tsx (3)
website/__tests__/setup/mocks/urql.tsx (2)
  • mockJob (70-90)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (1)
  • GET_JOB_SEARCH (172-190)
website/src/hooks/subsquid/useJobSearch.tsx (1)
  • useJobSearch (10-130)
website/__tests__/pages/DeliverResultButton.test.tsx (2)
website/__tests__/setup/mocks/urql.tsx (1)
  • mockJob (70-90)
website/__tests__/setup/mocks/apollo.ts (1)
  • mockJob (5-25)
website/src/hooks/subsquid/useArbitrators.tsx (2)
website/src/hooks/subsquid/queries.ts (1)
  • GET_ARBITRATORS (27-33)
contract/src/interfaces.ts (1)
  • Arbitrator (17-17)
website/src/hooks/subsquid/useCreatorCompletedJobs.tsx (3)
website/src/service/Interfaces.ts (1)
  • OrderByType (38-79)
website/src/hooks/subsquid/queries.ts (1)
  • GET_CREATOR_COMPLETED_JOBS (148-154)
contract/src/interfaces.ts (1)
  • Job (33-40)
website/src/hooks/subsquid/useCreatorOpenJobs.tsx (3)
website/src/service/Interfaces.ts (1)
  • OrderByType (38-79)
website/src/hooks/subsquid/queries.ts (1)
  • GET_CREATOR_OPEN_JOBS (132-138)
contract/src/interfaces.ts (1)
  • Job (33-40)
website/__tests__/hooks/subsquid/useUsers.test.tsx (3)
website/src/hooks/subsquid/queries.ts (1)
  • GET_USERS (92-98)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/useUsers.tsx (1)
  • useUsers (13-27)
website/__tests__/hooks/subsquid/useCreatorOpenJobs.test.tsx (3)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (1)
  • GET_CREATOR_OPEN_JOBS (132-138)
website/src/hooks/subsquid/useCreatorOpenJobs.tsx (1)
  • useCreatorOpenJobs (7-24)
website/__tests__/hooks/subsquid/useWorkerTakenJobs.test.tsx (2)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (1)
  • GET_WORKER_TAKEN_JOBS (207-213)
website/__tests__/hooks/subsquid/useJobsByIds.test.tsx (3)
website/src/hooks/subsquid/queries.ts (1)
  • GET_JOBS_BY_IDS (68-74)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/useJobsByIds.tsx (1)
  • useJobsByIds (6-21)
website/src/hooks/subsquid/useArbitratorPublicKeys.tsx (2)
website/src/hooks/subsquid/useArbitratorsByAddresses.tsx (1)
  • useArbitratorsByAddresses (6-38)
website/src/hooks/subsquid/queries.ts (1)
  • GET_ARBITRATORS_BY_ADDRESSES (43-49)
subsquid/tests/unit/job-event-processing.test.ts (2)
contract/src/utils/decodeEvents.ts (7)
  • decodeJobCreatedEvent (6-22)
  • decodeJobUpdatedEvent (24-38)
  • decodeJobSignedEvent (40-46)
  • decodeJobRatedEvent (48-55)
  • decodeJobDisputedEvent (57-66)
  • decodeJobArbitratedEvent (88-100)
  • decodeJobMessageEvent (102-109)
subsquid/tests/fixtures/mock-events.ts (3)
  • mockJobCreatedEventData (3-14)
  • mockJobRatedEventData (30-33)
  • mockJobArbitratedEventData (39-43)
website/__tests__/hooks/subsquid/useCreatorTakenJobs.test.tsx (2)
website/__tests__/setup/mocks/urql.tsx (2)
  • mockJob (70-90)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (1)
  • GET_CREATOR_TAKEN_JOBS (140-146)
website/__tests__/hooks/subsquid/useWorkerApplications.test.tsx (2)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (1)
  • GET_WORKER_APPLICATIONS (192-205)
website/src/hooks/subsquid/useCreatorTakenJobs.tsx (6)
website/src/hooks/subsquid/useWorkerCompletedJobs.tsx (1)
  • useCreatorTakenJobs (7-24)
website/src/hooks/subsquid/useWorkerApplications.tsx (1)
  • useCreatorTakenJobs (7-24)
website/src/hooks/subsquid/useWorkerTakenJobs.tsx (1)
  • useCreatorTakenJobs (7-24)
website/src/service/Interfaces.ts (1)
  • OrderByType (38-79)
website/src/hooks/subsquid/queries.ts (1)
  • GET_CREATOR_TAKEN_JOBS (140-146)
contract/src/interfaces.ts (1)
  • Job (33-40)
website/__tests__/hooks/subsquid/useCreatorCompletedJobs.test.tsx (3)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (1)
  • GET_CREATOR_COMPLETED_JOBS (148-154)
website/src/hooks/subsquid/useCreatorCompletedJobs.tsx (1)
  • useCreatorCompletedJobs (7-24)
website/src/app/users/page.tsx (1)
website/src/lib/urql-server.ts (1)
  • createServerUrqlClient (4-14)
website/src/app/post-job/page.tsx (1)
website/src/app/open-job-list/page.tsx (1)
  • dynamic (9-9)
website/__tests__/hooks/subsquid/useUsersByAddresses.test.tsx (3)
website/src/hooks/subsquid/queries.ts (1)
  • GET_USERS_BY_ADDRESSES (108-114)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/useUsersByAddresses.tsx (1)
  • useUsersByAddresses (13-47)
website/src/hooks/subsquid/useCreatorDisputedJobs.tsx (4)
website/src/hooks/subsquid/useWorkerDisputedJobs.tsx (1)
  • useCreatorDisputedJobs (7-24)
website/src/service/Interfaces.ts (1)
  • OrderByType (38-79)
website/src/hooks/subsquid/queries.ts (1)
  • GET_CREATOR_DISPUTED_JOBS (156-162)
contract/src/interfaces.ts (1)
  • Job (33-40)
subsquid/tests/unit/data-models.test.ts (1)
contract/src/interfaces.ts (4)
  • User (18-18)
  • Arbitrator (17-17)
  • Job (33-40)
  • Review (20-20)
website/__tests__/hooks/useDimensions.test.tsx (1)
docs/src/components/Heading.jsx (1)
  • ref (69-69)
website/__tests__/hooks/subsquid/useCreatorDisputedJobs.test.tsx (2)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (1)
  • GET_CREATOR_DISPUTED_JOBS (156-162)
contract/test/utils.test.ts (1)
contract/src/utils/encryption.ts (1)
  • publishMediaToIpfs (71-88)
website/__tests__/hooks/subsquid/useCreatorClosedJobs.test.tsx (3)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (1)
  • GET_CREATOR_CLOSED_JOBS (164-170)
website/src/hooks/subsquid/useCreatorClosedJobs.tsx (1)
  • useCreatorClosedJobs (7-24)
website/__tests__/components/Input.test.tsx (1)
website/src/components/Input.tsx (1)
  • Input (11-89)
website/__tests__/components/JobsList.test.tsx (1)
website/src/components/Dashboard/JobsList/JobsList.tsx (1)
  • JobsList (8-14)
website/src/app/providers.tsx (2)
website/src/contexts/CacheInvalidationContext.tsx (1)
  • CacheInvalidationProvider (12-24)
website/src/lib/urql-client.ts (1)
  • urqlClient (6-17)
website/src/hooks/subsquid/useArbitrator.tsx (1)
website/src/hooks/subsquid/queries.ts (1)
  • GET_ARBITRATOR_BY_ADDRESS (35-41)
website/__tests__/hooks/subsquid/useArbitrator.test.tsx (3)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (1)
  • GET_ARBITRATOR_BY_ADDRESS (35-41)
website/src/hooks/subsquid/useArbitrator.tsx (1)
  • useArbitrator (11-38)
website/__tests__/hooks/subsquid/useJob.test.tsx (3)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (1)
  • GET_JOB_BY_ID (60-66)
website/src/hooks/subsquid/useJob.tsx (1)
  • useJob (7-27)
website/__tests__/hooks/subsquid/useMarketplace.test.tsx (3)
website/src/hooks/subsquid/queries.ts (1)
  • GET_MARKETPLACES (11-17)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/useMarketplace.tsx (1)
  • useMarketplace (5-19)
website/__tests__/hooks/subsquid/useWorkerDisputedJobs.test.tsx (2)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (1)
  • GET_WORKER_DISPUTED_JOBS (223-229)
website/src/hooks/subsquid/useCreatorClosedJobs.tsx (3)
website/src/service/Interfaces.ts (1)
  • OrderByType (38-79)
website/src/hooks/subsquid/queries.ts (1)
  • GET_CREATOR_CLOSED_JOBS (164-170)
contract/src/interfaces.ts (1)
  • Job (33-40)
website/__tests__/hooks/subsquid/useSquidStatus.test.tsx (3)
website/src/hooks/subsquid/queries.ts (1)
  • GET_SQUID_STATUS (19-25)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/useSquidStatus.tsx (1)
  • useSquidStatus (5-21)
website/__tests__/pages/TakeJobButton.test.tsx (2)
website/__tests__/setup/mocks/urql.tsx (1)
  • mockJob (70-90)
website/__tests__/setup/mocks/apollo.ts (1)
  • mockJob (5-25)
website/__tests__/hooks/subsquid/useJobEvents.test.tsx (3)
website/src/hooks/subsquid/queries.ts (1)
  • GET_JOB_EVENTS (124-130)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/useJobEvents.tsx (1)
  • useJobEvents (7-27)
website/__tests__/hooks/subsquid/useArbitrators.test.tsx (3)
website/__tests__/setup/mocks/urql.tsx (2)
  • mockArbitrator (101-108)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (1)
  • GET_ARBITRATORS (27-33)
website/src/hooks/subsquid/useArbitrators.tsx (1)
  • useArbitrators (6-20)
website/src/app/jobs/[id]/page.tsx (2)
contract/src/interfaces.ts (1)
  • Job (33-40)
website/src/lib/urql-server.ts (1)
  • createServerUrqlClient (4-14)
website/src/hooks/subsquid/useArbitratorsByAddresses.tsx (2)
website/src/hooks/subsquid/queries.ts (1)
  • GET_ARBITRATORS_BY_ADDRESSES (43-49)
contract/src/interfaces.ts (1)
  • Arbitrator (17-17)
website/src/app/users/[address]/page.tsx (2)
website/src/lib/urql-server.ts (1)
  • createServerUrqlClient (4-14)
website/src/app/users/[address]/UserPageClient.tsx (1)
  • UserPageClient (56-389)
website/src/app/arbitrators/[address]/page.tsx (3)
contract/src/interfaces.ts (1)
  • Arbitrator (17-17)
website/src/lib/urql-server.ts (1)
  • createServerUrqlClient (4-14)
website/src/app/arbitrators/[address]/ArbitratorPageClient.tsx (1)
  • ArbitratorPageClient (67-467)
website/__tests__/hooks/subsquid/useReviews.test.tsx (3)
website/src/hooks/subsquid/queries.ts (1)
  • GET_REVIEWS (116-122)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/useReviews.tsx (1)
  • useReviews (12-74)
website/__tests__/pages/AcceptButton.test.tsx (2)
website/__tests__/setup/mocks/urql.tsx (1)
  • mockJob (70-90)
website/__tests__/setup/mocks/apollo.ts (1)
  • mockJob (5-25)
website/__tests__/hooks/subsquid/useUser.test.tsx (3)
website/__tests__/setup/mocks/urql.tsx (1)
  • createUrqlWrapper (61-67)
website/src/hooks/subsquid/queries.ts (1)
  • GET_USER_BY_ADDRESS (100-106)
website/src/hooks/subsquid/useUser.tsx (1)
  • useUser (22-46)
website/__tests__/setup/mocks/apollo.ts (1)
website/src/hooks/subsquid/queries.ts (2)
  • GET_JOBS (76-82)
  • GET_USERS (92-98)
website/src/hooks/subsquid/useJob.tsx (3)
website/src/contexts/CacheInvalidationContext.tsx (1)
  • useCacheInvalidation (26-32)
website/src/hooks/subsquid/queries.ts (1)
  • GET_JOB_BY_ID (60-66)
contract/src/interfaces.ts (1)
  • Job (33-40)


- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
if: (!cancelled())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the workflow condition syntax.

The condition is missing the expression syntax wrapper. In GitHub Actions, conditions must be wrapped in ${{ }}.

Apply this diff:

-        if: (!cancelled())
+        if: ${{ !cancelled() }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if: (!cancelled())
if: ${{ !cancelled() }}
🤖 Prompt for AI Agents
.github/workflows/coverage.yml around line 35: the workflow conditional uses
plain text "if: (!cancelled())" which is invalid; wrap the expression in GitHub
Actions expression syntax by changing it to use the ${{ }} wrapper (e.g., if:
${{ !cancelled() }}), ensuring there are no extra quotes and spacing is correct.

Comment on lines +1 to +12
module.exports = {
skipFiles: [
'EACCToken.sol',
'MarketplaceV1.sol',
'MarketplaceV2.sol',
'MockSablierLockup.sol',
'TreasurySplit.sol',
'unicrow/',
'interfaces/',
'libraries/',
]
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify coverage exclusions for core contracts.

The coverage configuration excludes several contracts that may be core to the system:

  • EACCToken.sol - appears to be a primary token contract
  • TreasurySplit.sol - appears to be treasury management
  • MarketplaceV1.sol and MarketplaceV2.sol - marketplace contracts

While excluding mocks, interfaces, and libraries is standard practice, ensure these core contracts are intentionally excluded and document the reasoning (e.g., thoroughly tested elsewhere, legacy code being replaced, etc.).

Run the following script to check if these contracts have associated test files:


🏁 Script executed:

#!/bin/bash
# Description: Find test files for excluded contracts

echo "Checking for test files of excluded contracts..."
rg -l "EACCToken|TreasurySplit|MarketplaceV1|MarketplaceV2" contract/test/ --type ts

Length of output: 150


Remove tested contracts from skipFiles or justify exclusions.

  • EACCToken.sol – covered by contract/test/eacctoken.test.ts
  • MarketplaceV1.sol & MarketplaceV2.sol – covered by contract/test/marketplace.test.ts
  • TreasurySplit.sol – no tests found; either add tests or document why it remains excluded
🤖 Prompt for AI Agents
In contract/.solcover.js around lines 1 to 12, the skipFiles list currently
excludes contracts that are actually tested (EACCToken.sol, MarketplaceV1.sol,
MarketplaceV2.sol) and leaves TreasurySplit.sol excluded without tests; remove
EACCToken.sol, MarketplaceV1.sol and MarketplaceV2.sol from skipFiles so
coverage includes them, and either add tests for TreasurySplit.sol and remove it
from skipFiles or add a clear comment in the repo (or a documented rationale in
this file) explaining why TreasurySplit.sol is intentionally excluded.

Comment on lines +145 to 159
it("Should emit Transfer events for each recipient", async function () {
const { eaccToken, deployer, alice, bob } = await loadFixture(deployTokenFixture);

// Set EACCBar to zero address
await eaccToken.connect(deployer).setEACCBar(ethers.ZeroAddress);
const recipients = [alice.address, bob.address];
const amounts = [ethers.parseEther("100"), ethers.parseEther("200")];

const oneYear = 52 * 7 * 24 * 60 * 60; // 52 weeks
const amount = ethers.parseEther("100");
const tx = await eaccToken.multitransfer(recipients, amounts);
const receipt = await tx.wait();

// depositForStream should revert when EACCBar is not set
await expect(
eaccToken.connect(alice).depositForStream(amount, oneYear)
).to.be.revertedWith("EACCToken::depositForStream: eaccBar not set");
});
});
const transferEvents = receipt?.logs.filter(
(log: any) => log.fragment?.name === "Transfer"
);

describe("Edge cases for parameter updates", () => {
it("Should handle extreme values for R", async () => {
const { eaccToken, deployer } = await loadFixture(deployContractsFixture);

// Set R to a very high value
const maxR = ethers.MaxUint256;
await eaccToken.connect(deployer).setR(maxR);
expect(await eaccToken.R()).to.equal(maxR);

// Set R back to a normal value
await eaccToken.connect(deployer).setR(1000);
expect(await eaccToken.R()).to.equal(1000);
});

it("Should handle extreme values for K", async () => {
const { eaccToken, deployer } = await loadFixture(deployContractsFixture);

// Set K to a very high value
const maxK = ethers.MaxUint256;
await eaccToken.connect(deployer).setK(maxK);
expect(await eaccToken.K()).to.equal(maxK);

// Set K back to a normal value
await eaccToken.connect(deployer).setK(10);
expect(await eaccToken.K()).to.equal(10);
});

it("Should handle extreme values for E", async () => {
const { eaccToken, deployer } = await loadFixture(deployContractsFixture);

// Set E to a very high value (be careful not to exceed uint64 max)
const highE = 2n**64n - 1n; // uint64 max
await eaccToken.connect(deployer).setE(highE);
expect(await eaccToken.E()).to.equal(highE);

// Set E to zero (might cause issues with exponentiation)
await eaccToken.connect(deployer).setE(0);
expect(await eaccToken.E()).to.equal(0);

// Set E back to a normal value
await eaccToken.connect(deployer).setE(1n * 10n**18n);
expect(await eaccToken.E()).to.equal(1n * 10n**18n);
});

it("Should update eaccBarPercent to zero and verify behavior", async () => {
const { eaccToken, eaccBar, alice, deployer, mockSablier } = await loadFixture(deployContractsFixture);

// Set eaccBarPercent to zero
await eaccToken.connect(deployer).setEACCBarPercent(0);
expect(await eaccToken.eaccBarPercent()).to.equal(0);

// Check balances before stream creation
const eaccBarBalanceBefore = await eaccToken.balanceOf(await eaccBar.getAddress());
const totalSupplyBefore = await eaccToken.totalSupply();

// Create a stream
const oneYear = 52 * 7 * 24 * 60 * 60; // 52 weeks
const amount = ethers.parseEther("100");
await mockSablier.setNextStreamId(1);
await eaccToken.connect(alice).depositForStream(amount, oneYear);

// Check balances after stream creation
const eaccBarBalanceAfter = await eaccToken.balanceOf(await eaccBar.getAddress());
const totalSupplyAfter = await eaccToken.totalSupply();

// EACCBar should receive nothing
expect(eaccBarBalanceAfter).to.equal(eaccBarBalanceBefore);

// The full amount should be burned
const multiplier = await eaccToken.M(oneYear);
const mintAmount = amount * multiplier / ethers.parseEther("1");

expect(totalSupplyAfter).to.be.closeTo(
totalSupplyBefore - amount + mintAmount,
ethers.parseEther("0.1") // Allow small rounding error
);
});

it("Should verify 100% eaccBarPercent", async () => {
const { eaccToken, eaccBar, alice, deployer, mockSablier } = await loadFixture(deployContractsFixture);

// Set eaccBarPercent to 100%
await eaccToken.connect(deployer).setEACCBarPercent(ethers.parseEther("1"));
expect(await eaccToken.eaccBarPercent()).to.equal(ethers.parseEther("1"));

// Check balances before stream creation
const eaccBarBalanceBefore = await eaccToken.balanceOf(await eaccBar.getAddress());
const totalSupplyBefore = await eaccToken.totalSupply();

// Create a stream
const oneYear = 52 * 7 * 24 * 60 * 60; // 52 weeks
const amount = ethers.parseEther("100");
await mockSablier.setNextStreamId(1);
await eaccToken.connect(alice).depositForStream(amount, oneYear);

// Check balances after stream creation
const eaccBarBalanceAfter = await eaccToken.balanceOf(await eaccBar.getAddress());
const totalSupplyAfter = await eaccToken.totalSupply();

// EACCBar should receive the full amount
expect(eaccBarBalanceAfter).to.equal(eaccBarBalanceBefore + amount);

// No tokens should be burned, only minted
const multiplier = await eaccToken.M(oneYear);
const mintAmount = amount * multiplier / ethers.parseEther("1");

expect(totalSupplyAfter).to.be.closeTo(
totalSupplyBefore + mintAmount,
ethers.parseEther("0.1") // Allow small rounding error
);
});
expect(transferEvents?.length).to.equal(2);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fragile event counting (log.fragment may be undefined).

Parsing receipt logs via log.fragment?.name isn’t reliable in ethers v6. Parse logs with the contract interface instead.

-  const tx = await eaccToken.multitransfer(recipients, amounts);
-  const receipt = await tx.wait();
-
-  const transferEvents = receipt?.logs.filter(
-    (log: any) => log.fragment?.name === "Transfer"
-  );
-  expect(transferEvents?.length).to.equal(2);
+  const tx = await eaccToken.multitransfer(recipients, amounts);
+  const receipt = await tx.wait();
+  const transferEvents =
+    receipt?.logs
+      .map((log: any) => {
+        try { return eaccToken.interface.parseLog(log); } catch { return null; }
+      })
+      .filter((parsed: any) => parsed?.name === "Transfer") ?? [];
+  expect(transferEvents.length).to.equal(2);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("Should emit Transfer events for each recipient", async function () {
const { eaccToken, deployer, alice, bob } = await loadFixture(deployTokenFixture);
// Set EACCBar to zero address
await eaccToken.connect(deployer).setEACCBar(ethers.ZeroAddress);
const recipients = [alice.address, bob.address];
const amounts = [ethers.parseEther("100"), ethers.parseEther("200")];
const oneYear = 52 * 7 * 24 * 60 * 60; // 52 weeks
const amount = ethers.parseEther("100");
const tx = await eaccToken.multitransfer(recipients, amounts);
const receipt = await tx.wait();
// depositForStream should revert when EACCBar is not set
await expect(
eaccToken.connect(alice).depositForStream(amount, oneYear)
).to.be.revertedWith("EACCToken::depositForStream: eaccBar not set");
});
});
const transferEvents = receipt?.logs.filter(
(log: any) => log.fragment?.name === "Transfer"
);
describe("Edge cases for parameter updates", () => {
it("Should handle extreme values for R", async () => {
const { eaccToken, deployer } = await loadFixture(deployContractsFixture);
// Set R to a very high value
const maxR = ethers.MaxUint256;
await eaccToken.connect(deployer).setR(maxR);
expect(await eaccToken.R()).to.equal(maxR);
// Set R back to a normal value
await eaccToken.connect(deployer).setR(1000);
expect(await eaccToken.R()).to.equal(1000);
});
it("Should handle extreme values for K", async () => {
const { eaccToken, deployer } = await loadFixture(deployContractsFixture);
// Set K to a very high value
const maxK = ethers.MaxUint256;
await eaccToken.connect(deployer).setK(maxK);
expect(await eaccToken.K()).to.equal(maxK);
// Set K back to a normal value
await eaccToken.connect(deployer).setK(10);
expect(await eaccToken.K()).to.equal(10);
});
it("Should handle extreme values for E", async () => {
const { eaccToken, deployer } = await loadFixture(deployContractsFixture);
// Set E to a very high value (be careful not to exceed uint64 max)
const highE = 2n**64n - 1n; // uint64 max
await eaccToken.connect(deployer).setE(highE);
expect(await eaccToken.E()).to.equal(highE);
// Set E to zero (might cause issues with exponentiation)
await eaccToken.connect(deployer).setE(0);
expect(await eaccToken.E()).to.equal(0);
// Set E back to a normal value
await eaccToken.connect(deployer).setE(1n * 10n**18n);
expect(await eaccToken.E()).to.equal(1n * 10n**18n);
});
it("Should update eaccBarPercent to zero and verify behavior", async () => {
const { eaccToken, eaccBar, alice, deployer, mockSablier } = await loadFixture(deployContractsFixture);
// Set eaccBarPercent to zero
await eaccToken.connect(deployer).setEACCBarPercent(0);
expect(await eaccToken.eaccBarPercent()).to.equal(0);
// Check balances before stream creation
const eaccBarBalanceBefore = await eaccToken.balanceOf(await eaccBar.getAddress());
const totalSupplyBefore = await eaccToken.totalSupply();
// Create a stream
const oneYear = 52 * 7 * 24 * 60 * 60; // 52 weeks
const amount = ethers.parseEther("100");
await mockSablier.setNextStreamId(1);
await eaccToken.connect(alice).depositForStream(amount, oneYear);
// Check balances after stream creation
const eaccBarBalanceAfter = await eaccToken.balanceOf(await eaccBar.getAddress());
const totalSupplyAfter = await eaccToken.totalSupply();
// EACCBar should receive nothing
expect(eaccBarBalanceAfter).to.equal(eaccBarBalanceBefore);
// The full amount should be burned
const multiplier = await eaccToken.M(oneYear);
const mintAmount = amount * multiplier / ethers.parseEther("1");
expect(totalSupplyAfter).to.be.closeTo(
totalSupplyBefore - amount + mintAmount,
ethers.parseEther("0.1") // Allow small rounding error
);
});
it("Should verify 100% eaccBarPercent", async () => {
const { eaccToken, eaccBar, alice, deployer, mockSablier } = await loadFixture(deployContractsFixture);
// Set eaccBarPercent to 100%
await eaccToken.connect(deployer).setEACCBarPercent(ethers.parseEther("1"));
expect(await eaccToken.eaccBarPercent()).to.equal(ethers.parseEther("1"));
// Check balances before stream creation
const eaccBarBalanceBefore = await eaccToken.balanceOf(await eaccBar.getAddress());
const totalSupplyBefore = await eaccToken.totalSupply();
// Create a stream
const oneYear = 52 * 7 * 24 * 60 * 60; // 52 weeks
const amount = ethers.parseEther("100");
await mockSablier.setNextStreamId(1);
await eaccToken.connect(alice).depositForStream(amount, oneYear);
// Check balances after stream creation
const eaccBarBalanceAfter = await eaccToken.balanceOf(await eaccBar.getAddress());
const totalSupplyAfter = await eaccToken.totalSupply();
// EACCBar should receive the full amount
expect(eaccBarBalanceAfter).to.equal(eaccBarBalanceBefore + amount);
// No tokens should be burned, only minted
const multiplier = await eaccToken.M(oneYear);
const mintAmount = amount * multiplier / ethers.parseEther("1");
expect(totalSupplyAfter).to.be.closeTo(
totalSupplyBefore + mintAmount,
ethers.parseEther("0.1") // Allow small rounding error
);
});
expect(transferEvents?.length).to.equal(2);
});
it("Should emit Transfer events for each recipient", async function () {
const { eaccToken, deployer, alice, bob } = await loadFixture(deployTokenFixture);
const recipients = [alice.address, bob.address];
const amounts = [ethers.parseEther("100"), ethers.parseEther("200")];
const tx = await eaccToken.multitransfer(recipients, amounts);
const receipt = await tx.wait();
const transferEvents =
receipt?.logs
.map((log: any) => {
try {
return eaccToken.interface.parseLog(log);
} catch {
return null;
}
})
.filter((parsed: any) => parsed?.name === "Transfer") ?? [];
expect(transferEvents.length).to.equal(2);
});
🤖 Prompt for AI Agents
In contract/test/eacctoken.test.ts around lines 145 to 159, the test filters
receipt logs using log.fragment?.name which is fragile in ethers v6; instead
iterate receipt.logs and use eaccToken.interface.parseLog(log) (wrapped in
try/catch or filter by topic length) to reliably parse each log and then count
those where parsed.name === "Transfer" so the expectation checks the number of
parsed Transfer events.

Comment on lines +222 to 226
it("Should start with nonce 0 for new address", async function () {
const { eaccToken, alice } = await loadFixture(deployTokenFixture);

// Execute multitransfer
await eaccToken.connect(alice).multitransfer(recipients, amounts);

// Check balances after
const aliceBalanceAfter = await eaccToken.balanceOf(alice.address);
const bobBalanceAfter = await eaccToken.balanceOf(bob.address);
const charlieBalanceAfter = await eaccToken.balanceOf(charlie.address);

// Alice should have lost both amounts
expect(aliceBalanceAfter).to.equal(aliceBalanceBefore - bobAmount - charlieAmount);

// Recipients should have received their amounts
expect(bobBalanceAfter).to.equal(bobBalanceBefore + bobAmount);
expect(charlieBalanceAfter).to.equal(charlieBalanceBefore + charlieAmount);
expect(await eaccToken.nonces(alice.address)).to.equal(0);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

BigInt equality: use 0n.

nonces() returns bigint. Comparing to 0 can fail. Use 0n.

-expect(await eaccToken.nonces(alice.address)).to.equal(0);
+expect(await eaccToken.nonces(alice.address)).to.equal(0n);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("Should start with nonce 0 for new address", async function () {
const { eaccToken, alice } = await loadFixture(deployTokenFixture);
// Execute multitransfer
await eaccToken.connect(alice).multitransfer(recipients, amounts);
// Check balances after
const aliceBalanceAfter = await eaccToken.balanceOf(alice.address);
const bobBalanceAfter = await eaccToken.balanceOf(bob.address);
const charlieBalanceAfter = await eaccToken.balanceOf(charlie.address);
// Alice should have lost both amounts
expect(aliceBalanceAfter).to.equal(aliceBalanceBefore - bobAmount - charlieAmount);
// Recipients should have received their amounts
expect(bobBalanceAfter).to.equal(bobBalanceBefore + bobAmount);
expect(charlieBalanceAfter).to.equal(charlieBalanceBefore + charlieAmount);
expect(await eaccToken.nonces(alice.address)).to.equal(0);
});
it("Should start with nonce 0 for new address", async function () {
const { eaccToken, alice } = await loadFixture(deployTokenFixture);
expect(await eaccToken.nonces(alice.address)).to.equal(0n);
});
🤖 Prompt for AI Agents
In contract/test/eacctoken.test.ts around lines 222 to 226, the assertion
compares a bigint returned by nonces() to the number 0 which can fail; update
the expectation to compare against a bigint (e.g. 0n or BigInt(0)) so the types
match—replace the .to.equal(0) call with .to.equal(0n) (or .to.equal(BigInt(0)))
to fix the test.

Comment on lines 97 to 100
it("should throw an error if media mime type is wrong is empty", async () => {
await expect(publishMediaToIpfs("", Uint8Array.from([1]))).to.be.rejectedWith("wrong mime type");
await expect(publishMediaToIpfs("text", Uint8Array.from([1]))).to.be.rejectedWith("wrong mime type");
await expect(publishMediaToIpfs("test.txt", "", Uint8Array.from([1]))).to.be.rejectedWith("wrong mime type");
await expect(publishMediaToIpfs("test.txt", "text", Uint8Array.from([1]))).to.be.rejectedWith("wrong mime type");
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix typo and add missing test case.

The test description contains a grammatical error: "is wrong is empty" should be "is wrong or empty". Additionally, the test suite is missing coverage for the empty fileName validation.

Apply this diff to fix the description:

-    it("should throw an error if media mime type is wrong is empty", async () => {
+    it("should throw an error if media mime type is wrong or empty", async () => {

Add this test case to cover the empty fileName validation:

    it("should throw an error if file name is empty", async () => {
      await expect(publishMediaToIpfs("", "text/plain", Uint8Array.from([1]))).to.be.rejectedWith("empty file name");
    });
🤖 Prompt for AI Agents
In contract/test/utils.test.ts around lines 97 to 100, fix the test description
typo by changing "is wrong is empty" to "is wrong or empty", and add a new test
case to cover empty fileName validation that calls publishMediaToIpfs with an
empty fileName and expects rejection with "empty file name"; ensure the new test
uses the same style as surrounding tests (async () => { await
expect(...).to.be.rejectedWith("empty file name"); }).

Comment on lines 15 to +25
useEffect(() => {
if (data) {
if (result.data) {
const results: Record<string, string> = {};
for (const arbitrator of data.arbitrators) {
for (const arbitrator of result.data.arbitrators) {
resultMap[arbitrator.address_] = arbitrator.publicKey;
}

setResultMap((prev) => ({ ...prev, ...results }));
}
}, [data]);
}, [result.data]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix critical bug in the useEffect data mapping.

Line 20 incorrectly references resultMap (the state variable) instead of results (the local accumulator). This causes the loop to populate the old state object instead of building the new results, which are then never merged into state.

Apply this diff to fix the bug:

   useEffect(() => {
     if (result.data) {
       const results: Record<string, string> = {};
       for (const arbitrator of result.data.arbitrators) {
-        resultMap[arbitrator.address_] = arbitrator.publicKey;
+        results[arbitrator.address_] = arbitrator.publicKey;
       }
 
       setResultMap((prev) => ({ ...prev, ...results }));
     }
   }, [result.data]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
if (data) {
if (result.data) {
const results: Record<string, string> = {};
for (const arbitrator of data.arbitrators) {
for (const arbitrator of result.data.arbitrators) {
resultMap[arbitrator.address_] = arbitrator.publicKey;
}
setResultMap((prev) => ({ ...prev, ...results }));
}
}, [data]);
}, [result.data]);
useEffect(() => {
if (result.data) {
const results: Record<string, string> = {};
for (const arbitrator of result.data.arbitrators) {
results[arbitrator.address_] = arbitrator.publicKey;
}
setResultMap((prev) => ({ ...prev, ...results }));
}
}, [result.data]);
🤖 Prompt for AI Agents
In website/src/hooks/subsquid/useArbitratorPublicKeys.tsx around lines 16 to 25,
the useEffect loop incorrectly assigns to the state variable resultMap instead
of the local accumulator results; change the loop to populate results (e.g.,
results[arbitrator.address_] = arbitrator.publicKey) and then call
setResultMap(prev => ({ ...prev, ...results })) so the newly built map is merged
into state.

Comment on lines +11 to 14
const [result] = useQuery({
query: GET_CREATOR_CLOSED_JOBS,
variables: { creatorAddress, ...(orderBy && { orderBy }) },
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Pass orderBy as an array to match GraphQL type

$orderBy: [JobOrderByInput!] expects an array; a scalar string will error.

Apply:

   const [result] = useQuery({
     query: GET_CREATOR_CLOSED_JOBS,
-    variables: { creatorAddress, ...(orderBy && { orderBy }) },
+    variables: {
+      creatorAddress,
+      ...(orderBy ? { orderBy: [orderBy] } : {}),
+    },
   });

Optional: pause when creatorAddress is falsy to avoid empty queries.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [result] = useQuery({
query: GET_CREATOR_CLOSED_JOBS,
variables: { creatorAddress, ...(orderBy && { orderBy }) },
});
const [result] = useQuery({
query: GET_CREATOR_CLOSED_JOBS,
variables: {
creatorAddress,
...(orderBy ? { orderBy: [orderBy] } : {}),
},
});
🤖 Prompt for AI Agents
In website/src/hooks/subsquid/useCreatorClosedJobs.tsx around lines 11 to 14,
the query currently passes orderBy as a scalar but the GraphQL variable $orderBy
expects an array [JobOrderByInput!]; change the variables to pass orderBy
wrapped in an array when present (e.g., ...(orderBy && { orderBy: [orderBy] })),
and optionally set the query to pause when creatorAddress is falsy to avoid
running an empty query.

Comment on lines +11 to 14
const [result] = useQuery({
query: GET_CREATOR_COMPLETED_JOBS,
variables: { creatorAddress, ...(orderBy && { orderBy }) },
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix variable shape for orderBy (expects array) and optionally pause on empty address

The query declares $orderBy: [JobOrderByInput!]. Passing a scalar may fail GraphQL validation. Wrap in an array. Optionally, pause when creatorAddress is falsy to avoid needless requests.

-  const [result] = useQuery({
-    query: GET_CREATOR_COMPLETED_JOBS,
-    variables: { creatorAddress, ...(orderBy && { orderBy }) },
-  });
+  const [result] = useQuery({
+    query: GET_CREATOR_COMPLETED_JOBS,
+    variables: {
+      creatorAddress,
+      ...(orderBy ? { orderBy: [orderBy] } : {}),
+    },
+    pause: !creatorAddress,
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [result] = useQuery({
query: GET_CREATOR_COMPLETED_JOBS,
variables: { creatorAddress, ...(orderBy && { orderBy }) },
});
const [result] = useQuery({
query: GET_CREATOR_COMPLETED_JOBS,
variables: {
creatorAddress,
...(orderBy ? { orderBy: [orderBy] } : {}),
},
pause: !creatorAddress,
});
🤖 Prompt for AI Agents
In website/src/hooks/subsquid/useCreatorCompletedJobs.tsx around lines 11 to 14,
the GraphQL variable `orderBy` is declared as an array but the query currently
passes a scalar; wrap the value in an array when present (e.g. pass orderBy:
[orderBy] or map to an array) so it matches `[JobOrderByInput!]`, and add an
optional pause/skip to the useQuery when `creatorAddress` is falsy to avoid
sending the request with an empty address.

Comment on lines +11 to 14
const [result] = useQuery({
query: GET_CREATOR_OPEN_JOBS,
variables: { creatorAddress, ...(orderBy && { orderBy }) },
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix variable shape: GraphQL $orderBy requires an array

You're passing a scalar orderBy where the query expects [JobOrderByInput!]. This can 400 the request or silently ignore ordering.

Apply one of these:

-  const [result] = useQuery({
-    query: GET_CREATOR_OPEN_JOBS,
-    variables: { creatorAddress, ...(orderBy && { orderBy }) },
-  });
+  const [result] = useQuery({
+    query: GET_CREATOR_OPEN_JOBS,
+    variables: {
+      creatorAddress,
+      ...(orderBy !== undefined && {
+        orderBy: Array.isArray(orderBy) ? orderBy : [orderBy],
+      }),
+    },
+  });

Or change the signature to accept an array:

-export default function useCreatorOpenJobs(
-  creatorAddress: string,
-  orderBy?: OrderByType
-) {
+export default function useCreatorOpenJobs(
+  creatorAddress: string,
+  orderBy?: OrderByType | OrderByType[]
+) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [result] = useQuery({
query: GET_CREATOR_OPEN_JOBS,
variables: { creatorAddress, ...(orderBy && { orderBy }) },
});
const [result] = useQuery({
query: GET_CREATOR_OPEN_JOBS,
variables: {
creatorAddress,
...(orderBy !== undefined && {
orderBy: Array.isArray(orderBy) ? orderBy : [orderBy],
}),
},
});
🤖 Prompt for AI Agents
In website/src/hooks/subsquid/useCreatorOpenJobs.tsx around lines 11 to 14, the
GraphQL variable orderBy is being passed as a scalar but the query expects an
array ([JobOrderByInput!]); change the variables shape so orderBy is an array
when present (e.g. variables: { creatorAddress, ...(orderBy && { orderBy:
Array.isArray(orderBy) ? orderBy : [orderBy] }) }) or update the hook signature
to accept orderBy as an array type and pass it directly; ensure downstream types
and callers are updated to match the chosen option.

Comment on lines +10 to 17
const [result] = useQuery({
query: GET_JOB_BY_ID,
variables: { jobId: id },
requestPolicy: 'cache-and-network',
context: useMemo(() => ({
_invalidationTimestamp: timestamp,
}), [timestamp]),
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Ensure _invalidationTimestamp affects cache keys or request policy

URQL ignores context for cache keys by default. Without a custom exchange, _invalidationTimestamp won’t bust the cache.

Please confirm a custom exchange reads _invalidationTimestamp to alter the request key or policy. Otherwise, consider toggling requestPolicy: 'network-only' when timestamp changes.

Run:

Expected: an exchange or client setup that consumes _invalidationTimestamp to influence caching.

Optional: if id can be empty, add pause: !id.

Also applies to: 19-26


🏁 Script executed:

#!/bin/bash
# Find custom urql client/exchanges and usage of the invalidation context
rg -n -C2 --type=ts '\bcreateClient\(|exchanges:\s*\[|cacheExchange|requestPolicy|_invalidationTimestamp|CacheInvalidationContext'

Length of output: 7871


Use invalidation timestamp or network-only policy

  • No URQL exchange currently reads _invalidationTimestamp, so it won’t bust the cache. Either add a custom exchange that consumes _invalidationTimestamp to modify cache keys, or switch requestPolicy to 'network-only' when timestamp changes.
  • Optionally, add pause: !id to avoid running the query with an empty id.
🤖 Prompt for AI Agents
In website/src/hooks/subsquid/useJob.tsx around lines 10 to 17, the query uses a
context field _invalidationTimestamp that no URQL exchange reads so the cache
won’t be busted; either implement a custom URQL exchange that reads
_invalidationTimestamp and uses it to change cache keys or force a fresh fetch
by changing the requestPolicy to 'network-only' whenever timestamp changes (e.g.
compute requestPolicy based on timestamp), and also add pause: !id to prevent
running the query when id is falsy.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
contract/test/utils.test.ts (1)

103-115: Fix incorrect describe block name and remove duplicate test.

The describe block is named "getFromIpfs" but primarily tests getMediaFromIpfs. Additionally, lines 104-108 duplicate the test from lines 66-70, which doesn't belong in this block.

Apply this diff to correct the block name and remove the duplicate:

-  describe("getFromIpfs", () => {
-    it("should get an unencrypted message from ipfs", async () => {
-      const hash = "QmQ8LWNYRHdz3JEnA12A2KkpKBwEbcpmzWfGDWj3KMapwW";
-      const message = await getFromIpfs(hash, undefined);
-      expect(message).to.equal("unencrypted");
-    });
-
+  describe("getMediaFromIpfs", () => {
     it("should get an encrypted message from ipfs", async () => {
       const { mimeType, mediaBytes } = await getMediaFromIpfs(encryptedCid, ZeroHash);
       expect(mimeType).to.equal("text/plain");
       expect(mediaBytes).to.deep.equal(utf8ToBytes(testFileContents));
     });
   });
♻️ Duplicate comments (39)
contract/test/eacctoken.test.ts (2)

154-158: Parse logs with the contract interface

log.fragment isn’t defined reliably in ethers v6, so this filter will intermittently miss Transfer events. Parse each log via eaccToken.interface.parseLog and count the parsed transfers instead.

-  const transferEvents = receipt?.logs.filter(
-    (log: any) => log.fragment?.name === "Transfer"
-  );
-  expect(transferEvents?.length).to.equal(2);
+  const transferEvents =
+    receipt?.logs
+      .map((log: any) => {
+        try {
+          return eaccToken.interface.parseLog(log);
+        } catch {
+          return null;
+        }
+      })
+      .filter((parsed: any) => parsed?.name === "Transfer") ?? [];
+  expect(transferEvents.length).to.equal(2);

222-226: Compare bigint nonces to 0n

nonces() returns a bigint, so asserting against the number 0 fails. Use 0n (or BigInt(0)) to match the type.

-      expect(await eaccToken.nonces(alice.address)).to.equal(0);
+      expect(await eaccToken.nonces(alice.address)).to.equal(0n);
website/src/app/jobs/[id]/JobChat/Events/WorkerMessageEvent.tsx (1)

146-146: Invalid Tailwind utility: use break-words instead of overflow-wrap-breakword.

This issue was previously flagged and remains unresolved. The class overflow-wrap-breakword is not a standard Tailwind CSS utility.

Apply this diff:

-          <div className='prose prose-sm dark:prose-invert max-w-none text-sm text-gray-700 sm:text-base dark:text-gray-300 overflow-wrap-breakword'>
+          <div className='prose prose-sm dark:prose-invert max-w-none text-sm text-gray-700 sm:text-base dark:text-gray-300 break-words'>
website/src/app/jobs/[id]/JobChat/Events/DeliveredEvent.tsx (1)

116-116: Invalid Tailwind utility: use break-words instead of overflow-wrap-breakword.

This issue was previously flagged and remains unresolved. Replace overflow-wrap-breakword with the standard Tailwind utility break-words.

Apply this diff:

-                <div className='prose prose-sm dark:prose-invert max-w-none text-xs text-gray-700 sm:text-sm dark:text-gray-300 overflow-wrap-breakword'>
+                <div className='prose prose-sm dark:prose-invert max-w-none text-xs text-gray-700 sm:text-sm dark:text-gray-300 break-words'>
website/src/app/jobs/[id]/JobChat/StatusStates/ResultAccepted.tsx (1)

206-206: Invalid Tailwind utility: use break-words instead of overflow-wrap-breakword.

This issue was previously flagged and remains unresolved. Replace overflow-wrap-breakword with break-words.

Apply this diff:

-                <div className='prose prose-sm dark:prose-invert max-w-none text-gray-700 dark:text-gray-300 overflow-wrap-breakword'>
+                <div className='prose prose-sm dark:prose-invert max-w-none text-gray-700 dark:text-gray-300 break-words'>
website/src/app/jobs/[id]/JobChat/Events/OwnerMessageEvent.tsx (1)

139-139: Invalid Tailwind utility: use break-words instead of overflow-wrap-breakword.

This issue was previously flagged and remains unresolved. Replace overflow-wrap-breakword with the standard Tailwind utility break-words.

Apply this diff:

-          <div className='prose prose-sm dark:prose-invert max-w-none text-sm text-gray-700 sm:text-base dark:text-gray-300 overflow-wrap-breakword'>
+          <div className='prose prose-sm dark:prose-invert max-w-none text-sm text-gray-700 sm:text-base dark:text-gray-300 break-words'>
website/src/components/TokenSelector/icons/PinIcon.tsx (1)

1-1: The any type issue was already flagged.

This concern was raised in a previous review and remains unresolved.

website/src/components/TokenSelector/icons/EthereumIcon.tsx (1)

1-1: The any type issue was already flagged.

This concern was raised in a previous review and remains unresolved.

website/src/components/TokenSelector/icons/ExternalLinkIcon.tsx (1)

1-1: The any type issue was already flagged.

This concern was raised in a previous review and remains unresolved.

website/src/app/jobs/[id]/JobActions/ApproveButton.tsx (1)

206-244: Previous feedback not addressed: star rating still uses imperative DOM manipulation.

This code segment continues to use direct DOM manipulation via classList operations (lines 212-234), which was already flagged in a previous review. The implementation remains brittle and violates React's declarative model.

As noted in the earlier review, please refactor to use React state for hover tracking instead of querying and mutating the DOM directly.

contract/test/utils.test.ts (1)

97-100: Fix typo and add missing test case (previously flagged).

The test description still contains the grammatical error "is wrong is empty" (should be "is wrong or empty"), and the test suite still lacks coverage for empty fileName validation, despite this being flagged in a previous review.

Apply this diff to fix the description:

-    it("should throw an error if media mime type is wrong is empty", async () => {
+    it("should throw an error if media mime type is wrong or empty", async () => {

Add this test case to cover the empty fileName validation:

    it("should throw an error if file name is empty", async () => {
      await expect(publishMediaToIpfs("", "text/plain", Uint8Array.from([1]))).to.be.rejectedWith("empty file name");
    });
contract/.solcover.js (1)

1-12: Remove tested contracts from skipFiles or justify exclusions.

The coverage configuration excludes several contracts:

  • EACCToken.sol, MarketplaceV1.sol, MarketplaceV2.sol - these appear to have test coverage based on the previous review comment
  • TreasurySplit.sol - no tests found

Excluding tested contracts from coverage defeats the purpose of coverage reporting. Either:

  1. Remove the tested contracts from skipFiles to include them in coverage reports
  2. Add tests for TreasurySplit.sol or document why it's intentionally excluded
.github/workflows/coverage.yml (1)

35-35: Fix the workflow condition syntax.

The condition is missing the GitHub Actions expression syntax wrapper. In GitHub Actions, conditions must be wrapped in ${{ }}.

Apply this diff:

-        if: (!cancelled())
+        if: ${{ !cancelled() }}
website/src/app/jobs/[id]/page.tsx (1)

192-197: Past review comment is outdated for Next.js 15.

Similar to the generateMetadata function, the page component correctly uses params: Promise<{ id: string }> and awaits it. This is the required pattern for Next.js 15+ dynamic routes.

website/__tests__/hooks/subsquid/useWorkerDisputedJobs.test.tsx (1)

20-30: Fix the mock variable name mismatch.

The mock uses creatorAddress as the variable name, but the GET_WORKER_DISPUTED_JOBS query expects worker. This prevents the mock from matching the query.

Apply this diff:

 const mocks = [
   {
     query: GET_WORKER_DISPUTED_JOBS,
     variables: {
-      creatorAddress: '0xWorker1',
+      worker: '0xWorker1',
     },
     data: {
       jobs: [mockJob],
     },
   },
 ];
website/__tests__/lib/utils.test.ts (1)

280-292: Time unit conversions are inconsistent.

The tests use 4-week months (2419200 sec = 28 days) and 336-day years (29030400 sec), but convertToSeconds uses 30-day months and 365-day years. This causes incorrect round-trip conversions.

Align tests to match the implementation:

   it('should return months for month-aligned values', () => {
-    expect(getUnitAndValueFromSeconds(2419200)).toEqual({
+    expect(getUnitAndValueFromSeconds(2592000)).toEqual({
       unit: 'months',
       value: 1,
     });
   });

   it('should return years for year-aligned values', () => {
-    expect(getUnitAndValueFromSeconds(29030400)).toEqual({
+    expect(getUnitAndValueFromSeconds(31536000)).toEqual({
       unit: 'years',
       value: 1,
     });
   });
website/__tests__/setup/mocks/apollo.ts (1)

2-2: Align Apollo mocks with updated query signatures.

The imports and mock requests use outdated query names and variables:

  • GET_JOB should be GET_JOB_BY_ID with variable jobId
  • GET_JOBS is now a function and uses offset/limit instead of first/skip
  • GET_USERS uses offset/limit instead of first/skip

Apply this diff:

-import { GET_JOB, GET_JOBS, GET_USER, GET_USERS } from '@/hooks/subsquid/queries';
+import { GET_JOB_BY_ID, GET_JOBS, GET_USER_BY_ADDRESS, GET_USERS } from '@/hooks/subsquid/queries';

Update the mock responses (lines 47-93):

 export const mockJobQueryResponse: MockedResponse = {
   request: {
-    query: GET_JOB,
-    variables: { id: '1' },
+    query: GET_JOB_BY_ID,
+    variables: { jobId: '1' },
   },
   // ...
 };

 export const mockJobsQueryResponse: MockedResponse = {
   request: {
-    query: GET_JOBS,
-    variables: { first: 10, skip: 0 },
+    query: GET_JOBS(),
+    variables: { offset: 0, limit: 10 },
   },
   // ...
 };

 export const mockUsersQueryResponse: MockedResponse = {
   request: {
     query: GET_USERS,
-    variables: { first: 10, skip: 0 },
+    variables: { offset: 0, limit: 10 },
   },
   // ...
 };
website/src/app/arbitrators/[address]/page.tsx (2)

83-86: Do not await params; Next.js passes params as an object.

In Next.js App Router, dynamic route params are provided as a plain object, not a Promise. Awaiting it will cause a runtime error.

Apply this diff:

 export async function generateMetadata({
   params,
 }: {
-  params: Promise<{ address: string }>;
+  params: { address: string };
 }): Promise<Metadata> {
-  const { address } = await params;
+  const { address } = params;

221-226: Same correction needed for the page component params.

Remove the Promise type and await from params.

Apply this diff:

 export default async function ArbitratorPage({
   params,
 }: {
-  params: Promise<{ address: string }>;
+  params: { address: string };
 }) {
-  const { address } = await params;
+  const { address } = params;
   return <ArbitratorPageClient address={address} />;
 }
website/__tests__/pages/AcceptButton.test.tsx (1)

46-46: Enable or remove skipped test suite.

The test suite remains skipped, preventing regression detection. Either enable these tests by removing .skip after fixing any failures, or delete the suite if it's obsolete.

Based on past review comments, this issue was previously flagged. Please address it by either enabling the tests or removing the file if it's no longer needed.

website/__tests__/hooks/subsquid/useSquidStatus.test.tsx (1)

6-11: Adjust mock data to match the actual schema.

The mock object includes fields (indexerHeight, chainHeight, syncing) that are not part of the GET_SQUID_STATUS query schema. According to the query definition, squidStatus only returns { height }.

website/__tests__/hooks/subsquid/useCreatorClosedJobs.test.tsx (1)

6-17: Mock data state doesn't match the query filter.

The mockJob has state: 4, but GET_CREATOR_CLOSED_JOBS filters for state_eq: 2. This mismatch means the test doesn't accurately validate whether the hook correctly fetches jobs matching the query's state filter.

website/__tests__/hooks/subsquid/useCreatorDisputedJobs.test.tsx (1)

6-18: Mock data state doesn't match the query filter.

The past review comment on this segment remains valid: mockJob has state: 2, but GET_CREATOR_DISPUTED_JOBS filters for state_eq: 1. Update the mock's state to 1.

website/src/hooks/subsquid/useCreatorCompletedJobs.tsx (1)

11-14: Fix variable shape for orderBy (expects array) and optionally pause on empty address.

The past review comment on this segment remains valid: wrap orderBy in an array to match the query signature [JobOrderByInput!], and consider adding pause: !creatorAddress.

website/__tests__/api/total_supply.test.ts (2)

61-69: Cache test doesn't validate caching behavior.

The past review comment on this segment remains valid: calling GET() twice and comparing results doesn't verify caching. Consider verifying Cache-Control headers or tracking contract method calls.


71-79: Test doesn't validate error handling.

The past review comment on this segment remains valid: this test is a placeholder that only checks response existence, not actual error-handling logic. Either implement proper error-handling tests or remove this test.

website/__tests__/hooks/subsquid/useWorkerCompletedJobs.test.tsx (1)

6-18: Mock data state doesn't match the query filter.

The mockJob has state: 3, but GET_WORKER_COMPLETED_JOBS filters for state_eq: 2. This mismatch means the test doesn't properly validate whether the hook correctly fetches jobs that match the query's state filter.

Apply this diff to align the mock with the query:

 const mockJob = {
   id: '1',
-  state: 3,
+  state: 2,
   title: 'Completed Job',
website/__tests__/hooks/subsquid/useUserNotifications.test.tsx (1)

7-18: Assign the localStorage mock to global.localStorage.

The localStorageMock is defined but never assigned to global.localStorage. Without this assignment, the actual browser localStorage will be used instead of the mock, which could cause test failures in headless environments and won't respect the beforeEach cleanup.

Apply this diff to properly mock localStorage:

 // Mock localStorage
 const localStorageMock = (() => {
   let store: Record<string, string> = {};
   return {
     getItem: (key: string) => store[key] || null,
     setItem: (key: string, value: string) => {
       store[key] = value;
     },
     clear: () => {
       store = {};
     }
   };
 })();
+
+Object.defineProperty(global, 'localStorage', {
+  value: localStorageMock,
+});
website/src/hooks/subsquid/useArbitratorPublicKeys.tsx (1)

19-23: Fix critical state update bug: assign to results, not resultMap

Inside the loop you mutate state (resultMap) instead of the local accumulator.

-      for (const arbitrator of result.data.arbitrators) {
-        resultMap[arbitrator.address_] = arbitrator.publicKey;
-      }
+      for (const arbitrator of result.data.arbitrators) {
+        results[arbitrator.address_] = arbitrator.publicKey;
+      }
 
       setResultMap((prev) => ({ ...prev, ...results }));
website/__tests__/hooks/useConfig.test.ts (1)

1-1: Await effect-driven state with waitFor

Assertions run before useEffect sets state. Use waitFor.

-import { renderHook } from '@testing-library/react';
+import { renderHook, waitFor } from '@testing-library/react';
@@
-  it('should return config for connected wallet on mainnet', () => {
+  it('should return config for connected wallet on mainnet', async () => {
@@
-    const { result } = renderHook(() => useConfig());
-
-    expect(result.current).toBeDefined();
-    expect(result.current?.marketplaceAddress).toBe('0xMainnetMarketplace');
+    const { result } = renderHook(() => useConfig());
+    await waitFor(() =>
+      expect(result.current?.marketplaceAddress).toBe('0xMainnetMarketplace')
+    );
   });
@@
-  it('should return config for connected wallet on Arbitrum', () => {
+  it('should return config for connected wallet on Arbitrum', async () => {
@@
-    const { result } = renderHook(() => useConfig());
-
-    expect(result.current).toBeDefined();
-    expect(result.current?.marketplaceAddress).toBe('0xArbitrumMarketplace');
+    const { result } = renderHook(() => useConfig());
+    await waitFor(() =>
+      expect(result.current?.marketplaceAddress).toBe('0xArbitrumMarketplace')
+    );
   });
@@
-  it('should return default config when wallet not connected', () => {
+  it('should return default config when wallet not connected', async () => {
@@
-    const { result } = renderHook(() => useConfig());
-
-    expect(result.current).toBeDefined();
+    const { result } = renderHook(() => useConfig());
+    await waitFor(() => expect(result.current).toBeDefined());
   });
@@
-  it('should update config when chain changes', () => {
+  it('should update config when chain changes', async () => {
@@
-    const { result, rerender } = renderHook(() => useConfig());
-
-    expect(result.current?.marketplaceAddress).toBe('0xMainnetMarketplace');
+    const { result, rerender } = renderHook(() => useConfig());
+    await waitFor(() =>
+      expect(result.current?.marketplaceAddress).toBe('0xMainnetMarketplace')
+    );
@@
-    rerender();
-
-    expect(result.current?.marketplaceAddress).toBe('0xArbitrumMarketplace');
+    rerender();
+    await waitFor(() =>
+      expect(result.current?.marketplaceAddress).toBe('0xArbitrumMarketplace')
+    );
   });

Also applies to: 34-45, 47-58, 60-68, 70-91

website/__tests__/pages/DeliverResultButton.test.tsx (1)

5-7: Fix mocks and required props for DeliverResultButton tests

  • Use a namespace import and mock after vi.mock to avoid binding real wagmi fns.
  • Mock the custom useWriteContractWithNotifications (not wagmi’s).
  • Pass required sessionKeys prop; remove events.
  • Ensure final test provides required props.
-import { DeliverResultButton } from '../../src/app/jobs/[id]/JobActions/DeliverResultButton';
-import { useAccount, useWriteContract } from 'wagmi';
+import { DeliverResultButton } from '../../src/app/jobs/[id]/JobActions/DeliverResultButton';
+import * as wagmi from 'wagmi';
@@
-vi.mock('@/hooks/useWriteContractWithNotifications', () => ({
-  useWriteContractWithNotifications: () => ({
-    writeContract: vi.fn(),
-    isPending: false,
-    isSuccess: false,
-  }),
-}));
+vi.mock('@/hooks/useWriteContractWithNotifications', () => ({
+  useWriteContractWithNotifications: vi.fn(() => ({
+    writeContract: vi.fn(),
+    isPending: false,
+    isSuccess: false,
+  })),
+}));
@@
-vi.mock('wagmi', () => ({
-  useAccount: vi.fn(),
-  useWriteContract: vi.fn(),
+vi.mock('wagmi', () => ({
+  useAccount: vi.fn(),
   useSignMessage: vi.fn(() => ({
     signMessageAsync: vi.fn(),
   })),
   useConfig: vi.fn(() => ({})),
 }));
@@
-  const mockEvents = [] as any;
+  const sessionKeys = { '0xCreator-0xWorker': '0xSessionKey' } as any;
@@
-    (useWriteContract as any).mockReturnValue({
-      writeContract: mockWriteContract,
-      isPending: false,
-      isSuccess: false,
-    });
+    const { useWriteContractWithNotifications } = await import('@/hooks/useWriteContractWithNotifications');
+    (useWriteContractWithNotifications as any).mockReturnValue({
+      writeContract: mockWriteContract,
+      isPending: false,
+      isSuccess: false,
+    });
@@
-    (useAccount as any).mockReturnValue({
+    (wagmi.useAccount as any).mockReturnValue({
       address: '0xWorker',
       isConnected: true,
     });
@@
-    render(<DeliverResultButton address="0xWorker" job={mockJob} events={mockEvents} />);
+    render(<DeliverResultButton address="0xWorker" job={mockJob} sessionKeys={sessionKeys} />);
@@
-    (useAccount as any).mockReturnValue({
+    (wagmi.useAccount as any).mockReturnValue({
       address: '0xCreator',
       isConnected: true,
     });
@@
-    const { container } = render(<DeliverResultButton address="0xWorker" job={mockJob} events={mockEvents} />);
+    const { container } = render(<DeliverResultButton address="0xWorker" job={mockJob} sessionKeys={sessionKeys} />);
@@
-    (useAccount as any).mockReturnValue({
+    (wagmi.useAccount as any).mockReturnValue({
       address: '0xWorker',
       isConnected: true,
     });
@@
-    render(<DeliverResultButton address="0xWorker" job={mockJob} events={mockEvents} />);
+    render(<DeliverResultButton address="0xWorker" job={mockJob} sessionKeys={sessionKeys} />);
@@
-    (useAccount as any).mockReturnValue({
+    (wagmi.useAccount as any).mockReturnValue({
       address: '0xWorker',
       isConnected: true,
     });
@@
-    render(<DeliverResultButton address="0xWorker" job={mockJob} events={mockEvents} />);
+    render(<DeliverResultButton address="0xWorker" job={mockJob} sessionKeys={sessionKeys} />);
@@
-    (useAccount as any).mockReturnValue({
+    (wagmi.useAccount as any).mockReturnValue({
       address: '0xWorker',
       isConnected: true,
     });
@@
-    const openJob = { ...mockJob, state: 'Open' };
-    const { container } = render(<DeliverResultButton job={openJob} />);
+    const openJob = { ...mockJob, state: 'Open' };
+    const { container } = render(
+      <DeliverResultButton address="0xWorker" job={openJob} sessionKeys={{}} />
+    );

Also applies to: 23-39, 61-68, 70-76, 87-90, 100-106, 116-118, 147-149, 158-166

website/src/hooks/subsquid/useCreatorDisputedJobs.tsx (1)

11-14: Pass orderBy as an array to match GraphQL schema.

The GraphQL query defines $orderBy: [JobOrderByInput!], expecting an array, but the code passes a scalar string. This will cause a GraphQL validation error.

Apply this diff:

   const [result] = useQuery({
     query: GET_CREATOR_DISPUTED_JOBS,
-    variables: { creatorAddress, ...(orderBy && { orderBy }) },
+    variables: {
+      creatorAddress,
+      ...(orderBy && { orderBy: [orderBy] }),
+    },
   });
website/src/hooks/subsquid/useJob.tsx (1)

10-17: Cache invalidation via _invalidationTimestamp won't work without a custom exchange.

By default, URQL ignores custom context fields for cache keys. The _invalidationTimestamp will not bust the cache unless a custom exchange consumes it.

Consider one of these approaches:

  1. Add a custom exchange that reads _invalidationTimestamp and modifies cache keys
  2. Use requestPolicy: 'network-only' when timestamp changes to force a fresh fetch
  3. Add pause: !id to prevent running the query when id is empty

Example for option 2:

   const [result] = useQuery({
     query: GET_JOB_BY_ID,
     variables: { jobId: id },
-    requestPolicy: 'cache-and-network',
+    requestPolicy: timestamp ? 'network-only' : 'cache-and-network',
+    pause: !id,
     context: useMemo(() => ({
       _invalidationTimestamp: timestamp,
     }), [timestamp]),
   });
website/src/hooks/subsquid/useCreatorClosedJobs.tsx (1)

11-14: Pass orderBy as an array to match GraphQL schema.

The GraphQL variable $orderBy: [JobOrderByInput!] expects an array, but the code passes a scalar. This will cause a GraphQL validation error.

Apply this diff:

   const [result] = useQuery({
     query: GET_CREATOR_CLOSED_JOBS,
-    variables: { creatorAddress, ...(orderBy && { orderBy }) },
+    variables: {
+      creatorAddress,
+      ...(orderBy && { orderBy: [orderBy] }),
+    },
   });
website/src/hooks/subsquid/useCreatorOpenJobs.tsx (1)

11-14: Pass orderBy as an array to match GraphQL schema.

The GraphQL query expects $orderBy: [JobOrderByInput!] (an array), but the code passes a scalar string. This will cause a GraphQL validation error.

Apply this diff:

   const [result] = useQuery({
     query: GET_CREATOR_OPEN_JOBS,
-    variables: { creatorAddress, ...(orderBy && { orderBy }) },
+    variables: {
+      creatorAddress,
+      ...(orderBy && { orderBy: [orderBy] }),
+    },
   });
website/__tests__/integration/job-creation-flow.test.tsx (1)

1-11: Move Vitest import outside the comment block.

The import statement is inside a block comment, which will break the test if Vitest globals are not enabled.

Apply this diff:

 /**
-import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
  * Integration Test: Complete Job Creation Flow
  *
  * This test covers the entire job creation workflow:
  * 1. Connect wallet
  * 2. Fill job form
  * 3. Approve token spending
  * 4. Submit job transaction
  * 5. View created job
  */
+
+import { vi, describe, it, expect, beforeEach } from 'vitest';
website/__tests__/setup/mocks/contracts.ts (1)

33-40: Use Vitest APIs and correct hash type.

This issue was previously flagged: replace jest.fn() with vi.fn() and type hash as Hash instead of Address.

website/src/app/users/[address]/page.tsx (2)

154-156: Do not type route params as Promise.

This issue was previously flagged: Next.js App Router supplies params as a synchronous object, not a Promise. Remove the Promise wrapper and the await.


312-317: Same: page component should receive params object, not Promise.

This issue was previously flagged: align the page component signature with Next.js conventions by removing the Promise type and await.

🧹 Nitpick comments (33)
website/src/app/jobs/[id]/JobActions/UpdateButton.tsx (2)

289-289: Redundant type parameter in RefObject.

The type React.RefObject<HTMLDivElement | null> is redundant. RefObject<T> already has .current typed as T | null, so RefObject<HTMLDivElement> is the idiomatic form.

Apply this diff to simplify the type:

     const errorFields: Array<{
-      ref: React.RefObject<HTMLDivElement | null>;
+      ref: React.RefObject<HTMLDivElement>;
       setter: (msg: string) => void;
       message: string;
     }> = [];

241-241: Use strict equality operator.

Use === instead of == for strict equality comparison, following JavaScript/TypeScript best practices.

Apply this diff:

-      if (addr == address) {
+      if (addr === address) {
website/src/app/jobs/[id]/JobActions/ApproveButton.tsx (1)

54-54: Consider grouping validation checks consistently.

The condition (!skipReview && rating < 0) || rating > 5 checks rating > 5 regardless of the skipReview flag, while rating < 0 is only checked when not skipping. Since skipReview bypasses rating usage entirely (line 71 passes 0), this is harmless but slightly inconsistent.

For clarity, consider:

-    } else if ((!skipReview && rating < 0) || rating > 5) {
+    } else if (!skipReview && (rating < 0 || rating > 5)) {
website/__tests__/lib/tokens.test.ts (2)

43-43: Consider tightening the URL pattern.

The regex allows any string starting with http:// or https:// without validating the rest of the URL structure. This could pass malformed URLs.

If stricter validation is desired, consider:

-      expect(token.id).toMatch(/^0x[a-fA-F0-9]{40}$|^https?:\/\//);
+      expect(token.id).toMatch(/^(?:0x[a-fA-F0-9]{40}|https?:\/\/[^\s]+)$/);

This ensures the entire string matches either pattern and prevents IDs with trailing invalid characters.


66-79: LGTM! Thorough property and type validation.

The comprehensive checks ensure all tokens conform to the Token interface with correct property types.

Optional: Consider adding non-empty string validation to catch edge cases:

expect(token.id).toBeTruthy();
expect(token.symbol).toBeTruthy();
expect(token.name).toBeTruthy();
expect(token.icon).toBeTruthy();
.github/dependabot.yml (1)

1-42: Consider grouping rules for dependency updates.

The Dependabot configuration is well-structured with appropriate PR limits and scheduling. However, you might consider adding grouping rules to reduce PR noise, especially for patch updates.

Here's an example configuration that groups minor and patch updates:

version: 2
updates:
  # Contract dependencies
  - package-ecosystem: "npm"
    directory: "/contract"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 5
    groups:
      minor-and-patch:
        patterns:
          - "*"
        update-types:
          - "minor"
          - "patch"

  # Website dependencies
  - package-ecosystem: "npm"
    directory: "/website"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 5
    groups:
      minor-and-patch:
        patterns:
          - "*"
        update-types:
          - "minor"
          - "patch"

  # ... repeat for other directories ...

This groups minor and patch updates into single PRs while keeping major version updates separate for easier review.

.github/PULL_REQUEST_TEMPLATE.md (1)

1-39: Consider clarifying the closing line.

The PR template is well-structured with comprehensive sections for description, testing, and review checklist. However, the closing line might benefit from clearer instructions.

Consider updating the closing line to be more explicit:

 ## Related Issues
 <!-- Link related issues here using #issue_number -->
 
-Closes #
+<!-- If this PR closes an issue, uncomment and add the issue number below -->
+<!-- Closes #issue_number -->

This makes it clearer that the line is optional and should only be included when relevant.

website/package.json (1)

18-56: Remove obsolete Apollo mocks and confirm dependencies

  • No peer dependency warnings found.
  • Tests still import @apollo/client/testing in __tests__/setup/mocks/apollo.ts—remove or convert these to URQL-based mocks.
  • No direct react, react-dom, or next version upgrades in package.json.
website/src/app/jobs/[id]/JobActions/PostMessageButton.tsx (1)

38-38: Explicit undefined initialization improves clarity.

Making the initial value explicit (undefined) is a minor improvement for code clarity, though functionally equivalent to the implicit default.

website/__tests__/hooks/subsquid/useJobs.test.tsx (1)

8-53: Consider reusing shared mock data.

The inline mock job definition duplicates data from website/__tests__/setup/mocks/urql.tsx (which exports mockJob). While this works, reusing the shared mock would reduce duplication and ensure consistency across tests.

Example:

+import { mockJob as sharedMockJob } from '../../setup/mocks/urql';
+
-const mockJob = {
-  id: '1',
-  state: 0,
-  whitelistWorkers: false,
-  // ... rest of inline definition
-};
-
 const mockJobs = [
-  mockJob,
+  sharedMockJob,
   {
-    ...mockJob,
+    ...sharedMockJob,
     id: '2',
     title: 'Test Job 2',
     state: 1,
   },
 ];
website/src/hooks/subsquid/useArbitratorsByAddresses.tsx (1)

30-37: Optimize dependency array for useMemo.

The dependency array includes the entire result object, which causes the memo to recompute whenever any property of result changes (including internal metadata). This can lead to unnecessary re-renders.

Apply this diff to use only the specific fields that affect the memoized value:

   return useMemo(
     () => ({
       data: result.data ? arbitrators : undefined,
       loading: result.fetching,
       error: result.error
     }),
-    [arbitratorAddresses, result, arbitrators]
+    [result.data, result.fetching, result.error, arbitrators]
   );
website/__tests__/hooks/subsquid/useJobEvents.test.tsx (1)

1-1: Remove unused waitFor import.

The test does not use waitFor since it relies on synchronous mocks. This import can be safely removed.

Apply this diff to remove the unused import:

-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
website/__tests__/hooks/subsquid/useCreatorTakenJobs.test.tsx (1)

1-1: Remove unused waitFor import.

The test does not use waitFor since it relies on synchronous mocks. This import can be safely removed.

Apply this diff to remove the unused import:

-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
website/__tests__/hooks/subsquid/useCreatorOpenJobs.test.tsx (1)

1-1: Remove unused waitFor import.

The test does not use waitFor since it relies on synchronous mocks. This import can be safely removed.

Apply this diff to remove the unused import:

-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
website/__tests__/hooks/subsquid/useSquidStatus.test.tsx (1)

1-1: Remove unused waitFor import.

The test does not use waitFor since it relies on synchronous mocks. This import can be safely removed.

Apply this diff to remove the unused import:

-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
website/__tests__/hooks/subsquid/useCreatorClosedJobs.test.tsx (1)

1-1: Remove unused waitFor import.

The test does not use waitFor since it relies on synchronous mocks. This import can be safely removed.

Apply this diff to remove the unused import:

-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
website/__tests__/hooks/subsquid/useWorkerApplications.test.tsx (1)

1-1: Remove unused waitFor import.

The test does not use waitFor since it relies on synchronous mocks. This import can be safely removed.

Apply this diff to remove the unused import:

-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
subsquid/tests/unit/job-event-processing.test.ts (1)

43-79: Consider adding decode/encode roundtrip tests.

The tests validate that decoder functions exist and are callable, which is useful for API surface validation. However, to ensure correctness of the decoding logic, consider adding tests that encode mock data and then decode it, verifying the roundtrip produces the expected structure.

Example enhancement:

it('should correctly decode JobCreated event data', () => {
  // Encode mock data first (you'll need an encoder function)
  const encoded = encodeJobCreatedEvent(mockJobCreatedEventData);
  
  // Decode and verify structure matches original
  const decoded = decodeJobCreatedEvent(encoded);
  expect(decoded.title).toBe(mockJobCreatedEventData.title);
  expect(decoded.amount).toBe(mockJobCreatedEventData.amount);
  // ... verify other fields
});
subsquid/tests/unit/event-types.test.ts (1)

7-56: Consider adding value stability tests.

The current tests validate existence and type, which is good. However, to ensure enum stability across versions and prevent unintended changes, consider adding assertions for the actual numeric values.

Example:

it('should have stable numeric values', () => {
  expect(JobEventType.Created).toBe(0);
  expect(JobEventType.Taken).toBe(1);
  // ... etc
});

This would catch accidental enum reordering or value changes that could break compatibility.

website/src/hooks/subsquid/useArbitrators.tsx (1)

14-14: Simplify data extraction.

The ternary with result.data ? (result.data?.arbitrators as Arbitrator[]) : undefined is redundant—the optional chaining already handles the undefined case.

Apply this diff:

-      data: result.data ? (result.data?.arbitrators as Arbitrator[]) : undefined,
+      data: result.data?.arbitrators as Arbitrator[] | undefined,
website/__tests__/hooks/subsquid/useUser.test.tsx (1)

6-16: DRY the mock: reuse shared mockUser from test mocks

Avoid redefining mockUser; import the shared one to keep tests consistent.

Apply within this range:

-const mockUser = {
-  address_: '0x123',
-  name: 'Test User',
-  bio: 'Test bio',
-  avatar: 'https://example.com/avatar.png',
-  publicKey: '0xPublicKey',
-  reputationUp: 10,
-  reputationDown: 2,
-  averageRating: 4.5,
-  numberOfReviews: 5,
-};

Also add this import near the top (adjust path if the export name/path differs):

import { mockUser } from '../../setup/mocks/urql';
website/src/app/providers.tsx (2)

72-80: Rename Inititalizers → Initializers (typo)

Minor readability fix.

-const Inititalizers = ({
+const Initializers = ({
   children,
 }: {
   children: React.ReactNode;
 }): React.JSX.Element => {
   useRegisterWebPushNotifications();

   return <>{children}</>;
 }
...
-              <Inititalizers>{children}</Inititalizers>
+              <Initializers>{children}</Initializers>

Also applies to: 91-91


85-96: Optional: tie cache invalidation to URQL by remounting provider

If you want invalidate() to force refetch broadly, remount UrqlProvider keyed by the timestamp.

Example wrapper:

import { useCacheInvalidation } from '@/contexts/CacheInvalidationContext';

function InvalidationUrqlProvider({ children }: { children: React.ReactNode }) {
  const { timestamp } = useCacheInvalidation();
  return (
    <UrqlProvider key={timestamp} value={urqlClient}>
      {children}
    </UrqlProvider>
  );
}

Then use instead of .

website/__tests__/hooks/subsquid/useArbitrators.test.tsx (1)

69-77: Strengthen the default-limit test (optional)

Also assert loading is false and data length > 0 to prove the fallback operated.

website/__tests__/setup/test-utils.tsx (1)

10-15: Type the mock urql client to avoid any

Leverage urql’s Client type for safer mocks.

-import { Provider } from 'urql';
+import { Provider, type Client } from 'urql';
...
-const mockClient = {
+const mockClient: Partial<Client> = {
   executeQuery: vi.fn(() => fromValue({ data: undefined, error: undefined })),
   executeMutation: vi.fn(() => fromValue({ data: undefined, error: undefined })),
   executeSubscription: vi.fn(() => fromValue({ data: undefined, error: undefined })),
-} as any;
+};

And pass as Client when providing:

-<Provider value={mockClient}>
+<Provider value={mockClient as Client}>
website/__tests__/api/market_cap.test.ts (1)

63-69: Optional: assert an exact expected market cap

You have deterministic mocks; assert the precise value instead of ≥ 0 to catch regressions.

subsquid/tests/unit/data-models.test.ts (1)

14-214: Prepare this suite for future unskip: add imports and valid inputs

  • Import model classes (User, Arbitrator, Job, Review, Marketplace, JobRoles, JobTimes).
  • Replace non-hex addresses (e.g., '0xcreator…') with valid checksummed hex strings.
  • Keep describe.skip until reflect-metadata/TypeORM setup is ready.

I can wire a minimal factory-based approach (no decorators) for pure data-shape tests if helpful.

website/src/app/arbitrators/page.tsx (1)

45-51: Handle URQL result.error (GraphQL/network errors) explicitly

Currently only exceptions are caught; URQL returns errors on result.error without throwing. Check and log/guard.

-      const result = await client
-        .query(GET_ARBITRATORS_QUERY, {})
-        .toPromise();
-      const data = result.data;
+      const result = await client.query(GET_ARBITRATORS_QUERY, {}).toPromise();
+      if (result.error) {
+        console.error('Error fetching arbitrators:', result.error);
+        return [];
+      }
+      const data = result.data;
website/src/hooks/subsquid/useArbitratorPublicKeys.tsx (2)

5-9: Hook name mismatch with filename

Function is named useArbitratorsByAddresses in a file named useArbitratorPublicKeys.tsx. Rename for clarity.


27-34: Stale data risk and noisy deps

  • resultMap accumulates across calls; if arbitratorAddresses shrinks, stale keys may persist. Consider resetting map when input set changes.
  • Narrow useMemo deps to result.data, result.fetching, result.error to avoid unnecessary recalcs.

Would you like a patch that resets state when arbitratorAddresses changes?

website/__tests__/api/circulating_supply.test.ts (1)

69-77: Remove or implement the placeholder error test.

This test doesn't actually verify error handling—it just calls GET() and expects a response. The comment acknowledges it's a placeholder.

Either implement meaningful error tests (e.g., mock Contract methods to throw errors) or remove this placeholder:

-  it('should handle errors gracefully', async () => {
-    // This test requires mocking the shared module which is complex
-    // Skip for now as it tests error handling paths
-    const response = await GET();
-
-    // Should return valid response even in error cases
-    expect(response).toBeDefined();
-    expect(response.status).toBeDefined();
-  });
website/__tests__/pages/TakeJobButton.test.tsx (1)

46-169: Test suite is skipped—enable when ready for TakeJobButton coverage.

The test structure is comprehensive, covering:

  • Rendering states
  • Role-based visibility
  • Contract interactions
  • Loading states
  • Collateral handling

Remove describe.skip when the TakeJobButton implementation is ready for testing:

-describe.skip('TakeJobButton', () => {
+describe('TakeJobButton', () => {
website/__tests__/integration/job-creation-flow.test.tsx (1)

34-173: Enable test suite when integration flow is stable.

The test suite is currently skipped but covers a comprehensive end-to-end workflow including wallet connection, form filling, token approval, and job submission.

Remove describe.skip when the integration flow is ready:

-describe.skip('Job Creation Flow Integration', () => {
+describe('Job Creation Flow Integration', () => {

Comment on lines +3 to +7
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Solidity](https://img.shields.io/badge/Solidity-0.8.28-blue)](https://soliditylang.org)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue)](https://www.typescriptlang.org/)
[![Coverage Status](https://coveralls.io/repos/github/semperai/effectiveacceleration.ai/badge.svg?branch=master)](https://coveralls.io/github/semperai/effectiveacceleration.ai?branch=master)
![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/semperai/effectiveacceleration.ai?utm_source=oss&utm_medium=github&utm_campaign=semperai%2Feffectiveacceleration.ai&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify badge URLs and coverage integration.

The addition of badges improves project visibility and provides quick health indicators. However, ensure that the coverage badge URL is correct and that Coveralls integration is properly configured.

Run the following script to verify the badge URLs:


🏁 Script executed:

#!/bin/bash
# Description: Verify badge URLs are accessible and check for coverage configuration

# Check Coveralls badge URL (note: may return 404 if not set up yet)
echo "Checking Coveralls badge..."
curl -I "https://coveralls.io/repos/github/semperai/effectiveacceleration.ai/badge.svg?branch=master" 2>/dev/null | head -1

# Check if .coveralls.yml exists
if [ -f ".coveralls.yml" ]; then
  echo "✓ .coveralls.yml found"
  cat .coveralls.yml
else
  echo "Note: No .coveralls.yml found - may need to configure Coveralls"
fi

# Check for coverage workflows
echo "Checking for coverage in CI workflows..."
fd -t f -e yml -e yaml . .github/workflows | xargs grep -l "coverage" 2>/dev/null

Length of output: 250


Add Coveralls config and CI workflow for coverage
Coveralls badge URL is reachable (HTTP 302), but there’s no .coveralls.yml and no coverage-related CI workflows. Add a Coveralls configuration file at the repo root and update your CI (e.g., under .github/workflows/) to generate and upload coverage reports so the badge reflects actual metrics.

🤖 Prompt for AI Agents
In README.md around lines 3 to 7, the Coveralls badge is present but the
repository lacks a Coveralls configuration and CI steps to produce/upload
coverage; add a .coveralls.yml at the repo root with basic config (repo_token or
service-specific settings) and create/update a CI workflow under
.github/workflows (e.g., a GitHub Actions YAML) that runs tests, generates a
coverage report (lcov or cobertura), and uploads it to Coveralls using an
official action or uploader; ensure the workflow references the
COVERALLS_REPO_TOKEN (or uses the GitHub Actions integration) stored in
repository secrets and validate the badge URL after a successful run.

Comment on lines +30 to +31
parseEther: vi.fn((value) => BigInt(value) * BigInt(10 ** 18)),
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix BigInt math: avoid BigInt(10 ** 18) precision pitfalls

Using a Number for 10 ** 18 loses precision before BigInt conversion.

Apply:

-    parseEther: vi.fn((value) => BigInt(value) * BigInt(10 ** 18)),
+    parseEther: vi.fn((value) => BigInt(value) * (10n ** 18n)),

Do the same for the duplicate export.

Also applies to: 39-40

🤖 Prompt for AI Agents
In website/__tests__/api/market_cap.test.ts around lines 30-31 (and also lines
39-40), the test stub uses BigInt(10 ** 18) which loses precision because 10 **
18 is a Number before conversion; replace that with a true BigInt expression
such as 10n ** 18n or BigInt("1000000000000000000") in both places (and update
any duplicate export similarly) so parseEther computes BigInt(value) * 10n **
18n (or uses the string BigInt) to avoid precision loss.

import { JobsList } from '../../src/components/Dashboard/JobsList/JobsList';
import userEvent from '@testing-library/user-event';

describe.skip('JobsList Component', () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Enable or remove skipped test suite.

The JobsList test suite is skipped with describe.skip, preventing it from running. Skipped tests mask potential regressions and incomplete features.

Either:

  1. Remove .skip and fix any failing tests to enable the suite
  2. Delete the file if these tests are not yet ready or no longer needed

Run the following to identify all skipped tests in the codebase:


🏁 Script executed:

#!/bin/bash
# Find all skipped test suites and tests
rg -nP --type-add 'tsx:*.tsx' --type tsx "describe\.skip|it\.skip|test\.skip" website/__tests__/

Length of output: 678


Remove or enable the skipped JobsList test suite. The JobsList tests are marked with describe.skip, masking regressions—either remove .skip and fix any failures or delete the file if it’s not ready. Review and address other skipped suites in website/__tests__ to ensure no tests remain unintentionally skipped.

🤖 Prompt for AI Agents
In website/__tests__/components/JobsList.test.tsx around line 7 the test suite
is marked with describe.skip which silences failures; either remove the .skip to
run the suite and fix any resulting test failures, or delete the file if the
component tests aren’t ready. Update the test file accordingly, run the test
runner to confirm the suite passes, and also scan website/__tests__ for other
unintended describe.skip usages and address them the same way.

// With synchronous mocks using fromValue, data is available immediately
expect(result.current.loading).toBe(false);
expect(result.current.data).toHaveLength(1);
expect(result.current.data?.[0].state).toBe(3);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Update the assertion to match the corrected state.

After correcting the mock's state to 2, the assertion should also expect state to be 2.

Apply this diff:

-    expect(result.current.data?.[0].state).toBe(3);
+    expect(result.current.data?.[0].state).toBe(2);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(result.current.data?.[0].state).toBe(3);
expect(result.current.data?.[0].state).toBe(2);
🤖 Prompt for AI Agents
In website/__tests__/hooks/subsquid/useWorkerCompletedJobs.test.tsx around line
44, the assertion still expects state 3 while the mock was corrected to state 2;
update the test expectation to check for state 2 by changing the assertion to
expect(result.current.data?.[0].state).toBe(2).

Comment on lines +7 to +65
beforeEach(() => {
vi.clearAllMocks();
});

it('should return initial dimensions of 0x0', () => {
const { result } = renderHook(() => {
const ref = useRef<HTMLDivElement>(null);
return useDimensions(ref);
});

expect(result.current).toEqual({
width: 0,
height: 0,
});
});

it('should return dimensions from ref element', () => {
const { result } = renderHook(() => {
const ref = useRef<HTMLDivElement>({
offsetWidth: 100,
offsetHeight: 200,
} as HTMLDivElement);
return useDimensions(ref);
});

expect(result.current).toEqual({
width: 100,
height: 200,
});
});

it('should handle null ref', () => {
const { result } = renderHook(() => {
const ref = useRef<HTMLDivElement>(null);
return useDimensions(ref);
});

expect(result.current).toEqual({
width: 0,
height: 0,
});
});

it('should subscribe to window resize events', () => {
const addEventListenerSpy = vi.spyOn(window, 'addEventListener');

renderHook(() => {
const ref = useRef<HTMLDivElement>(null);
return useDimensions(ref);
});

// Check that addEventListener was called with 'resize'
expect(addEventListenerSpy).toHaveBeenCalled();
const calls = addEventListenerSpy.mock.calls;
const resizeCall = calls.find(call => call[0] === 'resize');
expect(resizeCall).toBeDefined();

addEventListenerSpy.mockRestore();
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Ensure spies are always restored

vi.clearAllMocks() resets call history but it doesn’t put the original window.addEventListener/removeEventListener implementations back. Because mockRestore() lives inside individual tests, any early failure will skip it and leak the patched spies into subsequent tests, causing unpredictable follow-on failures. Add a global afterEach with vi.restoreAllMocks() (you already import afterEach) so the original browser APIs are restored regardless of individual test outcomes.

 describe('useDimensions', () => {
   beforeEach(() => {
     vi.clearAllMocks();
   });
+
+  afterEach(() => {
+    vi.restoreAllMocks();
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
beforeEach(() => {
vi.clearAllMocks();
});
it('should return initial dimensions of 0x0', () => {
const { result } = renderHook(() => {
const ref = useRef<HTMLDivElement>(null);
return useDimensions(ref);
});
expect(result.current).toEqual({
width: 0,
height: 0,
});
});
it('should return dimensions from ref element', () => {
const { result } = renderHook(() => {
const ref = useRef<HTMLDivElement>({
offsetWidth: 100,
offsetHeight: 200,
} as HTMLDivElement);
return useDimensions(ref);
});
expect(result.current).toEqual({
width: 100,
height: 200,
});
});
it('should handle null ref', () => {
const { result } = renderHook(() => {
const ref = useRef<HTMLDivElement>(null);
return useDimensions(ref);
});
expect(result.current).toEqual({
width: 0,
height: 0,
});
});
it('should subscribe to window resize events', () => {
const addEventListenerSpy = vi.spyOn(window, 'addEventListener');
renderHook(() => {
const ref = useRef<HTMLDivElement>(null);
return useDimensions(ref);
});
// Check that addEventListener was called with 'resize'
expect(addEventListenerSpy).toHaveBeenCalled();
const calls = addEventListenerSpy.mock.calls;
const resizeCall = calls.find(call => call[0] === 'resize');
expect(resizeCall).toBeDefined();
addEventListenerSpy.mockRestore();
});
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
vi.restoreAllMocks();
});
it('should return initial dimensions of 0x0', () => {
const { result } = renderHook(() => {
const ref = useRef<HTMLDivElement>(null);
return useDimensions(ref);
});
expect(result.current).toEqual({
width: 0,
height: 0,
});
});
🤖 Prompt for AI Agents
In website/__tests__/hooks/useDimensions.test.tsx around lines 7 to 65, the test
suite creates spies on window.addEventListener but only restores them inside
individual tests, risking leaked mocks if a test fails; add a global
afterEach(() => { vi.restoreAllMocks(); }) to ensure all spies/mocks are
restored after each test and remove individual mockRestore() calls in tests so
restoration is centralized and reliable.

Comment on lines +6 to +12
import { useAccount } from 'wagmi';

// Mock wagmi
vi.mock('wagmi', () => ({
...vi.importActual('wagmi'),
useAccount: vi.fn(),
}));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix wagmi mock: import order and async vi.importActual usage

  • useAccount is imported before mocking, and vi.importActual returns a Promise (can’t be spread). Use an async mock factory and a namespace import to ensure mocks bind correctly.

Apply:

-import { useAccount } from 'wagmi';
+import * as wagmi from 'wagmi';
@@
-vi.mock('wagmi', () => ({
-  ...vi.importActual('wagmi'),
-  useAccount: vi.fn(),
-}));
+vi.mock('wagmi', async () => {
+  const actual = await vi.importActual<typeof import('wagmi')>('wagmi');
+  return {
+    ...actual,
+    useAccount: vi.fn(),
+  };
+});
@@
-    (useAccount as any).mockReturnValue({
+    (wagmi.useAccount as any).mockReturnValue({
       address: '0x123',
       isConnected: true,
     });
@@
-    (useAccount as any).mockReturnValue({
+    (wagmi.useAccount as any).mockReturnValue({
       address: undefined,
       isConnected: false,
     });

Also applies to: 25-29, 100-105

🤖 Prompt for AI Agents
In website/__tests__/pages/PostJobPage.test.tsx around lines 6-12 (and similarly
at 25-29 and 100-105), the test currently imports useAccount before mocking
wagmi and attempts to spread vi.importActual (which returns a Promise); change
the mock to use an async mock factory and perform a namespace/mock before
importing useAccount: call vi.mock('wagmi', async () => { const actual = await
vi.importActual('wagmi'); return { ...actual, useAccount: vi.fn() }; }); then
import/useAccount (or import the module under test) after this mock to ensure
mocks bind correctly.

{/* Message Content */}
<div className='prose prose-sm dark:prose-invert max-w-none'>
<Markdown className='text-gray-700 dark:text-gray-300 overflow-wrap-breakword'>
<div className='prose prose-sm dark:prose-invert max-w-none text-gray-700 dark:text-gray-300 overflow-wrap-breakword'>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Invalid Tailwind utility: use break-words instead of overflow-wrap-breakword.

The class overflow-wrap-breakword is not a standard Tailwind CSS utility. Use break-words for proper word breaking.

Apply this diff:

-              <div className='prose prose-sm dark:prose-invert max-w-none text-gray-700 dark:text-gray-300 overflow-wrap-breakword'>
+              <div className='prose prose-sm dark:prose-invert max-w-none text-gray-700 dark:text-gray-300 break-words'>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className='prose prose-sm dark:prose-invert max-w-none text-gray-700 dark:text-gray-300 overflow-wrap-breakword'>
<div className='prose prose-sm dark:prose-invert max-w-none text-gray-700 dark:text-gray-300 break-words'>
🤖 Prompt for AI Agents
In website/src/app/jobs/[id]/JobChat/StatusStates/ResultVerification.tsx around
line 156, replace the invalid Tailwind class `overflow-wrap-breakword` with the
correct utility `break-words`; update the className value on that div to include
`break-words` (and remove the invalid token) so Tailwind applies proper word
wrapping.

Comment on lines +11 to 14
const [result] = useQuery({
query: GET_CREATOR_TAKEN_JOBS,
variables: { creatorAddress, ...(orderBy && { orderBy }) },
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix variable shape for orderBy (expects array) and optionally pause on empty address.

The query declares $orderBy: [JobOrderByInput!], but the code passes orderBy as a scalar. Wrap it in an array when present. Optionally, add pause: !creatorAddress to avoid requests with empty addresses.

Apply this diff:

   const [result] = useQuery({
     query: GET_CREATOR_TAKEN_JOBS,
-    variables: { creatorAddress, ...(orderBy && { orderBy }) },
+    variables: {
+      creatorAddress,
+      ...(orderBy ? { orderBy: [orderBy] } : {}),
+    },
+    pause: !creatorAddress,
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [result] = useQuery({
query: GET_CREATOR_TAKEN_JOBS,
variables: { creatorAddress, ...(orderBy && { orderBy }) },
});
const [result] = useQuery({
query: GET_CREATOR_TAKEN_JOBS,
variables: {
creatorAddress,
...(orderBy ? { orderBy: [orderBy] } : {}),
},
pause: !creatorAddress,
});
🤖 Prompt for AI Agents
In website/src/hooks/subsquid/useCreatorTakenJobs.tsx around lines 11 to 14, the
GraphQL variable $orderBy expects an array but the code sends a scalar; wrap
orderBy in an array when present (e.g., variables: { creatorAddress, ...(orderBy
&& { orderBy: [orderBy] }) }) to match [JobOrderByInput!], and optionally add
pause: !creatorAddress to the useQuery options to prevent firing the query when
creatorAddress is empty.

Removed platform-specific optional dependencies that were causing npm ci to fail in GitHub Actions with Hardhat HH18 error.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants