@guidekit/react 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -0
- package/dist/chunk-4NUEGCBT.js +8 -0
- package/dist/chunk-4NUEGCBT.js.map +1 -0
- package/dist/chunk-EQGJ2LTR.cjs +10 -0
- package/dist/chunk-EQGJ2LTR.cjs.map +1 -0
- package/dist/devtools.cjs +576 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.cts +10 -0
- package/dist/devtools.d.ts +10 -0
- package/dist/devtools.js +574 -0
- package/dist/devtools.js.map +1 -0
- package/dist/index.cjs +1233 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +68 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.js +1226 -0
- package/dist/index.js.map +1 -0
- package/dist/testing.cjs +196 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +133 -0
- package/dist/testing.d.ts +133 -0
- package/dist/testing.js +185 -0
- package/dist/testing.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { AgentState, GuideKitErrorType, GuideKitStore } from '@guidekit/core';
|
|
4
|
+
export { AgentState, GuideKitErrorType, GuideKitStore } from '@guidekit/core';
|
|
5
|
+
|
|
6
|
+
/** @internal */
|
|
7
|
+
declare class TestStore {
|
|
8
|
+
private listeners;
|
|
9
|
+
private _state;
|
|
10
|
+
constructor(initial?: Partial<GuideKitStore['status']>);
|
|
11
|
+
subscribe: (listener: () => void) => (() => void);
|
|
12
|
+
getSnapshot: () => GuideKitStore;
|
|
13
|
+
setState(updater: (prev: GuideKitStore) => GuideKitStore): void;
|
|
14
|
+
/** Replace the state entirely. */
|
|
15
|
+
setStateDirectly(next: GuideKitStore): void;
|
|
16
|
+
private emitChange;
|
|
17
|
+
}
|
|
18
|
+
/** Initial state for the mock provider. */
|
|
19
|
+
interface MockInitialState {
|
|
20
|
+
isReady?: boolean;
|
|
21
|
+
agentState?: AgentState;
|
|
22
|
+
error?: GuideKitErrorType | null;
|
|
23
|
+
}
|
|
24
|
+
/** Mock action implementations to inject into the provider. */
|
|
25
|
+
interface MockActions {
|
|
26
|
+
sendText?: (text: string) => Promise<string>;
|
|
27
|
+
highlight?: (params: {
|
|
28
|
+
sectionId?: string;
|
|
29
|
+
selector?: string;
|
|
30
|
+
tooltip?: string;
|
|
31
|
+
position?: 'top' | 'bottom' | 'left' | 'right' | 'auto';
|
|
32
|
+
}) => void;
|
|
33
|
+
dismissHighlight?: () => void;
|
|
34
|
+
scrollToSection?: (sectionId: string, offset?: number) => void;
|
|
35
|
+
startTour?: (sectionIds: string[], mode?: 'auto' | 'manual') => void;
|
|
36
|
+
navigate?: (href: string) => Promise<boolean>;
|
|
37
|
+
setPageContext?: (context: Record<string, unknown>) => void;
|
|
38
|
+
registerAction?: (actionId: string, action: {
|
|
39
|
+
description: string;
|
|
40
|
+
parameters: Record<string, unknown>;
|
|
41
|
+
handler: (params: Record<string, unknown>) => Promise<unknown>;
|
|
42
|
+
}) => void;
|
|
43
|
+
startListening?: () => Promise<void>;
|
|
44
|
+
stopListening?: () => void;
|
|
45
|
+
}
|
|
46
|
+
/** Props for MockGuideKitProvider. */
|
|
47
|
+
interface MockGuideKitProviderProps {
|
|
48
|
+
initialState?: MockInitialState;
|
|
49
|
+
actions?: MockActions;
|
|
50
|
+
children: ReactNode;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A mock provider for testing components that use GuideKit hooks.
|
|
54
|
+
*
|
|
55
|
+
* Instead of creating a real GuideKitCore instance (which requires API keys,
|
|
56
|
+
* browser APIs, etc.), this provider supplies a lightweight mock object that
|
|
57
|
+
* satisfies the context contract used by all hooks in `@guidekit/react`.
|
|
58
|
+
*
|
|
59
|
+
* All actions default to noops but can be overridden with mock functions
|
|
60
|
+
* (e.g. `vi.fn()`).
|
|
61
|
+
*
|
|
62
|
+
* The provider injects the mock into the same `GuideKitContext` that the
|
|
63
|
+
* real hooks read from (imported from `./_context.js`), so hooks like
|
|
64
|
+
* `useGuideKitStatus`, `useGuideKitVoice`, etc. work seamlessly.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```tsx
|
|
68
|
+
* import { MockGuideKitProvider } from '@guidekit/react/testing';
|
|
69
|
+
*
|
|
70
|
+
* render(
|
|
71
|
+
* <MockGuideKitProvider
|
|
72
|
+
* initialState={{ isReady: true, agentState: { status: 'idle' } }}
|
|
73
|
+
* actions={{ sendText: vi.fn() }}
|
|
74
|
+
* >
|
|
75
|
+
* <ComponentUnderTest />
|
|
76
|
+
* </MockGuideKitProvider>
|
|
77
|
+
* );
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
declare function MockGuideKitProvider({ initialState, actions, children, }: MockGuideKitProviderProps): react_jsx_runtime.JSX.Element;
|
|
81
|
+
/**
|
|
82
|
+
* Simulates an assistant response being added to the agent state.
|
|
83
|
+
* Sets the agent state to `{ status: 'speaking', utterance: text }`.
|
|
84
|
+
*
|
|
85
|
+
* Subscribers (e.g. hooks using `useSyncExternalStore`) are notified
|
|
86
|
+
* synchronously, so assertions can run immediately after this call.
|
|
87
|
+
*/
|
|
88
|
+
declare function simulateAgentResponse(text: string): void;
|
|
89
|
+
/**
|
|
90
|
+
* Simulates a voice transcript arriving (as if the user spoke).
|
|
91
|
+
* Sets the agent state to `{ status: 'processing', transcript: text }`
|
|
92
|
+
* and marks `voice.isListening` as `false`.
|
|
93
|
+
*/
|
|
94
|
+
declare function simulateVoiceInput(text: string): void;
|
|
95
|
+
/**
|
|
96
|
+
* Simulates an error occurring in the agent.
|
|
97
|
+
* Sets the agent state to `{ status: 'error', error }` and populates
|
|
98
|
+
* the store-level `error` field.
|
|
99
|
+
*/
|
|
100
|
+
declare function simulateError(error: GuideKitErrorType): void;
|
|
101
|
+
/**
|
|
102
|
+
* Returns the current mock store state. Useful for assertions in tests.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* const state = getGuideKitTestState();
|
|
107
|
+
* expect(state.status.isReady).toBe(true);
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
declare function getGuideKitTestState(): GuideKitStore;
|
|
111
|
+
/**
|
|
112
|
+
* Resets the agent state back to idle and clears any error.
|
|
113
|
+
* Useful for cleaning up between simulate* calls in a test.
|
|
114
|
+
*/
|
|
115
|
+
declare function resetAgentState(): void;
|
|
116
|
+
/**
|
|
117
|
+
* Sets the `isReady` flag on the mock store. Useful to simulate the SDK
|
|
118
|
+
* finishing initialization.
|
|
119
|
+
*/
|
|
120
|
+
declare function simulateReady(isReady?: boolean): void;
|
|
121
|
+
/**
|
|
122
|
+
* Returns the raw TestStore instance for advanced use cases (e.g.,
|
|
123
|
+
* subscribing to changes directly or calling `setState` with a custom
|
|
124
|
+
* updater). Returns `null` if no MockGuideKitProvider is mounted.
|
|
125
|
+
*/
|
|
126
|
+
declare function getTestStore_UNSAFE(): TestStore | null;
|
|
127
|
+
/**
|
|
128
|
+
* Cleans up the global test store reference. Call this in `afterEach` or
|
|
129
|
+
* `afterAll` to prevent state leaking between tests.
|
|
130
|
+
*/
|
|
131
|
+
declare function cleanupTestStore(): void;
|
|
132
|
+
|
|
133
|
+
export { type MockActions, MockGuideKitProvider, type MockGuideKitProviderProps, type MockInitialState, TestStore, cleanupTestStore, getGuideKitTestState, getTestStore_UNSAFE, resetAgentState, simulateAgentResponse, simulateError, simulateReady, simulateVoiceInput };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { GuideKitContext } from './chunk-4NUEGCBT.js';
|
|
2
|
+
import { useRef } from 'react';
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
var DEFAULT_STORE = {
|
|
6
|
+
status: {
|
|
7
|
+
isReady: false,
|
|
8
|
+
agentState: { status: "idle" },
|
|
9
|
+
error: null
|
|
10
|
+
}};
|
|
11
|
+
var TestStore = class {
|
|
12
|
+
listeners = /* @__PURE__ */ new Set();
|
|
13
|
+
_state;
|
|
14
|
+
constructor(initial) {
|
|
15
|
+
this._state = {
|
|
16
|
+
status: {
|
|
17
|
+
isReady: initial?.isReady ?? DEFAULT_STORE.status.isReady,
|
|
18
|
+
agentState: initial?.agentState ?? DEFAULT_STORE.status.agentState,
|
|
19
|
+
error: initial?.error ?? DEFAULT_STORE.status.error
|
|
20
|
+
},
|
|
21
|
+
voice: {
|
|
22
|
+
isListening: false,
|
|
23
|
+
isSpeaking: false
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
subscribe = (listener) => {
|
|
28
|
+
this.listeners.add(listener);
|
|
29
|
+
return () => {
|
|
30
|
+
this.listeners.delete(listener);
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
getSnapshot = () => {
|
|
34
|
+
return this._state;
|
|
35
|
+
};
|
|
36
|
+
setState(updater) {
|
|
37
|
+
const next = updater(this._state);
|
|
38
|
+
if (next !== this._state) {
|
|
39
|
+
this._state = next;
|
|
40
|
+
this.emitChange();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Replace the state entirely. */
|
|
44
|
+
setStateDirectly(next) {
|
|
45
|
+
if (next !== this._state) {
|
|
46
|
+
this._state = next;
|
|
47
|
+
this.emitChange();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
emitChange() {
|
|
51
|
+
for (const listener of this.listeners) {
|
|
52
|
+
listener();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var _testStore = null;
|
|
57
|
+
function requireTestStore() {
|
|
58
|
+
if (!_testStore) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
"[GuideKit Testing] No MockGuideKitProvider is mounted. Wrap your component in <MockGuideKitProvider> before calling simulate* functions."
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return _testStore;
|
|
64
|
+
}
|
|
65
|
+
var noopVoid = () => {
|
|
66
|
+
};
|
|
67
|
+
var noopAsync = () => Promise.resolve();
|
|
68
|
+
var noopAsyncString = () => Promise.resolve("");
|
|
69
|
+
var noopAsyncBool = () => Promise.resolve(true);
|
|
70
|
+
function MockGuideKitProvider({
|
|
71
|
+
initialState,
|
|
72
|
+
actions,
|
|
73
|
+
children
|
|
74
|
+
}) {
|
|
75
|
+
const storeRef = useRef(null);
|
|
76
|
+
if (storeRef.current === null) {
|
|
77
|
+
storeRef.current = new TestStore(initialState);
|
|
78
|
+
}
|
|
79
|
+
_testStore = storeRef.current;
|
|
80
|
+
const store = storeRef.current;
|
|
81
|
+
const mockCoreRef = useRef(null);
|
|
82
|
+
if (mockCoreRef.current === null) {
|
|
83
|
+
mockCoreRef.current = {
|
|
84
|
+
// Store protocol (useSyncExternalStore)
|
|
85
|
+
subscribe: store.subscribe,
|
|
86
|
+
getSnapshot: store.getSnapshot,
|
|
87
|
+
// Actions
|
|
88
|
+
sendText: actions?.sendText ?? noopAsyncString,
|
|
89
|
+
highlight: actions?.highlight ?? noopVoid,
|
|
90
|
+
dismissHighlight: actions?.dismissHighlight ?? noopVoid,
|
|
91
|
+
scrollToSection: actions?.scrollToSection ?? noopVoid,
|
|
92
|
+
startTour: actions?.startTour ?? noopVoid,
|
|
93
|
+
navigate: actions?.navigate ?? noopAsyncBool,
|
|
94
|
+
setPageContext: actions?.setPageContext ?? noopVoid,
|
|
95
|
+
registerAction: actions?.registerAction ?? noopVoid,
|
|
96
|
+
startListening: actions?.startListening ?? noopAsync,
|
|
97
|
+
stopListening: actions?.stopListening ?? noopVoid,
|
|
98
|
+
// Read-only properties
|
|
99
|
+
hasVoice: false,
|
|
100
|
+
instanceId: "mock-test-instance",
|
|
101
|
+
isReady: initialState?.isReady ?? false,
|
|
102
|
+
agentState: initialState?.agentState ?? { status: "idle" },
|
|
103
|
+
// i18n stub (used by the widget internals)
|
|
104
|
+
i18n: { t: (key) => key },
|
|
105
|
+
// Lifecycle stubs
|
|
106
|
+
init: noopAsync,
|
|
107
|
+
destroy: noopAsync
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return /* @__PURE__ */ jsx(GuideKitContext.Provider, { value: mockCoreRef.current, children });
|
|
111
|
+
}
|
|
112
|
+
function simulateAgentResponse(text) {
|
|
113
|
+
const store = requireTestStore();
|
|
114
|
+
store.setState((prev) => ({
|
|
115
|
+
...prev,
|
|
116
|
+
status: {
|
|
117
|
+
...prev.status,
|
|
118
|
+
agentState: { status: "speaking", utterance: text }
|
|
119
|
+
}
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
function simulateVoiceInput(text) {
|
|
123
|
+
const store = requireTestStore();
|
|
124
|
+
store.setState((prev) => ({
|
|
125
|
+
...prev,
|
|
126
|
+
status: {
|
|
127
|
+
...prev.status,
|
|
128
|
+
agentState: { status: "processing", transcript: text }
|
|
129
|
+
},
|
|
130
|
+
voice: {
|
|
131
|
+
...prev.voice,
|
|
132
|
+
isListening: false
|
|
133
|
+
}
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
function simulateError(error) {
|
|
137
|
+
const store = requireTestStore();
|
|
138
|
+
store.setState((prev) => ({
|
|
139
|
+
...prev,
|
|
140
|
+
status: {
|
|
141
|
+
...prev.status,
|
|
142
|
+
agentState: { status: "error", error },
|
|
143
|
+
error
|
|
144
|
+
}
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
function getGuideKitTestState() {
|
|
148
|
+
const store = requireTestStore();
|
|
149
|
+
return store.getSnapshot();
|
|
150
|
+
}
|
|
151
|
+
function resetAgentState() {
|
|
152
|
+
const store = requireTestStore();
|
|
153
|
+
store.setState((prev) => ({
|
|
154
|
+
...prev,
|
|
155
|
+
status: {
|
|
156
|
+
...prev.status,
|
|
157
|
+
agentState: { status: "idle" },
|
|
158
|
+
error: null
|
|
159
|
+
},
|
|
160
|
+
voice: {
|
|
161
|
+
isListening: false,
|
|
162
|
+
isSpeaking: false
|
|
163
|
+
}
|
|
164
|
+
}));
|
|
165
|
+
}
|
|
166
|
+
function simulateReady(isReady = true) {
|
|
167
|
+
const store = requireTestStore();
|
|
168
|
+
store.setState((prev) => ({
|
|
169
|
+
...prev,
|
|
170
|
+
status: {
|
|
171
|
+
...prev.status,
|
|
172
|
+
isReady
|
|
173
|
+
}
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
function getTestStore_UNSAFE() {
|
|
177
|
+
return _testStore;
|
|
178
|
+
}
|
|
179
|
+
function cleanupTestStore() {
|
|
180
|
+
_testStore = null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export { MockGuideKitProvider, TestStore, cleanupTestStore, getGuideKitTestState, getTestStore_UNSAFE, resetAgentState, simulateAgentResponse, simulateError, simulateReady, simulateVoiceInput };
|
|
184
|
+
//# sourceMappingURL=testing.js.map
|
|
185
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing.tsx"],"names":[],"mappings":"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","file":"testing.js","sourcesContent":["// ---------------------------------------------------------------------------\n// @guidekit/react/testing — Testing utilities for apps using GuideKit\n// ---------------------------------------------------------------------------\n//\n// Provides MockGuideKitProvider and simulate* helpers so consumers can test\n// components that depend on GuideKit hooks without spinning up a real\n// GuideKitCore instance.\n//\n// Usage:\n// import { MockGuideKitProvider, simulateAgentResponse } from '@guidekit/react/testing';\n//\n// render(\n// <MockGuideKitProvider initialState={{ isReady: true }}>\n// <MyComponent />\n// </MockGuideKitProvider>\n// );\n//\n// simulateAgentResponse('Hello from the agent!');\n// ---------------------------------------------------------------------------\n\nimport { useRef } from 'react';\nimport type { ReactNode } from 'react';\nimport type { AgentState, GuideKitStore, GuideKitErrorType } from '@guidekit/core';\n\nimport { GuideKitContext } from './_context.js';\n\n// ---------------------------------------------------------------------------\n// Default store snapshot\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_STORE: GuideKitStore = {\n status: {\n isReady: false,\n agentState: { status: 'idle' },\n error: null,\n },\n voice: {\n isListening: false,\n isSpeaking: false,\n },\n};\n\n// ---------------------------------------------------------------------------\n// TestStore — a minimal external store compatible with useSyncExternalStore\n// ---------------------------------------------------------------------------\n\n/** @internal */\nexport class TestStore {\n private listeners = new Set<() => void>();\n private _state: GuideKitStore;\n\n constructor(initial?: Partial<GuideKitStore['status']>) {\n this._state = {\n status: {\n isReady: initial?.isReady ?? DEFAULT_STORE.status.isReady,\n agentState: initial?.agentState ?? DEFAULT_STORE.status.agentState,\n error: initial?.error ?? DEFAULT_STORE.status.error,\n },\n voice: {\n isListening: false,\n isSpeaking: false,\n },\n };\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n getSnapshot = (): GuideKitStore => {\n return this._state;\n };\n\n setState(updater: (prev: GuideKitStore) => GuideKitStore): void {\n const next = updater(this._state);\n if (next !== this._state) {\n this._state = next;\n this.emitChange();\n }\n }\n\n /** Replace the state entirely. */\n setStateDirectly(next: GuideKitStore): void {\n if (next !== this._state) {\n this._state = next;\n this.emitChange();\n }\n }\n\n private emitChange(): void {\n for (const listener of this.listeners) {\n listener();\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Global test store — shared across simulate* functions\n// ---------------------------------------------------------------------------\n\nlet _testStore: TestStore | null = null;\n\nfunction requireTestStore(): TestStore {\n if (!_testStore) {\n throw new Error(\n '[GuideKit Testing] No MockGuideKitProvider is mounted. ' +\n 'Wrap your component in <MockGuideKitProvider> before calling simulate* functions.',\n );\n }\n return _testStore;\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Initial state for the mock provider. */\nexport interface MockInitialState {\n isReady?: boolean;\n agentState?: AgentState;\n error?: GuideKitErrorType | null;\n}\n\n/** Mock action implementations to inject into the provider. */\nexport interface MockActions {\n sendText?: (text: string) => Promise<string>;\n highlight?: (params: {\n sectionId?: string;\n selector?: string;\n tooltip?: string;\n position?: 'top' | 'bottom' | 'left' | 'right' | 'auto';\n }) => void;\n dismissHighlight?: () => void;\n scrollToSection?: (sectionId: string, offset?: number) => void;\n startTour?: (sectionIds: string[], mode?: 'auto' | 'manual') => void;\n navigate?: (href: string) => Promise<boolean>;\n setPageContext?: (context: Record<string, unknown>) => void;\n registerAction?: (\n actionId: string,\n action: {\n description: string;\n parameters: Record<string, unknown>;\n handler: (params: Record<string, unknown>) => Promise<unknown>;\n },\n ) => void;\n startListening?: () => Promise<void>;\n stopListening?: () => void;\n}\n\n/** Props for MockGuideKitProvider. */\nexport interface MockGuideKitProviderProps {\n initialState?: MockInitialState;\n actions?: MockActions;\n children: ReactNode;\n}\n\n// ---------------------------------------------------------------------------\n// Noop stubs\n// ---------------------------------------------------------------------------\n\nconst noopVoid = () => {};\nconst noopAsync = () => Promise.resolve();\nconst noopAsyncString = () => Promise.resolve('');\nconst noopAsyncBool = () => Promise.resolve(true);\n\n// ---------------------------------------------------------------------------\n// MockGuideKitProvider\n// ---------------------------------------------------------------------------\n\n/**\n * A mock provider for testing components that use GuideKit hooks.\n *\n * Instead of creating a real GuideKitCore instance (which requires API keys,\n * browser APIs, etc.), this provider supplies a lightweight mock object that\n * satisfies the context contract used by all hooks in `@guidekit/react`.\n *\n * All actions default to noops but can be overridden with mock functions\n * (e.g. `vi.fn()`).\n *\n * The provider injects the mock into the same `GuideKitContext` that the\n * real hooks read from (imported from `./_context.js`), so hooks like\n * `useGuideKitStatus`, `useGuideKitVoice`, etc. work seamlessly.\n *\n * @example\n * ```tsx\n * import { MockGuideKitProvider } from '@guidekit/react/testing';\n *\n * render(\n * <MockGuideKitProvider\n * initialState={{ isReady: true, agentState: { status: 'idle' } }}\n * actions={{ sendText: vi.fn() }}\n * >\n * <ComponentUnderTest />\n * </MockGuideKitProvider>\n * );\n * ```\n */\nexport function MockGuideKitProvider({\n initialState,\n actions,\n children,\n}: MockGuideKitProviderProps) {\n // Create a TestStore on first render, keep it stable across re-renders.\n const storeRef = useRef<TestStore | null>(null);\n if (storeRef.current === null) {\n storeRef.current = new TestStore(initialState);\n }\n\n // Register as the global test store so simulate* functions can find it.\n _testStore = storeRef.current;\n\n const store = storeRef.current;\n\n // Build the mock object that duck-types as GuideKitCore.\n // Kept stable via a ref so the context value identity does not change.\n const mockCoreRef = useRef<Record<string, unknown> | null>(null);\n if (mockCoreRef.current === null) {\n mockCoreRef.current = {\n // Store protocol (useSyncExternalStore)\n subscribe: store.subscribe,\n getSnapshot: store.getSnapshot,\n\n // Actions\n sendText: actions?.sendText ?? noopAsyncString,\n highlight: actions?.highlight ?? noopVoid,\n dismissHighlight: actions?.dismissHighlight ?? noopVoid,\n scrollToSection: actions?.scrollToSection ?? noopVoid,\n startTour: actions?.startTour ?? noopVoid,\n navigate: actions?.navigate ?? noopAsyncBool,\n setPageContext: actions?.setPageContext ?? noopVoid,\n registerAction: actions?.registerAction ?? noopVoid,\n startListening: actions?.startListening ?? noopAsync,\n stopListening: actions?.stopListening ?? noopVoid,\n\n // Read-only properties\n hasVoice: false,\n instanceId: 'mock-test-instance',\n isReady: initialState?.isReady ?? false,\n agentState: initialState?.agentState ?? { status: 'idle' as const },\n\n // i18n stub (used by the widget internals)\n i18n: { t: (key: string) => key },\n\n // Lifecycle stubs\n init: noopAsync,\n destroy: noopAsync,\n };\n }\n\n // Provide the mock into the same GuideKitContext that the real hooks\n // consume, so useGuideKitStatus, useGuideKitVoice, etc. all work.\n return (\n <GuideKitContext.Provider value={mockCoreRef.current as any}>\n {children}\n </GuideKitContext.Provider>\n );\n}\n\n// ---------------------------------------------------------------------------\n// simulate* functions — update the global test store\n// ---------------------------------------------------------------------------\n\n/**\n * Simulates an assistant response being added to the agent state.\n * Sets the agent state to `{ status: 'speaking', utterance: text }`.\n *\n * Subscribers (e.g. hooks using `useSyncExternalStore`) are notified\n * synchronously, so assertions can run immediately after this call.\n */\nexport function simulateAgentResponse(text: string): void {\n const store = requireTestStore();\n\n store.setState((prev) => ({\n ...prev,\n status: {\n ...prev.status,\n agentState: { status: 'speaking', utterance: text } as AgentState,\n },\n }));\n}\n\n/**\n * Simulates a voice transcript arriving (as if the user spoke).\n * Sets the agent state to `{ status: 'processing', transcript: text }`\n * and marks `voice.isListening` as `false`.\n */\nexport function simulateVoiceInput(text: string): void {\n const store = requireTestStore();\n\n store.setState((prev) => ({\n ...prev,\n status: {\n ...prev.status,\n agentState: { status: 'processing', transcript: text } as AgentState,\n },\n voice: {\n ...prev.voice,\n isListening: false,\n },\n }));\n}\n\n/**\n * Simulates an error occurring in the agent.\n * Sets the agent state to `{ status: 'error', error }` and populates\n * the store-level `error` field.\n */\nexport function simulateError(error: GuideKitErrorType): void {\n const store = requireTestStore();\n\n store.setState((prev) => ({\n ...prev,\n status: {\n ...prev.status,\n agentState: { status: 'error', error } as AgentState,\n error,\n },\n }));\n}\n\n/**\n * Returns the current mock store state. Useful for assertions in tests.\n *\n * @example\n * ```ts\n * const state = getGuideKitTestState();\n * expect(state.status.isReady).toBe(true);\n * ```\n */\nexport function getGuideKitTestState(): GuideKitStore {\n const store = requireTestStore();\n return store.getSnapshot();\n}\n\n// ---------------------------------------------------------------------------\n// Additional helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resets the agent state back to idle and clears any error.\n * Useful for cleaning up between simulate* calls in a test.\n */\nexport function resetAgentState(): void {\n const store = requireTestStore();\n\n store.setState((prev) => ({\n ...prev,\n status: {\n ...prev.status,\n agentState: { status: 'idle' } as AgentState,\n error: null,\n },\n voice: {\n isListening: false,\n isSpeaking: false,\n },\n }));\n}\n\n/**\n * Sets the `isReady` flag on the mock store. Useful to simulate the SDK\n * finishing initialization.\n */\nexport function simulateReady(isReady: boolean = true): void {\n const store = requireTestStore();\n\n store.setState((prev) => ({\n ...prev,\n status: {\n ...prev.status,\n isReady,\n },\n }));\n}\n\n/**\n * Returns the raw TestStore instance for advanced use cases (e.g.,\n * subscribing to changes directly or calling `setState` with a custom\n * updater). Returns `null` if no MockGuideKitProvider is mounted.\n */\nexport function getTestStore_UNSAFE(): TestStore | null {\n return _testStore;\n}\n\n/**\n * Cleans up the global test store reference. Call this in `afterEach` or\n * `afterAll` to prevent state leaking between tests.\n */\nexport function cleanupTestStore(): void {\n _testStore = null;\n}\n\n// ---------------------------------------------------------------------------\n// Re-export types for convenience\n// ---------------------------------------------------------------------------\n\nexport type { AgentState, GuideKitStore, GuideKitErrorType };\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@guidekit/react",
|
|
3
|
+
"version": "0.1.0-beta.1",
|
|
4
|
+
"description": "React bindings for GuideKit SDK — Provider, hooks, and Widget",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./devtools": {
|
|
16
|
+
"types": "./dist/devtools.d.ts",
|
|
17
|
+
"import": "./dist/devtools.js",
|
|
18
|
+
"require": "./dist/devtools.cjs"
|
|
19
|
+
},
|
|
20
|
+
"./testing": {
|
|
21
|
+
"types": "./dist/testing.d.ts",
|
|
22
|
+
"import": "./dist/testing.js",
|
|
23
|
+
"require": "./dist/testing.cjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"../../LICENSE",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsup",
|
|
33
|
+
"dev": "tsup --watch",
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"test:unit": "vitest run",
|
|
36
|
+
"test:watch": "vitest",
|
|
37
|
+
"typecheck": "tsc --noEmit",
|
|
38
|
+
"clean": "rm -rf dist"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@guidekit/core": "workspace:*"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"react": ">=18.0.0",
|
|
45
|
+
"react-dom": ">=18.0.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"typescript": "^5.7.0",
|
|
49
|
+
"tsup": "^8.4.0",
|
|
50
|
+
"vitest": "^3.0.0",
|
|
51
|
+
"react": "^19.0.0",
|
|
52
|
+
"react-dom": "^19.0.0",
|
|
53
|
+
"@types/react": "^19.0.0",
|
|
54
|
+
"@types/react-dom": "^19.0.0"
|
|
55
|
+
},
|
|
56
|
+
"sideEffects": false,
|
|
57
|
+
"license": "MIT"
|
|
58
|
+
}
|