Troubleshooting
This guide helps you diagnose and fix common issues when using Acquiescence.
Common Issues
Element Not Found
Problem: You get errors about elements not being found or being null.
Symptoms:
const button = document.querySelector('#my-button'); // null
await inspector.waitForInteractionReady(button, 'click', 5000); // Error!Solutions:
- Wait for element to exist:
async function waitForElement(selector: string, timeout = 5000) {
const start = Date.now();
while (Date.now() - start < timeout) {
const element = document.querySelector(selector);
if (element) return element;
await new Promise(resolve => setTimeout(resolve, 100));
}
throw new Error(`Element ${selector} not found`);
}
const button = await waitForElement('#my-button');- Check your selector:
// Verify element exists in console
console.log(document.querySelector('#my-button'));
// Try different selectors
document.querySelector('[data-testid="my-button"]');
document.querySelector('.my-button-class');- Check for Shadow DOM:
// Element might be inside shadow root
const host = document.querySelector('my-component');
const button = host?.shadowRoot?.querySelector('#my-button');Element Not Connected Error
Problem: Error message: "element not connected"
Symptoms:
// Error: element not connected
const result = await inspector.queryElementStates(button, ['visible']);Cause: The element was removed from the DOM between selection and state check.
Solutions:
- Store reference to stable element:
// ❌ Element might be replaced
function getButton() {
return document.querySelector('#button');
}
// Use fresh reference
await inspector.waitForInteractionReady(getButton(), 'click', 5000);- Re-query if element might be replaced:
async function clickWithRetry(selector: string) {
for (let i = 0; i < 3; i++) {
try {
const element = document.querySelector(selector);
if (!element) throw new Error('Element not found');
await inspector.waitForInteractionReady(element, 'click', 5000);
element.click();
return;
} catch (error) {
if (error.message.includes('not connected') && i < 2) {
await new Promise(resolve => setTimeout(resolve, 500));
continue;
}
throw error;
}
}
}Timeout Waiting for Interaction
Problem: Error message: "timeout waiting for interaction to be ready"
Symptoms:
// Throws after 5 seconds
await inspector.waitForInteractionReady(element, 'click', 5000);Diagnose:
// Check what state is preventing readiness
async function diagnoseInteraction(element: Element, type: ElementInteractionType) {
console.log('Diagnosing element:', element);
// Check each required state
const visible = await inspector.queryElementState(element, 'visible');
console.log('Visible:', visible.matches, visible.received);
const enabled = await inspector.queryElementState(element, 'enabled');
console.log('Enabled:', enabled.matches, enabled.received);
const inview = await inspector.queryElementState(element, 'inview');
console.log('In View:', inview.matches, inview.received);
const stable = await inspector.queryElementStates(element, ['stable']);
console.log('Stable:', stable.status);
if (type === 'type' || type === 'clear') {
const editable = await inspector.queryElementState(element, 'editable');
console.log('Editable:', editable.matches, editable.received);
}
}
await diagnoseInteraction(element, 'click');Solutions:
- Element not visible:
// Check if element has display:none or visibility:hidden
const style = window.getComputedStyle(element);
console.log('Display:', style.display);
console.log('Visibility:', style.visibility);
console.log('Opacity:', style.opacity);- Element is disabled:
// Check if disabled
console.log('Disabled attr:', element.hasAttribute('disabled'));
console.log('Aria-disabled:', element.getAttribute('aria-disabled'));
// Check parent for aria-disabled
let parent = element.parentElement;
while (parent) {
if (parent.getAttribute('aria-disabled') === 'true') {
console.log('Parent is aria-disabled:', parent);
break;
}
parent = parent.parentElement;
}- Element not in viewport:
// Check viewport position
const rect = element.getBoundingClientRect();
console.log('Element rect:', rect);
console.log('Viewport size:', {
width: window.innerWidth,
height: window.innerHeight
});
// Try scrolling manually
element.scrollIntoView({ block: 'center', inline: 'center' });- Element not stable:
// Check if element is animating
const style = window.getComputedStyle(element);
console.log('Animation:', style.animation);
console.log('Transition:', style.transition);
// Wait longer for animations to complete
await inspector.waitForInteractionReady(element, 'click', 10000);Element Obscured by Another Element
Problem: Error mentioning an element is obscured by another element.
Symptoms:
// Error: <div class="overlay"> from <dialog> subtree
await inspector.getElementClickPoint(button);Cause: Another element is covering the target element at its center point.
Solutions:
- Close obstructing elements:
// Close modal/dialog first
const modal = document.querySelector('.modal');
if (modal) {
modal.style.display = 'none';
}
// Now try interaction
await inspector.waitForInteractionReady(button, 'click', 5000);- Use offset to click different part:
// Click offset from center to avoid obstruction
const result = await inspector.isInteractionReady(
button,
'click',
{ x: 20, y: 0 } // 20px right of center
);- Wait for obstruction to disappear:
// Wait for overlay to disappear
async function waitForElementGone(selector: string, timeout = 5000) {
const start = Date.now();
while (Date.now() - start < timeout) {
const element = document.querySelector(selector);
if (!element || !inspector.isElementVisible(element)) {
return;
}
await new Promise(resolve => setTimeout(resolve, 100));
}
}
await waitForElementGone('.loading-overlay');
await inspector.waitForInteractionReady(button, 'click', 5000);Element Hidden by Overflow
Problem: Error message: "element is not in view port, and cannot be scrolled into view due to overflow"
Cause: Element is hidden by an ancestor with overflow: hidden.
Solutions:
- Check overflow styles:
// Find which parent has overflow:hidden
let parent = element.parentElement;
while (parent) {
const style = window.getComputedStyle(parent);
if (style.overflow === 'hidden' || style.overflowX === 'hidden' || style.overflowY === 'hidden') {
console.log('Parent with overflow:hidden:', parent);
}
parent = parent.parentElement;
}- Temporarily change overflow:
// Store original overflow
const parent = element.closest('.container');
const originalOverflow = parent.style.overflow;
// Change to allow scrolling
parent.style.overflow = 'visible';
// Interact
await inspector.waitForInteractionReady(element, 'click', 5000);
element.click();
// Restore
parent.style.overflow = originalOverflow;Editable State Error
Problem: Error: "Element is not an <input>, <textarea>, <select> or [contenteditable]..."
Cause: Trying to check editable state on a non-input element.
Solutions:
- Check correct element type:
// Verify element type
console.log('Tag name:', element.tagName);
console.log('Is contenteditable:', element.hasAttribute('contenteditable'));
// Only check editable for appropriate elements
if (['INPUT', 'TEXTAREA', 'SELECT'].includes(element.tagName) ||
element.hasAttribute('contenteditable')) {
await inspector.queryElementState(element, 'editable');
}- Use correct interaction type:
// For buttons, use 'click' not 'type'
await inspector.waitForInteractionReady(button, 'click', 5000); // ✓
// For inputs, use 'type'
await inspector.waitForInteractionReady(input, 'type', 5000); // ✓Performance Issues
Slow State Checks
Problem: State checks taking too long.
Solutions:
- Reduce unnecessary checks:
// ❌ Too many checks
await inspector.queryElementStates(element,
['visible', 'enabled', 'editable', 'stable', 'inview']
);
// ✅ Only necessary checks
await inspector.queryElementStates(element, ['visible', 'enabled']);- Skip stability when not needed:
// Static elements don't need stability
await inspector.queryElementStates(staticElement, ['visible']);- Use helper methods for sync checks:
// ❌ Async when sync would work
const result = await inspector.queryElementState(element, 'visible');
// ✅ Sync helper
const visible = inspector.isElementVisible(element);Memory Leaks
Problem: Memory usage grows over time.
Cause: Holding references to removed elements.
Solutions:
- Don't store element references long-term:
// ❌ Storing elements
class PageObject {
private button = document.querySelector('#button');
}
// ✅ Query when needed
class PageObject {
getButton() {
return document.querySelector('#button');
}
}- Clean up in tests:
afterEach(() => {
// Clear DOM
document.body.innerHTML = '';
// Force garbage collection in Node.js test environments
if (global.gc) {
global.gc();
}
});Browser-Specific Issues
Safari Issues
Problem: Behavior differs in Safari.
Solutions:
- Longer timeouts in Safari:
const timeout = navigator.userAgent.includes('Safari') ? 10000 : 5000;
await inspector.waitForInteractionReady(element, 'click', timeout);- Check Intersection Observer support:
if (!window.IntersectionObserver) {
console.error('IntersectionObserver not supported');
// Use polyfill or alternative approach
}Firefox Issues
Problem: Different hit testing behavior.
Solutions:
Account for different
elementsFromPointbehavior:- Acquiescence handles this internally, but be aware of possible differences
Use longer stability checks:
// Firefox sometimes needs extra frame for stability
await inspector.queryElementStates(element, ['stable']);Debugging Tools
Visual Debugging
function highlightElement(element: Element, color = 'red') {
element.style.outline = `3px solid ${color}`;
setTimeout(() => {
element.style.outline = '';
}, 2000);
}
// Highlight element being checked
highlightElement(button);
await inspector.waitForInteractionReady(button, 'click', 5000);Comprehensive Diagnostic
async function fullDiagnostic(element: Element) {
console.log('=== Element Diagnostic ===');
console.log('Element:', element);
console.log('Connected:', element.isConnected);
console.log('Tag:', element.tagName);
console.log('ID:', element.id);
console.log('Classes:', element.className);
const rect = element.getBoundingClientRect();
console.log('Rect:', rect);
const style = window.getComputedStyle(element);
console.log('Display:', style.display);
console.log('Visibility:', style.visibility);
console.log('Opacity:', style.opacity);
console.log('Position:', style.position);
console.log('\n=== State Checks ===');
console.log('Visible:', inspector.isElementVisible(element));
console.log('Disabled:', inspector.isElementDisabled(element));
console.log('Scrollable:', inspector.isElementScrollable(element));
const inView = await inspector.isElementInViewPort(element);
console.log('In Viewport:', inView);
console.log('\n=== Interaction Check ===');
try {
const result = await inspector.isInteractionReady(element, 'click');
console.log('Interaction Ready:', result);
} catch (error) {
console.log('Interaction Error:', error.message);
}
}Getting Help
If you're still stuck:
Check the examples:
Review the API documentation:
Provide minimal reproduction:
- Create a minimal HTML page demonstrating the issue
- Include the specific Acquiescence code that's failing
- Note which browser and version
File an issue:
- Include diagnostic output
- Provide expected vs actual behavior
- Share any error messages
Next Steps
- Review Best Practices
- See Examples
- Check API Reference