fix: fix: wallet connector panel not rendering at standard viewports — blocks all user funnels (#1156)

Root cause: the test wallet provider's eth_accounts and getProviderState
always returned the account address regardless of connection state. This
caused wagmi to auto-connect via EIP-6963 provider discovery, skipping
the 'disconnected' status entirely. As a result, .connect-button--disconnected
never rendered and .connectors-element was never shown.

Changes:
- wallet-provider: eth_accounts returns [] when not connected (EIP-1193 compliant)
- wallet-provider: getProviderState returns empty accounts when not connected
- All wallet connection helpers: handle auto-reconnect case, increase timeout
  for wagmi to settle into disconnected state (5s → 10s)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
johba 2026-03-25 09:29:53 +00:00
parent f2e7369ec5
commit db9e99f4c0
5 changed files with 117 additions and 88 deletions

View file

@ -43,10 +43,17 @@ export async function connectWallet(page: Page): Promise<void> {
const screenWidth = await page.evaluate(() => window.screen.width); const screenWidth = await page.evaluate(() => window.screen.width);
console.log(`[wallet] screen.width = ${screenWidth}`); console.log(`[wallet] screen.width = ${screenWidth}`);
// Check if wallet is already connected (e.g. wagmi auto-reconnected from storage)
const connectedButton = page.locator('.connect-button--connected').first();
if (await connectedButton.isVisible({ timeout: 1_000 }).catch(() => false)) {
console.log('[wallet] Wallet already connected (auto-reconnect)');
return;
}
let panelOpened = false; let panelOpened = false;
const connectButton = page.locator('.connect-button--disconnected').first(); const connectButton = page.locator('.connect-button--disconnected').first();
if (await connectButton.isVisible({ timeout: 5_000 })) { if (await connectButton.isVisible({ timeout: 10_000 })) {
console.log('[wallet] Found desktop Connect button, clicking...'); console.log('[wallet] Found desktop Connect button, clicking...');
await connectButton.click(); await connectButton.click();
panelOpened = true; panelOpened = true;

View file

@ -93,49 +93,54 @@ test.describe('Acquire & Stake', () => {
// We expect the desktop Connect button to be visible. // We expect the desktop Connect button to be visible.
console.log('[TEST] Looking for Connect button...'); console.log('[TEST] Looking for Connect button...');
// Desktop Connect button // Check if wallet already connected (wagmi may auto-reconnect from storage)
const connectButton = page.locator('.connect-button--disconnected').first(); const alreadyConnected = page.locator('.connect-button--connected').first();
if (await alreadyConnected.isVisible({ timeout: 1_000 }).catch(() => false)) {
let panelOpened = false; console.log('[TEST] Wallet already connected (auto-reconnect), skipping connect flow');
// Wait for the Connect button with a reasonable timeout
if (await connectButton.isVisible({ timeout: 5_000 })) {
console.log('[TEST] Found desktop Connect button, clicking...');
await connectButton.click();
panelOpened = true;
} else { } else {
// Debug: Log current screen.width and navbar-end contents // Desktop Connect button — wait up to 10s for wagmi to settle into disconnected state
const screenWidth = await page.evaluate(() => window.screen.width); const connectButton = page.locator('.connect-button--disconnected').first();
const navbarEndHtml = await page.locator('.navbar-end').innerHTML().catch(() => 'not found');
console.log(`[TEST] DEBUG: screen.width = ${screenWidth}`);
console.log(`[TEST] DEBUG: navbar-end HTML = ${navbarEndHtml.substring(0, 500)}`);
console.log('[TEST] Connect button not visible - checking for mobile fallback...');
// Fallback to mobile login icon (SVG in navbar-end when disconnected) let panelOpened = false;
const mobileLoginIcon = page.locator('.navbar-end svg').first();
if (await mobileLoginIcon.isVisible({ timeout: 2_000 })) { if (await connectButton.isVisible({ timeout: 10_000 })) {
console.log('[TEST] Found mobile login icon, clicking...'); console.log('[TEST] Found desktop Connect button, clicking...');
await mobileLoginIcon.click(); await connectButton.click();
panelOpened = true; panelOpened = true;
} else { } else {
console.log('[TEST] No Connect button or mobile icon visible - wallet may already be connected'); // Debug: Log current screen.width and navbar-end contents
const screenWidth = await page.evaluate(() => window.screen.width);
const navbarEndHtml = await page.locator('.navbar-end').innerHTML().catch(() => 'not found');
console.log(`[TEST] DEBUG: screen.width = ${screenWidth}`);
console.log(`[TEST] DEBUG: navbar-end HTML = ${navbarEndHtml.substring(0, 500)}`);
console.log('[TEST] Connect button not visible - checking for mobile fallback...');
// Fallback to mobile login icon (SVG in navbar-end when disconnected)
const mobileLoginIcon = page.locator('.navbar-end svg').first();
if (await mobileLoginIcon.isVisible({ timeout: 2_000 })) {
console.log('[TEST] Found mobile login icon, clicking...');
await mobileLoginIcon.click();
panelOpened = true;
} else {
console.log('[TEST] No Connect button or mobile icon visible - wallet may already be connected');
}
} }
}
if (panelOpened) { if (panelOpened) {
// eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for Ponder indexing lag and UI re-renders after on-chain transactions in E2E tests. See AGENTS.md #Engineering Principles.
await page.waitForTimeout(1_000);
// Look for the injected wallet connector in the slideout panel
console.log('[TEST] Looking for wallet connector in panel...');
const injectedConnector = page.locator('.connectors-element').first();
if (await injectedConnector.isVisible({ timeout: 5_000 })) {
console.log('[TEST] Clicking first wallet connector...');
await injectedConnector.click();
// eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for Ponder indexing lag and UI re-renders after on-chain transactions in E2E tests. See AGENTS.md #Engineering Principles. // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for Ponder indexing lag and UI re-renders after on-chain transactions in E2E tests. See AGENTS.md #Engineering Principles.
await page.waitForTimeout(2_000); await page.waitForTimeout(1_000);
} else {
console.log('[TEST] WARNING: No wallet connector found in panel'); // Look for the injected wallet connector in the slideout panel
console.log('[TEST] Looking for wallet connector in panel...');
const injectedConnector = page.locator('.connectors-element').first();
if (await injectedConnector.isVisible({ timeout: 5_000 })) {
console.log('[TEST] Clicking first wallet connector...');
await injectedConnector.click();
// eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for Ponder indexing lag and UI re-renders after on-chain transactions in E2E tests. See AGENTS.md #Engineering Principles.
await page.waitForTimeout(2_000);
} else {
console.log('[TEST] WARNING: No wallet connector found in panel');
}
} }
} }

View file

@ -114,49 +114,54 @@ test.describe('Max Stake All Tax Rates', () => {
// We expect the desktop Connect button to be visible. // We expect the desktop Connect button to be visible.
console.log('[TEST] Looking for Connect button...'); console.log('[TEST] Looking for Connect button...');
// Desktop Connect button // Check if wallet already connected (wagmi may auto-reconnect from storage)
const connectButton = page.locator('.connect-button--disconnected').first(); const alreadyConnected = page.locator('.connect-button--connected').first();
if (await alreadyConnected.isVisible({ timeout: 1_000 }).catch(() => false)) {
let panelOpened = false; console.log('[TEST] Wallet already connected (auto-reconnect), skipping connect flow');
// Wait for the Connect button with a reasonable timeout
if (await connectButton.isVisible({ timeout: 5_000 })) {
console.log('[TEST] Found desktop Connect button, clicking...');
await connectButton.click();
panelOpened = true;
} else { } else {
// Debug: Log current screen.width and navbar-end contents // Desktop Connect button — wait up to 10s for wagmi to settle into disconnected state
const screenWidth = await page.evaluate(() => window.screen.width); const connectButton = page.locator('.connect-button--disconnected').first();
const navbarEndHtml = await page.locator('.navbar-end').innerHTML().catch(() => 'not found');
console.log(`[TEST] DEBUG: screen.width = ${screenWidth}`);
console.log(`[TEST] DEBUG: navbar-end HTML = ${navbarEndHtml.substring(0, 500)}`);
console.log('[TEST] Connect button not visible - checking for mobile fallback...');
// Fallback to mobile login icon (SVG in navbar-end when disconnected) let panelOpened = false;
const mobileLoginIcon = page.locator('.navbar-end svg').first();
if (await mobileLoginIcon.isVisible({ timeout: 2_000 })) { if (await connectButton.isVisible({ timeout: 10_000 })) {
console.log('[TEST] Found mobile login icon, clicking...'); console.log('[TEST] Found desktop Connect button, clicking...');
await mobileLoginIcon.click(); await connectButton.click();
panelOpened = true; panelOpened = true;
} else { } else {
console.log('[TEST] No Connect button or mobile icon visible - wallet may already be connected'); // Debug: Log current screen.width and navbar-end contents
const screenWidth = await page.evaluate(() => window.screen.width);
const navbarEndHtml = await page.locator('.navbar-end').innerHTML().catch(() => 'not found');
console.log(`[TEST] DEBUG: screen.width = ${screenWidth}`);
console.log(`[TEST] DEBUG: navbar-end HTML = ${navbarEndHtml.substring(0, 500)}`);
console.log('[TEST] Connect button not visible - checking for mobile fallback...');
// Fallback to mobile login icon (SVG in navbar-end when disconnected)
const mobileLoginIcon = page.locator('.navbar-end svg').first();
if (await mobileLoginIcon.isVisible({ timeout: 2_000 })) {
console.log('[TEST] Found mobile login icon, clicking...');
await mobileLoginIcon.click();
panelOpened = true;
} else {
console.log('[TEST] No Connect button or mobile icon visible - wallet may already be connected');
}
} }
}
if (panelOpened) { if (panelOpened) {
// eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for Ponder indexing lag and UI re-renders after on-chain transactions in E2E tests. See AGENTS.md #Engineering Principles.
await page.waitForTimeout(1_000);
// Look for the injected wallet connector in the slideout panel
console.log('[TEST] Looking for wallet connector in panel...');
const injectedConnector = page.locator('.connectors-element').first();
if (await injectedConnector.isVisible({ timeout: 5_000 })) {
console.log('[TEST] Clicking first wallet connector...');
await injectedConnector.click();
// eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for Ponder indexing lag and UI re-renders after on-chain transactions in E2E tests. See AGENTS.md #Engineering Principles. // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for Ponder indexing lag and UI re-renders after on-chain transactions in E2E tests. See AGENTS.md #Engineering Principles.
await page.waitForTimeout(2_000); await page.waitForTimeout(1_000);
} else {
console.log('[TEST] WARNING: No wallet connector found in panel'); // Look for the injected wallet connector in the slideout panel
console.log('[TEST] Looking for wallet connector in panel...');
const injectedConnector = page.locator('.connectors-element').first();
if (await injectedConnector.isVisible({ timeout: 5_000 })) {
console.log('[TEST] Clicking first wallet connector...');
await injectedConnector.click();
// eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for Ponder indexing lag and UI re-renders after on-chain transactions in E2E tests. See AGENTS.md #Engineering Principles.
await page.waitForTimeout(2_000);
} else {
console.log('[TEST] WARNING: No wallet connector found in panel');
}
} }
} }

View file

@ -148,17 +148,29 @@ export async function connectWallet(page: Page): Promise<void> {
window.dispatchEvent(new Event('resize')); window.dispatchEvent(new Event('resize'));
}); });
// Try desktop Connect button first // Wait for wagmi to settle — the connect button or connected display must appear.
// After the wallet-provider fix, eth_accounts returns [] when not connected,
// so wagmi should land on 'disconnected' status and render the connect button.
const connectButton = page.locator('.connect-button--disconnected').first(); const connectButton = page.locator('.connect-button--disconnected').first();
const connectedButton = page.locator('.connect-button--connected').first();
if (await connectButton.isVisible({ timeout: 5_000 })) { // Wait for either the disconnect button (normal case) or connected button (auto-reconnect)
console.log('[HELPER] Found desktop Connect button'); const desktopButton = page.locator('.connect-button--disconnected, .connect-button--connected').first();
await connectButton.click();
// Wait for the connector panel to open — .connectors-element appearing is the observable event if (await desktopButton.isVisible({ timeout: 10_000 })) {
const injectedConnector = page.locator('.connectors-element').first(); if (await connectedButton.isVisible({ timeout: 500 }).catch(() => false)) {
await injectedConnector.waitFor({ state: 'visible', timeout: 10_000 }); // Wallet already connected (e.g. wagmi reconnected from storage) — skip connect flow
console.log('[HELPER] Clicking wallet connector...'); console.log('[HELPER] Wallet already connected (auto-reconnect)');
await injectedConnector.click(); } else {
// Desktop connect button found — click to open connector panel
console.log('[HELPER] Found desktop Connect button');
await connectButton.click();
// Wait for the connector panel to open — .connectors-element appearing is the observable event
const injectedConnector = page.locator('.connectors-element').first();
await injectedConnector.waitFor({ state: 'visible', timeout: 10_000 });
console.log('[HELPER] Clicking wallet connector...');
await injectedConnector.click();
}
} else { } else {
// Try mobile fallback // Try mobile fallback
const mobileLoginIcon = page.locator('.navbar-end svg').first(); const mobileLoginIcon = page.locator('.navbar-end svg').first();

View file

@ -107,7 +107,7 @@ export async function createWalletContext(
emit('accountsChanged', [account]); emit('accountsChanged', [account]);
return [account]; return [account];
case 'eth_accounts': case 'eth_accounts':
return [account]; return connected ? [account] : [];
case 'eth_chainId': case 'eth_chainId':
return cidHex; return cidHex;
case 'net_version': case 'net_version':
@ -160,7 +160,7 @@ export async function createWalletContext(
requestPermissions: () => requestPermissions: () =>
Promise.resolve([{ parentCapability: 'eth_accounts' }]), Promise.resolve([{ parentCapability: 'eth_accounts' }]),
getProviderState: async () => ({ getProviderState: async () => ({
accounts: [account], accounts: connected ? [account] : [],
chainId: cidHex, chainId: cidHex,
isConnected: connected, isConnected: connected,
}), }),