-
Notifications
You must be signed in to change notification settings - Fork 3
Test pr 253 #255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Test pr 253 #255
Conversation
- 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.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including You can disable this status message by setting the WalkthroughReplace 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
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
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. Comment |
There was a problem hiding this 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: Avoidunknowncast; use typed factory and avoid name shadowing.
unknown as EACCTokenhides type errors andconst 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
useWorkerApplicationshook using URQL mocks. The synchronous assertion pattern (checkingloading=falseimmediately) aligns with thefromValuemock 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
useReviewshook 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 withhttp://orhttps://, 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 testsisImageValid 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 dataGuard 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 handlingCheck 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 handlingHandle 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 importswaitFor 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 semanticsGET_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
waitForutility 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
afterEachutility 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
userEventutility 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
beforeEachhook callsvi.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
afterEachutility 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
waitForutility 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
waitForutility 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
waitForutility 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
waitForutility 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 existingcreateUrqlWrapperhelper for more reliable mocking.The custom
createMockClientfunction 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 existingcreateUrqlWrapperhelper fromwebsite/__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 ofwrapper({ jobs: mockJobs }).website/__tests__/hooks/subsquid/useUsers.test.tsx (1)
1-1: Remove unused import
waitForisn’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 withUserFields(address vs address_)This mock uses
address_, while other tests and the query useaddress. IfUserFieldsdoesn’t aliasaddress→address_, update the mock toaddressto avoid drift.-const mockUser = { - address_: '0x123', +const mockUser = { + address: '0x123',
1-1: Remove unused import
waitForisn’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
afterEachisn’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 blockwrapperor hard‑lock the URQL clientYour 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 depsAvoid
as Job[]by typinguseQuery, 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
Jobtypes.Also applies to: 17-22
website/__tests__/integration/job-creation-flow.test.tsx (2)
87-89: Fix the index or the commentYou'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 withrequirein testsPrefer 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 arbitratorsMerging into previous state can retain arbitrators no longer requested when
arbitratorAddressesshrinks.- 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 avoidanyand shape driftUse generics so
result.data.arbitratorsis 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
GetArbitratorsByAddressesQuerytype.
30-37: Tighten memo depsDepend 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 contractArbitratorYou’re storing Subsquid arbitrator objects (with
address_) intoRecord<string, Arbitrator>whereArbitratoris a contract read type. That’s misleading and can mask field differences.Either:
- define a GraphQL
Arbitratorshape for Subsquid responses, or- convert the Subsquid object to the contract
Arbitratorshape before storing.Also applies to: 22-24
website/__tests__/setup/mocks/urql.tsx (2)
44-51: Improve “no mock found” error messagePrinting 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 mocksIf you use GraphQL documents, type
queryasDocumentNodeand narrowvariablesfor 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.skipto 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:
- Set up the test database configuration
- Create test fixtures and factories for models
- 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:
- Increase the performance threshold to 0.8 to match other categories
- Change assertion level from
warntoerroronce the site consistently meets thresholds to prevent performance regressionsApply 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 alltestscripts 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-minutessetting, 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.
ZeroAddressandZeroHashare imported but never used in this file.Apply this diff:
-import { ZeroAddress, ZeroHash } from 'ethers'; +// No imports needed for this filesubsquid/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:
- Encode sample data using the contract's encoding logic
- Pass the encoded data to the decoder
- 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.tsto 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, andJobTimesclasses without importing them. While the tests are currently skipped, they would fail immediately if unskipped due to undefined references.Consider one of these approaches:
- Add proper imports from your TypeORM models (if you plan to enable these tests soon)
- Remove the placeholder tests until you're ready to implement proper integration tests with database setup
- 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
⛔ Files ignored due to path filters (6)
contract/package-lock.jsonis excluded by!**/package-lock.jsoncontract/yarn.lockis excluded by!**/yarn.lock,!**/*.locksubsquid/package-lock.jsonis excluded by!**/package-lock.jsonwebsite/package-lock.jsonis excluded by!**/package-lock.jsonwebsite/yarn.lockis excluded by!**/yarn.lock,!**/*.lockyarn.lockis 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()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| module.exports = { | ||
| skipFiles: [ | ||
| 'EACCToken.sol', | ||
| 'MarketplaceV1.sol', | ||
| 'MarketplaceV2.sol', | ||
| 'MockSablierLockup.sol', | ||
| 'TreasurySplit.sol', | ||
| 'unicrow/', | ||
| 'interfaces/', | ||
| 'libraries/', | ||
| ] | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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 contractTreasurySplit.sol- appears to be treasury managementMarketplaceV1.solandMarketplaceV2.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 tsLength 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.
| 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); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| 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); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| 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"); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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"); }).
| 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]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| const [result] = useQuery({ | ||
| query: GET_CREATOR_CLOSED_JOBS, | ||
| variables: { creatorAddress, ...(orderBy && { orderBy }) }, | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| const [result] = useQuery({ | ||
| query: GET_CREATOR_COMPLETED_JOBS, | ||
| variables: { creatorAddress, ...(orderBy && { orderBy }) }, | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| const [result] = useQuery({ | ||
| query: GET_CREATOR_OPEN_JOBS, | ||
| variables: { creatorAddress, ...(orderBy && { orderBy }) }, | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| const [result] = useQuery({ | ||
| query: GET_JOB_BY_ID, | ||
| variables: { jobId: id }, | ||
| requestPolicy: 'cache-and-network', | ||
| context: useMemo(() => ({ | ||
| _invalidationTimestamp: timestamp, | ||
| }), [timestamp]), | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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_invalidationTimestampto modify cache keys, or switchrequestPolicyto'network-only'whentimestampchanges. - Optionally, add
pause: !idto avoid running the query with an emptyid.
🤖 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.
There was a problem hiding this 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.fragmentisn’t defined reliably in ethers v6, so this filter will intermittently miss Transfer events. Parse each log viaeaccToken.interface.parseLogand 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 number0fails. Use0n(orBigInt(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: usebreak-wordsinstead ofoverflow-wrap-breakword.This issue was previously flagged and remains unresolved. The class
overflow-wrap-breakwordis 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: usebreak-wordsinstead ofoverflow-wrap-breakword.This issue was previously flagged and remains unresolved. Replace
overflow-wrap-breakwordwith the standard Tailwind utilitybreak-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: usebreak-wordsinstead ofoverflow-wrap-breakword.This issue was previously flagged and remains unresolved. Replace
overflow-wrap-breakwordwithbreak-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: usebreak-wordsinstead ofoverflow-wrap-breakword.This issue was previously flagged and remains unresolved. Replace
overflow-wrap-breakwordwith the standard Tailwind utilitybreak-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: Theanytype 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: Theanytype 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: Theanytype 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
classListoperations (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 commentTreasurySplit.sol- no tests foundExcluding tested contracts from coverage defeats the purpose of coverage reporting. Either:
- Remove the tested contracts from
skipFilesto include them in coverage reports- Add tests for
TreasurySplit.solor 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
creatorAddressas the variable name, but theGET_WORKER_DISPUTED_JOBSquery expectsworker. 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
convertToSecondsuses 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_JOBshould beGET_JOB_BY_IDwith variablejobIdGET_JOBSis now a function and usesoffset/limitinstead offirst/skipGET_USERSusesoffset/limitinstead offirst/skipApply 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
.skipafter 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 theGET_SQUID_STATUSquery schema. According to the query definition,squidStatusonly returns{ height }.website/__tests__/hooks/subsquid/useCreatorClosedJobs.test.tsx (1)
6-17: Mock data state doesn't match the query filter.The
mockJobhasstate: 4, butGET_CREATOR_CLOSED_JOBSfilters forstate_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:
mockJobhasstate: 2, butGET_CREATOR_DISPUTED_JOBSfilters forstate_eq: 1. Update the mock's state to 1.website/src/hooks/subsquid/useCreatorCompletedJobs.tsx (1)
11-14: Fix variable shape fororderBy(expects array) and optionally pause on empty address.The past review comment on this segment remains valid: wrap
orderByin an array to match the query signature[JobOrderByInput!], and consider addingpause: !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
mockJobhasstate: 3, butGET_WORKER_COMPLETED_JOBSfilters forstate_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 toglobal.localStorage.The
localStorageMockis defined but never assigned toglobal.localStorage. Without this assignment, the actual browserlocalStoragewill be used instead of the mock, which could cause test failures in headless environments and won't respect thebeforeEachcleanup.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 toresults, notresultMapInside 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 withwaitForAssertions run before
useEffectsets state. UsewaitFor.-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.mockto avoid binding real wagmi fns.- Mock the custom
useWriteContractWithNotifications(not wagmi’s).- Pass required
sessionKeysprop; removeevents.- 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: PassorderByas 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_invalidationTimestampwon't work without a custom exchange.By default, URQL ignores custom context fields for cache keys. The
_invalidationTimestampwill not bust the cache unless a custom exchange consumes it.Consider one of these approaches:
- Add a custom exchange that reads
_invalidationTimestampand modifies cache keys- Use
requestPolicy: 'network-only'when timestamp changes to force a fresh fetch- Add
pause: !idto prevent running the query whenidis emptyExample 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: PassorderByas 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: PassorderByas 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()withvi.fn()and typehashasHashinstead ofAddress.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.currenttyped asT | null, soRefObject<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 > 5checksrating > 5regardless of theskipReviewflag, whilerating < 0is only checked when not skipping. SinceskipReviewbypasses rating usage entirely (line 71 passes0), 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://orhttps://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
Tokeninterface 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/testingin__tests__/setup/mocks/apollo.ts—remove or convert these to URQL-based mocks.- No direct
react,react-dom, ornextversion 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 exportsmockJob). 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
resultobject, which causes the memo to recompute whenever any property ofresultchanges (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 unusedwaitForimport.The test does not use
waitForsince 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 unusedwaitForimport.The test does not use
waitForsince 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 unusedwaitForimport.The test does not use
waitForsince 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 unusedwaitForimport.The test does not use
waitForsince 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 unusedwaitForimport.The test does not use
waitForsince 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 unusedwaitForimport.The test does not use
waitForsince 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[]) : undefinedis 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 mocksAvoid 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 providerIf 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 anyLeverage 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 capYou 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 URQLresult.error(GraphQL/network errors) explicitlyCurrently only exceptions are caught; URQL returns errors on
result.errorwithout 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 filenameFunction is named
useArbitratorsByAddressesin a file nameduseArbitratorPublicKeys.tsx. Rename for clarity.
27-34: Stale data risk and noisy deps
resultMapaccumulates across calls; ifarbitratorAddressesshrinks, stale keys may persist. Consider resetting map when input set changes.- Narrow
useMemodeps toresult.data,result.fetching,result.errorto avoid unnecessary recalcs.Would you like a patch that resets state when
arbitratorAddresseschanges?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.skipwhen 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.skipwhen the integration flow is ready:-describe.skip('Job Creation Flow Integration', () => { +describe('Job Creation Flow Integration', () => {
| [](https://opensource.org/licenses/MIT) | ||
| [](https://soliditylang.org) | ||
| [](https://www.typescriptlang.org/) | ||
| [](https://coveralls.io/github/semperai/effectiveacceleration.ai?branch=master) | ||
|  |
There was a problem hiding this comment.
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/nullLength 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.
| parseEther: vi.fn((value) => BigInt(value) * BigInt(10 ** 18)), | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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:
- Remove
.skipand fix any failing tests to enable the suite - 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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).
| 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(); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| import { useAccount } from 'wagmi'; | ||
|
|
||
| // Mock wagmi | ||
| vi.mock('wagmi', () => ({ | ||
| ...vi.importActual('wagmi'), | ||
| useAccount: vi.fn(), | ||
| })); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix wagmi mock: import order and async vi.importActual usage
useAccountis imported before mocking, andvi.importActualreturns 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'> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| <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.
| const [result] = useQuery({ | ||
| query: GET_CREATOR_TAKEN_JOBS, | ||
| variables: { creatorAddress, ...(orderBy && { orderBy }) }, | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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>
Summary by CodeRabbit
New Features
Improvements
Documentation
Tests
Chores