Skip to content

E2E tests + governance optimistic actions + proposal redirect#964

Open
lcamargof wants to merge 1 commit intomasterfrom
feature/e2e
Open

E2E tests + governance optimistic actions + proposal redirect#964
lcamargof wants to merge 1 commit intomasterfrom
feature/e2e

Conversation

@lcamargof
Copy link
Copy Markdown
Collaborator

Summary

  • E2E infrastructure: Playwright test suite with fully mocked network (API, subgraph, RPC). Includes EIP-6963 wallet mock, multicall3 ABI decoder, and auto-mock fixtures. 8 governance tests covering list rendering, proposal detail states, and navigation.
  • Proposal creation redirect: After creating a proposal, users are now redirected immediately to the proposal detail page instead of waiting 10s. The ProposalCreated event is parsed from the receipt to get the proposal ID, and an optimistic ProposalDetail is pre-seeded. useProposalDetail polls the subgraph every 3s (capped at 10 retries) until real data arrives.
  • Optimistic actions: Vote, queue, and execute operations now update the UI instantly via Jotai write atoms. The governance list is also patched optimistically. A 30s scheduled refetch ensures eventual consistency.
  • Governance refactoring: proposal-detail-stats no longer makes an on-chain proposalVotes() RPC call — it reads from the atom (subgraph + optimistic data). The proposal updater was simplified: removed the key-based remount hack and proposalRefreshFnAtom, replaced with deadline-aware polling (1s in last 5 min, 10s otherwise).
  • Defensive fixes: basket ?? [] guards in discover components, governance.ts gets optional currentTime param for testability, data-testid attributes for e2e selectors.

Tests

  • 17 unit tests for getProposalState (governance.test.ts)
  • 12 unit tests for optimistic actions (optimistic-actions.test.ts)
  • 8 Playwright e2e tests for governance flows

…sal redirect

- Add Playwright e2e test infrastructure with fully mocked network (API, subgraph, RPC)
- Add governance e2e tests (8 tests: list rendering, proposal detail, navigation)
- Replace setTimeout(10000) hacks in submit-proposal buttons with immediate redirect
- Parse ProposalCreated event to navigate directly to proposal detail page
- Add optimistic action atoms for vote/queue/execute (instant UI feedback)
- Refactor proposal-detail-stats to use atom data instead of on-chain RPC call
- Simplify proposal updater with deadline-aware polling (1s near deadline, 10s otherwise)
- Add useProposalDetail polling with retry cap for just-created proposals
- Add governance.ts unit tests (17 tests) and optimistic-actions tests (12 tests)
- Add defensive basket/performance null guards in discover components
- Add data-testid attributes for e2e selectors across UI components
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying register-app with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4aa36ce
Status: ✅  Deploy successful!
Preview URL: https://a917e4c7.register-app.pages.dev
Branch Preview URL: https://feature-e2e.register-app.pages.dev

View logs

})

// Merkl campaign API — prevents real network calls from overview page
await page.route('**/api.merkl.xyz/**', (route) => {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Merkl? we no longer use it, pretty sure, this is stale code

// Discover DTF list — only return data for Base (8453), empty for other chains.
if (pathname.includes('/discover/dtf')) {
const chainId = new URL(url).searchParams.get('chainId')
const data = chainId === '8453' ? discoverDtfData : []
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

dont use magic numbers, we have the chainIDs stored on constants

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Also, pretty sure latest endpoint is chain independant and it returns all the indexDTFs

export async function installTestWallet(
page: Page,
config: MockWalletConfig = {
address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

wasnt this a constant?

page: Page,
config: MockWalletConfig = {
address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chainId: 8453,
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

use constant

page: Page,
config: MockWalletConfig = {
address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chainId: 8453,
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Also make sure to support multichain on our mock provider, we will want to test with ChainId.Mainnet, ChainId.Base and ChainId.BSC

} from '../optimistic-actions'
import { Address } from 'viem'

const makeProposalDetail = (
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

lets grab a REAL proposal detail data for this mock


const { writeContract, isPending, data, error } = useWriteContract()
const { isSuccess } = useWaitForTransactionReceipt({
const targets = useMemo(() => {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

is this robust enough?

targets,
calldatas: calldatas ?? [],
description: description ?? '',
govAddress: (govAddress ?? '0x') as Address,
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

govAddress and Proposer MUST be there, this is to fragile

proposer: (wallet ?? '0x') as Address,
})

const handleSubmit = () => {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think handleSubmit is the spot where we need to save the state for the optimisticActions data, everything must be here, voter, gov address, targets, etc

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

it may actually just be updating a ref

receipt,
targets: proposalData?.targets ?? [],
calldatas: proposalData?.calldatas ?? [],
description: description ?? '',
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

same, this thing is fragile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant