@syngrisi/syngrisi
Version:
Syngrisi - Visual Testing Tool
201 lines (178 loc) • 5.04 kB
text/typescript
/**
* useRCA Hook
*
* Manages RCA (Root Cause Analysis) state and data fetching.
*/
import { useState, useCallback, useEffect } from 'react';
import { RCAService } from '@shared/services';
import { diffDOMTrees } from '@shared/utils/domDiff';
import {
DOMNode,
DOMChange,
DOMDiffResult,
RCAState,
} from '@shared/interfaces/IRCA';
export interface UseRCAOptions {
checkId: string | null;
baselineId: string | null;
apikey?: string;
shareToken?: string;
}
export interface UseRCAReturn {
state: RCAState;
enable: () => void;
disable: () => void;
toggle: () => void;
selectChange: (change: DOMChange | null) => void;
selectElement: (element: DOMNode | null) => void;
setHoveredElement: (element: DOMNode | null) => void;
toggleWireframe: () => void;
}
const initialState: RCAState = {
isEnabled: false,
isPanelOpen: false,
isLoading: false,
error: null,
baselineDom: null,
actualDom: null,
diffResult: null,
selectedElement: null,
selectedChange: null,
hoveredElement: null,
isWireframeEnabled: false,
};
export function useRCA(options: UseRCAOptions): UseRCAReturn {
const [state, setState] = useState<RCAState>(initialState);
const { checkId, baselineId, apikey, shareToken } = options;
/**
* Fetch DOM snapshots and perform diff analysis
*/
const fetchAndAnalyze = useCallback(async () => {
if (!checkId) {
setState((prev) => ({
...prev,
error: 'No check ID provided',
isLoading: false,
}));
return;
}
setState((prev) => ({ ...prev, isLoading: true, error: null }));
try {
const { actual, baseline } = await RCAService.getDomSnapshots({
checkId,
baselineId: baselineId || undefined,
apikey,
shareToken,
});
if (!actual) {
setState((prev) => ({
...prev,
isLoading: false,
error: 'No DOM snapshot available for this check. DOM capture may not have been enabled during the test run.',
}));
return;
}
// Perform diff analysis
const diffResult = diffDOMTrees(baseline, actual);
setState((prev) => ({
...prev,
isLoading: false,
actualDom: actual,
baselineDom: baseline,
diffResult,
error: null,
}));
} catch (error) {
setState((prev) => ({
...prev,
isLoading: false,
error: error instanceof Error ? error.message : 'Failed to fetch DOM snapshots',
}));
}
}, [checkId, baselineId, apikey, shareToken]);
/**
* Enable RCA mode
*/
const enable = useCallback(() => {
setState((prev) => ({
...prev,
isEnabled: true,
isPanelOpen: true,
}));
fetchAndAnalyze();
}, [fetchAndAnalyze]);
/**
* Disable RCA mode
*/
const disable = useCallback(() => {
setState({
...initialState,
isEnabled: false,
isPanelOpen: false,
});
}, []);
/**
* Toggle RCA mode
*/
const toggle = useCallback(() => {
if (state.isEnabled) {
disable();
} else {
enable();
}
}, [state.isEnabled, enable, disable]);
/**
* Select a change
*/
const selectChange = useCallback((change: DOMChange | null) => {
setState((prev) => ({
...prev,
selectedChange: change,
selectedElement: change?.actualNode || change?.baselineNode || null,
}));
}, []);
/**
* Select an element
*/
const selectElement = useCallback((element: DOMNode | null) => {
setState((prev) => ({
...prev,
selectedElement: element,
}));
}, []);
/**
* Set hovered element
*/
const setHoveredElement = useCallback((element: DOMNode | null) => {
setState((prev) => ({
...prev,
hoveredElement: element,
}));
}, []);
/**
* Toggle wireframe mode
*/
const toggleWireframe = useCallback(() => {
setState((prev) => ({
...prev,
isWireframeEnabled: !prev.isWireframeEnabled,
}));
}, []);
// Re-fetch when check changes and RCA is enabled
useEffect(() => {
if (state.isEnabled && checkId) {
fetchAndAnalyze();
}
}, [checkId, baselineId, state.isEnabled, fetchAndAnalyze]);
return {
state,
enable,
disable,
toggle,
selectChange,
selectElement,
setHoveredElement,
toggleWireframe,
};
}
export default useRCA;