PSA/docs/ui/ui_automation_troubleshooting.md
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

770 lines
20 KiB
Markdown

# UI Automation Troubleshooting Guide
## Overview
This guide helps troubleshoot common issues with the UI Automation and Reflection system. It covers problems with component registration, UI state visibility, automation server connectivity, and debugging techniques.
## Quick Diagnostic Tools
### 1. Browser Console UI State Inspection
Inspect current component registration directly in the browser console via `window.__UI_STATE__`:
```javascript
// In the browser console:
window.__UI_STATE__ // Full UI state object
JSON.stringify(window.__UI_STATE__, null, 2) // Pretty-printed JSON output
Object.keys(window.__UI_STATE__).length // Component count
```
### 2. Claude Code Slash Command
Use the `/ui-state` command in Claude Code to get automated analysis:
```
/ui-state
```
This will run the dump tool and provide AI-powered analysis of your UI state.
### 3. Browser Console Logging
Enable detailed logging by checking browser console for UI reflection messages:
- `🔄 [UI-STATE]` - Component registration updates
- `🔌 [WEBSOCKET]` - WebSocket connection status
- `📡 [UI_STATE_UPDATE]` - State change broadcasts
## Common Issues and Solutions
### Issue 1: Components Not Showing in UI State
**Symptoms:**
- UI state dump shows only sidebar/navigation components
- Screen-specific components are missing
- Empty or minimal component count
**Root Causes & Solutions:**
#### A. Missing UI Reflection Integration
**Problem:** Component doesn't use `useAutomationIdAndRegister` hook.
**Solution:** Add UI reflection to the component:
```tsx
// Before: No UI reflection
function MyComponent() {
return (
<div>
<button id="my-button">Click me</button>
</div>
);
}
// After: With UI reflection
function MyComponent() {
const { automationIdProps } = useAutomationIdAndRegister({
id: 'my-component',
type: 'container',
label: 'My Component'
});
const { automationIdProps: buttonProps } = useAutomationIdAndRegister({
id: 'my-button',
type: 'button',
label: 'Click me'
});
return (
<ReflectionContainer {...automationIdProps}>
<div>
<button {...buttonProps}>Click me</button>
</div>
</ReflectionContainer>
);
}
```
#### B. Incorrect Import Paths
**Problem:** Import paths are wrong, causing hook to be undefined.
**Error Message:**
```
TypeError: useAutomationIdAndRegister is not a function
```
**Solution:** Use correct import paths:
```tsx
// Correct imports
import { useAutomationIdAndRegister } from 'packages/ui/src/ui-reflection/useAutomationIdAndRegister';
import { ReflectionContainer } from 'packages/ui/src/ui-reflection/ReflectionContainer';
```
#### C. Missing ReflectionContainer Wrapper
**Problem:** Components are registered but not properly nested.
**Solution:** Wrap main content with ReflectionContainer:
```tsx
function PageComponent() {
const { automationIdProps: pageProps } = useAutomationIdAndRegister({
id: 'my-page',
type: 'container',
label: 'My Page'
});
return (
<ReflectionContainer {...pageProps}>
{/* All page content goes here */}
<div className="page-content">
{/* ... */}
</div>
</ReflectionContainer>
);
}
```
### Issue 2: Automation Server Connection Problems
**Symptoms:**
- `window.__UI_STATE__` returns undefined or empty
- WebSocket connection failures in browser console
- UI state not updating in real-time
**Diagnostic Steps:**
1. **Check Automation Server Status:**
```bash
# Check if automation server is running
curl http://localhost:4000/api/ui-state
```
2. **Verify Server Logs:**
```bash
# In the main project directory
docker-compose logs | grep automation
```
3. **Check WebSocket Connection:**
Open browser console and look for:
```
[WEBSOCKET] 🔌 Client connected
[WEBSOCKET] 📡 UI_STATE_UPDATE received
```
**Solutions:**
#### A. Start Automation Server
```bash
# Start the automation server
cd tools/ai-automation
npm start
```
#### B. Check Port Conflicts
If port 4000 is in use, update configuration:
```bash
# Check what's using port 4000
lsof -i :4000
# Kill conflicting process if needed
kill -9 <PID>
```
#### C. Restart Docker Services
```bash
# Restart all services
docker-compose down
docker-compose up -d
```
### Issue 3: UI State Not Updating
**Symptoms:**
- Components show in initial dump but don't update
- State changes not reflected in UI state
- Stale component metadata
**Root Causes & Solutions:**
#### A. Missing Metadata Updates
**Problem:** Component state changes but metadata isn't updated.
**Solution:** Use `updateMetadata` function:
```tsx
function DynamicButton({ label, disabled }) {
const { automationIdProps, updateMetadata } = useAutomationIdAndRegister({
id: 'dynamic-button',
type: 'button',
label,
disabled
});
// Update metadata when props change
useEffect(() => {
updateMetadata({ label, disabled });
}, [label, disabled, updateMetadata]);
return <button {...automationIdProps}>{label}</button>;
}
```
#### B. Module Instance Mismatch
**Problem:** Different UIStateManager instances in different processes.
**Solution:** Ensure automation server is used as source of truth:
- Use HTTP API endpoints instead of direct module access
- Verify WebSocket connections are active
- Check for multiple server instances
### Issue 4: Component Hierarchy Issues
**Symptoms:**
- Components appear as root-level instead of nested
- Parent-child relationships are incorrect
- Auto-generated IDs are wrong
**Solutions:**
#### A. Proper Context Usage
```tsx
// Correct: Parent sets context for children
function ParentContainer() {
const { automationIdProps } = useAutomationIdAndRegister({
id: 'parent-container',
type: 'container',
label: 'Parent'
});
return (
<ReflectionContainer {...automationIdProps}>
{/* Children inherit parent context automatically */}
<ChildComponent /> {/* Will be parent-container-child */}
</ReflectionContainer>
);
}
function ChildComponent() {
// No explicit parentId needed - inherited from context
const { automationIdProps } = useAutomationIdAndRegister({
type: 'button',
label: 'Child Button'
});
return <button {...automationIdProps}>Click</button>;
}
```
#### B. ID Naming Conventions
Follow consistent naming patterns:
- Screen/Page: `my-screen`
- Subcontainer: `${parentId}-section` (e.g., `my-screen-filters`)
- Component: `${parentId}-type` (e.g., `my-screen-filters-select`)
### Issue 5: Form Field Naming and Override ID Support
**Symptoms:**
- Form fields show generic auto-generated names like `formField-1`, `formField-2`
- Meaningful field names like `email`, `phone_no` are not appearing
- Components ignore `data-automation-id` attributes
**Root Causes & Solutions:**
#### A. Missing Override ID Support
**Problem:** Components auto-generate IDs instead of using provided `data-automation-id`.
**Solution:** Implement the override ID pattern in form components:
```tsx
// Enhanced useAutomationIdAndRegister with actions support
export function useAutomationIdAndRegister<T extends UIComponent>(
component: Omit<T, 'id' | 'actions'> & { id?: string },
actionsOrShouldRegister: ActionConfig | boolean = [],
overrideId?: string
): {
automationIdProps: { id: string; 'data-automation-id': string };
updateMetadata: (partial: Partial<T>) => void;
updateActions: (newActions: ActionConfig) => void;
}
// Where ActionConfig = ComponentAction[] | (() => ComponentAction[])
// In form components (Input, CustomSelect, TextArea):
const { automationIdProps } = useAutomationIdAndRegister<FormFieldComponent>({
type: 'formField',
fieldType: 'textField',
id,
label
}, true, dataAutomationId); // Pass override ID as third parameter
```
#### B. Implementing Override ID Pattern in Components
**Solution:** Update form components to support `data-automation-id`:
```tsx
// Before: No override support
export const Input = forwardRef<HTMLInputElement, InputProps & AutomationProps>(
({ label, id, ...props }, ref) => {
const { automationIdProps } = useAutomationIdAndRegister({
id,
type: 'formField',
fieldType: 'textField'
});
return <input {...automationIdProps} {...props} />;
}
);
// After: With override support
export const Input = forwardRef<HTMLInputElement, InputProps & AutomationProps>(
({ label, id, "data-automation-id": dataAutomationId, ...props }, ref) => {
const { automationIdProps } = useAutomationIdAndRegister({
id,
type: 'formField',
fieldType: 'textField'
}, true, dataAutomationId); // Pass override ID
return <input {...automationIdProps} {...props} />;
}
);
```
#### C. Usage Pattern for Meaningful Field Names
**Solution:** Use `data-automation-id` for meaningful form field names:
```tsx
// Form with meaningful field names
function MyForm() {
return (
<form>
<Input
data-automation-id="company-name-input"
label="Company Name"
value={formData.companyName}
onChange={(e) => setFormData({...formData, companyName: e.target.value})}
/>
<CustomSelect
data-automation-id="client_type_select"
label="Client Type"
options={clientTypeOptions}
value={formData.clientType}
onValueChange={(value) => setFormData({...formData, clientType: value})}
/>
<TextArea
data-automation-id="notes"
label="Notes"
value={formData.notes}
onChange={(e) => setFormData({...formData, notes: e.target.value})}
/>
</form>
);
}
```
### Issue 6: Dialog Type Registration Problems
**Symptoms:**
- Dialogs appear as `Type: container` instead of `Type: dialog`
- Dialog `open` property is missing from UI state
- Dialog actions (submit/cancel) are not available
**Root Causes & Solutions:**
#### A. Incorrect Dialog Wrapper Usage
**Problem:** Using `ReflectionContainer` wrapper makes dialogs appear as containers.
**Solution:** Use `withDataAutomationId` directly on dialog content:
```tsx
// Before: Shows as Type: container
function MyDialog({ open, onOpenChange }) {
const { automationIdProps } = useAutomationIdAndRegister({
id: 'my-dialog',
type: 'dialog',
title: 'My Dialog'
});
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<ReflectionContainer {...automationIdProps}>
{/* Dialog content */}
</ReflectionContainer>
</DialogContent>
</Dialog>
);
}
// After: Shows as Type: dialog
function MyDialog({ open, onOpenChange }) {
const { automationIdProps: updateDialog } = useAutomationIdAndRegister<DialogComponent>({
id: 'my-dialog',
type: 'dialog',
title: 'My Dialog',
open
});
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent
{...withDataAutomationId(updateDialog)}
className="..."
>
<ReflectionParentContext.Provider value={updateDialog.id}>
{/* Dialog content */}
</ReflectionParentContext.Provider>
</DialogContent>
</Dialog>
);
}
```
#### B. Missing Dialog State Updates
**Solution:** Update dialog metadata when state changes:
```tsx
// Update dialog metadata when open state changes
useEffect(() => {
if (updateMetadata) {
updateMetadata({ open });
}
}, [open, updateMetadata]);
```
### Issue 7: Double Registration Prevention
**Symptoms:**
- Duplicate components appearing in UI state
- Form fields registered both by parent and themselves
- Generic IDs mixed with meaningful names
**Root Causes & Solutions:**
#### A. Parent and Child Both Registering
**Problem:** Parent component and form components both call `useAutomationIdAndRegister`.
**Solution:** Remove duplicate registrations from parent component:
```tsx
// Before: Double registration
function QuickAddForm() {
// DON'T: Register fields at parent level
const { automationIdProps: emailProps } = useAutomationIdAndRegister({
id: 'email',
type: 'formField',
fieldType: 'textField',
parentId: 'my-form'
});
return (
<form>
<Input {...emailProps} /> {/* Input also registers itself */}
</form>
);
}
// After: Single registration with override ID
function QuickAddForm() {
return (
<form>
<Input data-automation-id="email" /> {/* Only Input registers */}
</form>
);
}
```
#### B. Conditional Registration Pattern
**Solution:** Use conditional registration to prevent duplicates:
```tsx
// In form components, check for data-automation-id
const shouldRegister = !dataAutomationId;
const { automationIdProps } = useAutomationIdAndRegister({
type: 'formField',
fieldType: 'textField',
id: shouldRegister ? id : undefined
}, shouldRegister, dataAutomationId);
```
### Issue 8: Hook Parameter Debugging
**Symptoms:**
- `useAutomationIdAndRegister` behaves unexpectedly
- Parameters not being passed correctly
- Function receiving default values instead of provided ones
**Debugging Steps:**
#### A. Verify Function Signature
**Check:** Ensure you're passing parameters in the correct order:
```tsx
// Correct parameter order
useAutomationIdAndRegister<T>(
component: Omit<T, 'id' | 'actions'> & { id?: string },
actionsOrShouldRegister: ActionConfig | boolean = [],
overrideId?: string
)
// Where ActionConfig = ComponentAction[] | (() => ComponentAction[])
// Common mistake: Missing actionsOrShouldRegister parameter
// WRONG:
const { automationIdProps } = useAutomationIdAndRegister({
type: 'formField',
fieldType: 'textField'
}, dataAutomationId); // Missing actionsOrShouldRegister parameter
// CORRECT:
const { automationIdProps } = useAutomationIdAndRegister({
type: 'formField',
fieldType: 'textField'
}, true, dataAutomationId); // All parameters provided
```
#### B. Add Debug Logging
**Solution:** Add logging to verify parameter values:
```tsx
const { automationIdProps } = useAutomationIdAndRegister({
type: 'formField',
fieldType: 'textField',
id
}, shouldRegister, dataAutomationId);
console.log('🔍 Hook params:', { shouldRegister, dataAutomationId, id });
console.log('🔍 Generated props:', automationIdProps);
```
### Issue 9: Raw HTML Elements vs UI Components
**Symptoms:**
- Some form fields not appearing in UI state
- Mixed registration patterns in the same component
- Inconsistent automation ID application
**Root Causes & Solutions:**
#### A. Raw HTML Elements Don't Auto-Register
**Problem:** Using raw `<input>`, `<select>`, `<textarea>` elements.
**Solution:** Replace with UI reflection components:
```tsx
// Before: Raw HTML elements (invisible to UI reflection)
function UserPicker() {
return (
<div className="dropdown">
<div className="search">
<input // This won't appear in UI state
type="text"
placeholder="Search..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
</div>
);
}
// After: Using UI reflection components
function UserPicker({ "data-automation-id": dataAutomationId }) {
return (
<div className="dropdown">
<div className="search">
<Input // This will appear in UI state
data-automation-id={dataAutomationId ? `${dataAutomationId}-search` : undefined}
type="text"
placeholder="Search..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
</div>
);
}
```
#### B. Component Registration Requirements
**Guidelines:**
- Always use UI reflection components (`Input`, `CustomSelect`, `TextArea`, etc.) instead of raw HTML
- Ensure all interactive elements have automation IDs
- Prefer `data-automation-id` for meaningful names over auto-generated IDs
## Debugging Techniques
### 1. Enable Verbose Logging
Add detailed logging to your components:
```tsx
function DebugComponent() {
const { automationIdProps, updateMetadata } = useAutomationIdAndRegister({
id: 'debug-component',
type: 'container',
label: 'Debug Component'
});
console.log('🔍 Debug Component Props:', automationIdProps);
useEffect(() => {
console.log('🔍 Component mounted with ID:', automationIdProps.id);
return () => {
console.log('🔍 Component unmounting:', automationIdProps.id);
};
}, []);
return <ReflectionContainer {...automationIdProps}>...</ReflectionContainer>;
}
```
### 2. UI State Monitoring
Set up real-time monitoring of UI state changes:
```javascript
// In browser console
const socket = io('http://localhost:4000');
socket.on('ui_state_update', (state) => {
console.log('🔄 UI State Update:', state);
});
```
### 3. Component Registration Tracking
Track component registrations in real-time:
```tsx
// Add to UIStateContext for debugging
const registerComponent = useCallback((component: UIComponent) => {
console.log('📝 Registering component:', component.id, component.type);
// ... existing registration logic
}, []);
```
### 4. Automation Server Health Check
Create a health check script:
```javascript
// health-check.js
const fetch = require('node-fetch');
async function checkHealth() {
try {
const response = await fetch('http://localhost:4000/api/ui-state');
const data = await response.json();
console.log('✅ Automation server healthy');
console.log('📊 Component count:', data.componentCount);
} catch (error) {
console.error('❌ Automation server unhealthy:', error.message);
}
}
checkHealth();
```
## Performance Considerations
### 1. Component Registration Limits
- Only register components that need automation visibility
- Avoid registering every minor UI element
- Use containers to group related elements
### 2. Update Frequency
- Batch state updates when possible
- Debounce rapid state changes
- Use `useMemo` for expensive state calculations
### 3. Memory Management
- Ensure components unregister on unmount
- Clear component hierarchies properly
- Monitor for memory leaks in long-running pages
## Validation Checklist
When troubleshooting UI automation issues, verify:
### Basic Setup
- [ ] Automation server is running on port 4000
- [ ] WebSocket connection is established
- [ ] Components use `useAutomationIdAndRegister` hook
- [ ] Import paths are correct
- [ ] ReflectionContainer wraps main content
- [ ] Browser console shows no UI reflection errors
### Component Registration
- [ ] Component metadata updates with state changes
- [ ] Parent-child relationships are properly established
- [ ] No duplicate component IDs exist
- [ ] Form components use UI reflection components instead of raw HTML
### Form Field Naming
- [ ] Form fields have meaningful names (not `formField-1`, `formField-2`)
- [ ] `data-automation-id` attributes are properly passed to form components
- [ ] Override ID pattern is implemented in form components
- [ ] No duplicate registrations from parent and child components
### Dialog Registration
- [ ] Dialogs appear as `Type: dialog` (not `Type: container`)
- [ ] Dialog `open` property is available in UI state
- [ ] Dialog uses `withDataAutomationId` instead of `ReflectionContainer`
- [ ] Dialog metadata updates when state changes
### Hook Parameter Validation
- [ ] `useAutomationIdAndRegister` receives all parameters in correct order
- [ ] `shouldRegister` parameter is provided when using override IDs
- [ ] `overrideId` parameter is passed as third argument
- [ ] Hook parameters are logged for debugging when issues occur
## Getting Help
If issues persist:
1. **Collect Debug Information:**
- UI state dump output (`window.__UI_STATE__` in browser console)
- Browser console logs
- Automation server logs
- Component registration errors
2. **Create Minimal Reproduction:**
- Isolate the problematic component
- Create a simple test case
- Document expected vs. actual behavior
3. **Check Documentation:**
- [UI Reflection System](ui_reflection_system.md)
- [UI Automation IDs](ui_automation_ids.md)
- [Development Guide](../getting-started/development_guide.md)
4. **Report Issues:**
Include debug information and reproduction steps when reporting problems.
## Related Documentation
- [UI Reflection System](ui_reflection_system.md) - Core system documentation
- [UI Automation IDs](ui_automation_ids.md) - ID conventions and standards
- [Development Guide](../getting-started/development_guide.md) - General development practices