Repository: tjtanjin/react-chatbotify (ts)

Issue:
[Task] Add unit test cases for useFirstInteractionInternal hook
**Task Description:**
Currently, we run unit tests using jest which can be found [**here**](https://github.com/tjtanjin/react-chatbotify/tree/main/__tests__). 

However, the test cases are not yet comprehensive enough and work can be done to cover more grounds. Specifically for this issue, we hope to add test cases for the `useFirstInteractionInternal` hook.

**Deliverable(s):**
New unit test cases that cover the testing of the `useFirstInteractionInternal` hook.

**Additional Context:**
- Hooks API can be found [**here**](https://react-chatbotify.com/docs/api/hooks)
- Existing unit test for hooks can be found [**here**](https://github.com/tjtanjin/react-chatbotify/tree/main/__tests__/hooks) which may serve as a good source for reference.


Pull request:
Add test for hook useFirstInteractionInternal
#### Description

Added unit tests for the hook useFirstInteractionInternal

Closes #128 

Patch:
diff --git a/package-lock.json b/package-lock.json
index 285fef9..ad4b2be 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -37,8 +37,8 @@
         "vite-plugin-svgr": "^3.2.0"
       },
       "peerDependencies": {
-        "react": ">=16.14.0 <20.0.0",
-        "react-dom": ">=16.14.0 <20.0.0"
+        "react": ">=16.14.0 <20.0.0 || ^19.0.0-0",
+        "react-dom": ">=16.14.0 <20.0.0 || ^19.0.0-0"
       }
     },
     "node_modules/@aashutoshrathi/word-wrap": {
diff --git a/src/components/ChatBot.tsx b/src/components/ChatBot.tsx
index 73df0fe..dbdffae 100644
--- a/src/components/ChatBot.tsx
+++ b/src/components/ChatBot.tsx
@@ -68,7 +68,7 @@ const ChatBot = ({
 	const renderChatBot = () => (
 		<>
 			<ChatBotLoader styleRootRef={styleRootRef} id={id} flow={flow} settings={settings}
-				styles={styles} themes={themes} setConfigLoaded={setConfigLoaded}
+				styles={styles} themes={themes} plugins={plugins} setConfigLoaded={setConfigLoaded}
 			/>
 			{configLoaded && <ChatBotContainer plugins={plugins} />}
 		</>
diff --git a/src/components/ChatBotLoader.tsx b/src/components/ChatBotLoader.tsx
index ddcb2a2..ca1807e 100644
--- a/src/components/ChatBotLoader.tsx
+++ b/src/components/ChatBotLoader.tsx
@@ -1,11 +1,14 @@
 import { Dispatch, MutableRefObject, SetStateAction, useEffect } from "react";
 
+import { emitRcbEvent } from "../services/RcbEventService";
 import { useChatBotContext } from "../context/ChatBotContext";
 import { useBotRefsContext } from "../context/BotRefsContext";
 import { Settings } from "../types/Settings";
 import { Styles } from "../types/Styles";
 import { Flow } from "../types/Flow";
 import { Theme } from "../types/Theme";
+import { Plugin } from "../types/Plugin";
+import { RcbEvent } from "../constants/RcbEvent";
 
 /**
  * Helps to load the chatbot configurations.
@@ -24,6 +27,7 @@ const ChatBotLoader = ({
 	settings,
 	styles,
 	themes,
+	plugins,
 	setConfigLoaded,
 	styleRootRef,
 }: {
@@ -32,6 +36,7 @@ const ChatBotLoader = ({
 	settings: Settings;
 	styles: Styles;
 	themes: Theme | Array<Theme>;
+	plugins: Array<Plugin>;
 	setConfigLoaded: Dispatch<SetStateAction<boolean>>;
 	styleRootRef: MutableRefObject<HTMLStyleElement | null>;
 }) => {
@@ -51,10 +56,39 @@ const ChatBotLoader = ({
 	 * Loads the config file to the provider.
 	 */
 	const runLoadConfig = async () => {
+		// handles pre load chatbot event
+		if (settings.event?.rcbPreLoadChatBot) {
+			const event = emitRcbEvent(RcbEvent.PRE_LOAD_CHATBOT, {botId: id, currPath: null, prevPath: null}, 
+				{
+					flow,
+					settings,
+					styles,
+					themes,
+					plugins
+				}
+			);
+			if (event.defaultPrevented) {
+				return;
+			}
+		}
+
 		if (chatBotContext?.loadConfig) {
 			await chatBotContext.loadConfig(id, flow, settings, styles, themes, styleRootRef);
 			setConfigLoaded(true);
 		}
+
+		// handles post load chatbot event
+		if (settings.event?.rcbPostLoadChatBot) {
+			emitRcbEvent(RcbEvent.POST_LOAD_CHATBOT, {botId: id, currPath: null, prevPath: null}, 
+				{
+					flow,
+					settings,
+					styles,
+					themes,
+					plugins
+				}
+			);
+		}
 	}
 
 	useEffect(() => {
diff --git a/src/constants/RcbEvent.ts b/src/constants/RcbEvent.ts
index 6817fd2..60a47ae 100644
--- a/src/constants/RcbEvent.ts
+++ b/src/constants/RcbEvent.ts
@@ -35,7 +35,11 @@ const RcbEvent = {
 	USER_UPLOAD_FILE: "rcb-user-upload-file",
 
 	// text area value change
-	TEXT_AREA_CHANGE_VALUE: "rcb-text-area-change-value"
+	TEXT_AREA_CHANGE_VALUE: "rcb-text-area-change-value",
+
+	// chatbot loading
+	PRE_LOAD_CHATBOT: "rcb-pre-load-chatbot",
+	POST_LOAD_CHATBOT: "rcb-post-load-chatbot",
 }
 
 export { RcbEvent };
diff --git a/src/constants/internal/DefaultSettings.tsx b/src/constants/internal/DefaultSettings.tsx
index d8b22ae..d61a7d0 100644
--- a/src/constants/internal/DefaultSettings.tsx
+++ b/src/constants/internal/DefaultSettings.tsx
@@ -168,6 +168,8 @@ export const DefaultSettings: Settings = {
 	event: {
 		rcbPreInjectMessage: false,
 		rcbPostInjectMessage: false,
+		rcbPreLoadChatBot: false,
+		rcbPostLoadChatBot: false,
 		rcbStartStreamMessage: false,
 		rcbChunkStreamMessage: false,
 		rcbStopStreamMessage: false,
diff --git a/src/context/BotStatesContext.tsx b/src/context/BotStatesContext.tsx
index 0c67eb2..d875b9e 100644
--- a/src/context/BotStatesContext.tsx
+++ b/src/context/BotStatesContext.tsx
@@ -5,7 +5,7 @@ import { Settings } from "../types/Settings";
 /**
  * Creates the useBotStatesContext() hook to manage common states.
  */
-type BotStatesContextType = {
+export type BotStatesContextType = {
 	isBotTyping: boolean;
 	setIsBotTyping: Dispatch<SetStateAction<boolean>>;
 	
diff --git a/src/hooks/internal/useBotEffectsInternal.tsx b/src/hooks/internal/useBotEffectsInternal.tsx
index f6229e2..bbbae2c 100644
--- a/src/hooks/internal/useBotEffectsInternal.tsx
+++ b/src/hooks/internal/useBotEffectsInternal.tsx
@@ -120,11 +120,12 @@ export const useBotEffectInternal = () => {
 		}, 1)
 	}, [])
 
-	// renders chat history button if enabled
+	// renders chat history button if enabled and triggers update if chat history configurations change
 	useEffect(() => {
 		if (settings.chatHistory?.disabled) {
 			localStorage.removeItem(settings.chatHistory?.storageKey as string);
 		} else {
+			setHistoryStorageValues(settings);
 			const chatHistory = localStorage.getItem(settings.chatHistory?.storageKey as string);
 			if (chatHistory != null) {
 				// note: must always render this button even if autoload (chat history logic relies on system message)
@@ -135,11 +136,6 @@ export const useBotEffectInternal = () => {
 				}
 			}
 		}
-	}, []);
-
-	// triggers update to chat history options
-	useEffect(() => {
-		setHistoryStorageValues(settings);
 	}, [settings.chatHistory?.storageKey, settings.chatHistory?.maxEntries, settings.chatHistory?.disabled]);
 
 	// handles virtualkeyboard api (if supported on browser) on mobile devices
diff --git a/src/hooks/internal/useBotIdInternal.tsx b/src/hooks/internal/useBotIdInternal.tsx
new file mode 100644
index 0000000..bdfcbaf
--- /dev/null
+++ b/src/hooks/internal/useBotIdInternal.tsx
@@ -0,0 +1,20 @@
+import { useBotRefsContext } from "../../context/BotRefsContext";
+
+/**
+ * Internal custom hook for managing bot id.
+ */
+export const useBotIdInternal = () => {
+	// handles bot refs
+	const { botIdRef } = useBotRefsContext();
+	
+	/**
+	 * Retrieves the id for the chatbot.
+	 */
+	const getBotId = () => {
+		return botIdRef.current;
+	}
+	
+	return {
+		getBotId
+	};
+}
\ No newline at end of file
diff --git a/src/hooks/internal/useChatHistoryInternal.ts b/src/hooks/internal/useChatHistoryInternal.ts
index fd9b972..7101eff 100644
--- a/src/hooks/internal/useChatHistoryInternal.ts
+++ b/src/hooks/internal/useChatHistoryInternal.ts
@@ -22,7 +22,12 @@ export const useChatHistoryInternal = () => {
 	const { setMessages } = useMessagesContext();
 
 	// handles bot states
-	const { isLoadingChatHistory, setIsLoadingChatHistory, setTextAreaDisabled } = useBotStatesContext();
+	const {
+		isLoadingChatHistory,
+		setIsLoadingChatHistory,
+		textAreaDisabled,
+		setTextAreaDisabled
+	} = useBotStatesContext();
 
 	// handles rcb events
 	const { callRcbEvent } = useRcbEventInternal();
@@ -46,8 +51,9 @@ export const useChatHistoryInternal = () => {
 			}
 		}
 		setIsLoadingChatHistory(true);
+		const prevTextAreaDisabled = textAreaDisabled;
 		setTextAreaDisabled(true);
-		loadChatHistory(settings, styles, chatHistory, setMessages, setTextAreaDisabled);
+		loadChatHistory(settings, styles, chatHistory, setMessages, prevTextAreaDisabled, setTextAreaDisabled);
 	}, [settings, styles, setMessages, setTextAreaDisabled]);
 
 	return { isLoadingChatHistory, setIsLoadingChatHistory, showChatHistory };
diff --git a/src/hooks/internal/usePathsInternal.ts b/src/hooks/internal/usePathsInternal.ts
index 90198ac..c2291a0 100644
--- a/src/hooks/internal/usePathsInternal.ts
+++ b/src/hooks/internal/usePathsInternal.ts
@@ -55,7 +55,7 @@ export const usePathsInternal = () => {
 		if (settings.event?.rcbChangePath) {
 			const currPath = getCurrPath();
 			const prevPath = getPrevPath();
-			const details = {id: botIdRef.current, currPath, prevPath}
+			const details = {botId: botIdRef.current, currPath, prevPath}
 			event = emitRcbEvent(RcbEvent.CHANGE_PATH, details, {currPath, prevPath, nextPath: pathToGo});
 			if (event.defaultPrevented) {
 				return false;
diff --git a/src/hooks/internal/useRcbEventInternal.ts b/src/hooks/internal/useRcbEventInternal.ts
index a187fe0..cbee16a 100644
--- a/src/hooks/internal/useRcbEventInternal.ts
+++ b/src/hooks/internal/useRcbEventInternal.ts
@@ -22,7 +22,7 @@ export const useRcbEventInternal = () => {
 	 * @param data additional data to include with the event
 	 */
 	const callRcbEvent = useCallback((eventName: typeof RcbEvent[keyof typeof RcbEvent], data: object) => {
-		const details = {id: botIdRef.current, currPath: getCurrPath(), prevPath: getPrevPath()}
+		const details = {botId: botIdRef.current, currPath: getCurrPath(), prevPath: getPrevPath()}
 		return emitRcbEvent(eventName, details, data);
 	}, [paths])
 
diff --git a/src/hooks/useBotId.tsx b/src/hooks/useBotId.tsx
new file mode 100644
index 0000000..7fb4294
--- /dev/null
+++ b/src/hooks/useBotId.tsx
@@ -0,0 +1,13 @@
+import { useBotIdInternal } from "./internal/useBotIdInternal";
+
+/**
+ * External custom hook for managing bot id.
+ */
+export const useBotId = () => {
+	// handles bot id
+	const { getBotId } = useBotIdInternal();
+	
+	return {
+		getBotId
+	};
+}
\ No newline at end of file
diff --git a/src/index.tsx b/src/index.tsx
index 066010a..823829a 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -18,6 +18,8 @@ import { RcbLoadChatHistoryEvent } from "./types/events/RcbLoadChatHistoryEvent"
 import { RcbToggleChatWindowEvent } from "./types/events/RcbToggleChatWindowEvent";
 import { RcbToggleAudioEvent } from "./types/events/RcbToggleAudioEvent";
 import { RcbPreInjectMessageEvent } from "./types/events/RcbPreInjectMessageEvent";
+import { RcbPreLoadChatBotEvent } from "./types/events/RcbPreLoadChatBotEvent";
+import { RcbPostLoadChatBotEvent } from "./types/events/RcbPostLoadChatBotEvent";
 import { RcbToggleNotificationsEvent } from "./types/events/RcbToggleNotificationsEvent";
 import { RcbToggleVoiceEvent } from "./types/events/RcbToggleVoiceEvent";
 import { RcbStartStreamMessageEvent } from "./types/events/RcbStartStreamMessageEvent";
@@ -69,6 +71,8 @@ export type {
 export type {
 	RcbPreInjectMessageEvent,
 	RcbPostInjectMessageEvent,
+	RcbPreLoadChatBotEvent,
+	RcbPostLoadChatBotEvent,
 	RcbStartStreamMessageEvent,
 	RcbChunkStreamMessageEvent,
 	RcbStopStreamMessageEvent,
diff --git a/src/services/ChatHistoryService.tsx b/src/services/ChatHistoryService.tsx
index 6edd7ab..bba2112 100644
--- a/src/services/ChatHistoryService.tsx
+++ b/src/services/ChatHistoryService.tsx
@@ -120,13 +120,16 @@ const parseMessageToString = (message: Message) => {
 /**
  * Loads chat history into the chat window for user view.
  * 
- * @param settings options provided to the bot
+ * @param settings settings provided to the bot
+ * @param styles styles provided to the bot
  * @param chatHistory chat history to show
  * @param setMessages setter for updating messages
+ * @param prevTextAreaDisabled boolean indicating if text area was previously disabled
  * @param setTextAreaDisabled setter for enabling/disabling user text area
  */
 const loadChatHistory = (settings: Settings, styles: Styles, chatHistory: Message[],
-	setMessages: Dispatch<SetStateAction<Message[]>>, setTextAreaDisabled: Dispatch<SetStateAction<boolean>>) => {
+	setMessages: Dispatch<SetStateAction<Message[]>>, prevTextAreaDisabled: boolean,
+	setTextAreaDisabled: Dispatch<SetStateAction<boolean>>) => {
 
 	historyLoaded = true;
 	if (chatHistory != null) {
@@ -157,7 +160,7 @@ const loadChatHistory = (settings: Settings, styles: Styles, chatHistory: Messag
 					}
 					return [...parsedMessages, lineBreakMessage, ...prevMessages];
 				});
-				setTextAreaDisabled(settings.chatInput?.disabled ?? false);
+				setTextAreaDisabled(prevTextAreaDisabled ?? settings.chatInput?.disabled ?? false);
 			}, 500)
 		} catch {
 			// remove chat history on error (to address corrupted storage values)
diff --git a/src/services/RcbEventService.tsx b/src/services/RcbEventService.tsx
index 4d13956..2e94ca1 100644
--- a/src/services/RcbEventService.tsx
+++ b/src/services/RcbEventService.tsx
@@ -10,6 +10,8 @@ const cancellableMap = {
 	[RcbEvent.TOGGLE_CHAT_WINDOW]: true,
 	[RcbEvent.PRE_INJECT_MESSAGE]: true,
 	[RcbEvent.POST_INJECT_MESSAGE]: false,
+	[RcbEvent.PRE_LOAD_CHATBOT]: true,
+	[RcbEvent.POST_LOAD_CHATBOT]: false,
 	[RcbEvent.START_STREAM_MESSAGE]: true,
 	[RcbEvent.CHUNK_STREAM_MESSAGE]: true,
 	[RcbEvent.STOP_STREAM_MESSAGE]: true,
diff --git a/src/types/Settings.ts b/src/types/Settings.ts
index 1f1a886..3b05b89 100644
--- a/src/types/Settings.ts
+++ b/src/types/Settings.ts
@@ -134,6 +134,8 @@ export type Settings = {
 	event?: {
 		rcbPreInjectMessage?: boolean;
 		rcbPostInjectMessage?: boolean;
+		rcbPreLoadChatBot?: boolean;
+		rcbPostLoadChatBot?: boolean;
 		rcbStartStreamMessage?: boolean;
 		rcbChunkStreamMessage?: boolean;
 		rcbStopStreamMessage?: boolean;
diff --git a/src/types/events/RcbPostLoadChatBotEvent.ts b/src/types/events/RcbPostLoadChatBotEvent.ts
new file mode 100644
index 0000000..767d326
--- /dev/null
+++ b/src/types/events/RcbPostLoadChatBotEvent.ts
@@ -0,0 +1,17 @@
+import { RcbBaseEvent } from "../internal/events/RcbBaseEvent";
+import { Flow } from "../Flow";
+import { Plugin } from "../Plugin";
+import { Settings } from "../Settings";
+import { Styles } from "../Styles";
+import { Theme } from "../Theme";
+
+/**
+ * Defines the data available for post-load chatbot event.
+ */
+export type RcbPostLoadChatBotEvent = RcbBaseEvent<{
+    flow: Flow;
+    settings: Settings;
+    styles: Styles;
+    themes: Theme | Array<Theme>;
+	plugins: Array<Plugin>;
+}>;
\ No newline at end of file
diff --git a/src/types/events/RcbPreLoadChatBotEvent.ts b/src/types/events/RcbPreLoadChatBotEvent.ts
new file mode 100644
index 0000000..57f2f11
--- /dev/null
+++ b/src/types/events/RcbPreLoadChatBotEvent.ts
@@ -0,0 +1,17 @@
+import { RcbBaseEvent } from "../internal/events/RcbBaseEvent";
+import { Flow } from "../Flow";
+import { Plugin } from "../Plugin";
+import { Settings } from "../Settings";
+import { Styles } from "../Styles";
+import { Theme } from "../Theme";
+
+/**
+ * Defines the data available for pre-load chatbot event.
+ */
+export type RcbPreLoadChatBotEvent = RcbBaseEvent<{
+    flow: Flow;
+    settings: Settings;
+    styles: Styles;
+    themes: Theme | Array<Theme>;
+	plugins: Array<Plugin>;
+}>;
\ No newline at end of file
diff --git a/src/types/internal/events/EventDetail.ts b/src/types/internal/events/EventDetail.ts
index 5f40cd7..9a1ebf0 100644
--- a/src/types/internal/events/EventDetail.ts
+++ b/src/types/internal/events/EventDetail.ts
@@ -1,5 +1,5 @@
 export type EventDetail = {
-    id: string | null;
+    botId: string | null;
     currPath: string | null;
 	prevPath: string | null;
 }
\ No newline at end of file
diff --git a/types/global.d.ts b/types/global.d.ts
index dab0726..05dfdd5 100644
--- a/types/global.d.ts
+++ b/types/global.d.ts
@@ -1,6 +1,8 @@
 import { EventHandler } from "react";
 import { RcbPreInjectMessageEvent } from "../src/types/events/RcbPreInjectMessageEvent";
 import { RcbPostInjectMessageEvent } from "../src/types/events/RcbPostInjectMessageEvent";
+import { RcbPreLoadChatBotEvent } from "../src/types/events/RcbPreLoadChatBotEvent";
+import { RcbPostLoadChatBotEvent } from "../src/types/events/RcbPostLoadChatBotEvent";
 import { RcbLoadChatHistoryEvent } from "../src/types/events/RcbLoadChatHistoryEvent";
 import { RcbToggleChatWindowEvent } from "../src/types/events/RcbToggleChatWindowEvent";
 import { RcbToggleAudioEvent } from "../src/types/events/RcbToggleAudioEvent";
@@ -76,5 +78,9 @@ declare global {
 
 		// textarea change value
 		"rcb-text-area-change-value": RcbTextAreaChangeValueEvent;
+
+		// chatbot loading
+		"rcb-pre-load-chatbot": RcbPreLoadChatBotEvent;
+		"rcb-post-load-chatbot": RcbPostLoadChatBotEvent;
 	}
 }


Test patch:
diff --git a/__tests__/__mocks__/constants.ts b/__tests__/__mocks__/constants.ts
index 2c3a69a..7f32e6b 100644
--- a/__tests__/__mocks__/constants.ts
+++ b/__tests__/__mocks__/constants.ts
@@ -6,6 +6,8 @@ export const MockDefaultSettings = {
 	event: {
 		rcbPreInjectMessage: true,
 		rcbPostInjectMessage: true,
+		rcbPreLoadChatBot: true,
+		rcbPostLoadChatBot: true,
 		rcbStartStreamMessage: true,
 		rcbChunkStreamMessage: true,
 		rcbStopStreamMessage: true,
diff --git a/__tests__/hooks/useChatHistoryInternal.test.ts b/__tests__/hooks/useChatHistoryInternal.test.ts
index d4a936d..730eedf 100644
--- a/__tests__/hooks/useChatHistoryInternal.test.ts
+++ b/__tests__/hooks/useChatHistoryInternal.test.ts
@@ -55,7 +55,7 @@ describe("useChatHistoryInternal Hook", () => {
 
 		// mocks loadChatHistory to resolve successfully
 		mockLoadChatHistory.mockImplementation(
-			(settings, styles, chatHistory, setMessages, setTextAreaDisabled) => {
+			(settings, styles, chatHistory, setMessages, prevTextAreaDisabled, setTextAreaDisabled) => {
 				setMessages(chatHistory);
 				setTextAreaDisabled(false);
 				return Promise.resolve();
@@ -90,6 +90,7 @@ describe("useChatHistoryInternal Hook", () => {
 			expect.any(Object),
 			initialChatHistory,
 			expect.any(Function),
+			expect.any(Boolean),
 			expect.any(Function)
 		);
 
diff --git a/__tests__/hooks/useFirstInteractionInternal.test.ts b/__tests__/hooks/useFirstInteractionInternal.test.ts
new file mode 100644
index 0000000..cd5daed
--- /dev/null
+++ b/__tests__/hooks/useFirstInteractionInternal.test.ts
@@ -0,0 +1,72 @@
+import { renderHook, act } from '@testing-library/react';
+import { useFirstInteractionInternal } from '../../src/hooks/internal/useFirstInteractionInternal';
+
+jest.mock('../../src/context/BotStatesContext', () => ({
+  useBotStatesContext: jest.fn(() => ({
+    hasInteractedPage: false,
+    setHasInteractedPage: jest.fn(),
+    hasFlowStarted: false,
+    setHasFlowStarted: jest.fn(),
+  })),
+}));
+
+jest.mock('../../src/context/SettingsContext', () => ({
+  useSettingsContext: jest.fn(() => ({
+    settings: {
+      general: {
+        flowStartTrigger: 'ON_PAGE_INTERACT',
+      },
+    },
+  })),
+}));
+class MockSpeechSynthesisUtterance {
+    text = '';
+    onend: () => void = () => {};
+  
+    constructor() {
+      setTimeout(() => {
+        this.onend();
+      }, 0);
+    }
+  }
+  
+  const MockSpeechSynthesis = {
+    speak: jest.fn().mockImplementation((utterance: MockSpeechSynthesisUtterance) => {
+      utterance.onend();
+    }),
+  };
+  
+  global.SpeechSynthesisUtterance = MockSpeechSynthesisUtterance as any;
+  global.speechSynthesis = MockSpeechSynthesis as any;
+
+describe('useFirstInteractionInternal', () => {
+  let setHasInteractedPage: jest.Mock;
+  let setHasFlowStarted: jest.Mock;
+
+  beforeEach(() => {
+    setHasInteractedPage = jest.fn();
+    setHasFlowStarted = jest.fn();
+
+    (require('../../src/context/BotStatesContext').useBotStatesContext as jest.Mock).mockReturnValue({
+      hasInteractedPage: false,
+      setHasInteractedPage,
+      hasFlowStarted: false,
+      setHasFlowStarted,
+    });
+  });
+
+  afterEach(() => {
+    jest.clearAllMocks();
+  });
+
+  it('should call setHasInteractedPage and setHasFlowStarted on interaction', () => {
+    const { result } = renderHook(() => useFirstInteractionInternal());
+
+    act(() => {
+      result.current.handleFirstInteraction();
+    });
+
+    expect(setHasInteractedPage).toHaveBeenCalledWith(true);
+    expect(setHasFlowStarted).toHaveBeenCalledWith(true);
+  });
+});