@thoughtspot/visual-embed-sdk 1.39.2 → 1.39.3
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/cjs/package.json +1 -1
- package/cjs/src/config.spec.js +9 -0
- package/cjs/src/config.spec.js.map +1 -1
- package/cjs/src/embed/app.d.ts +19 -15
- package/cjs/src/embed/app.d.ts.map +1 -1
- package/cjs/src/embed/app.js +23 -2
- package/cjs/src/embed/app.js.map +1 -1
- package/cjs/src/embed/app.spec.js +56 -8
- package/cjs/src/embed/app.spec.js.map +1 -1
- package/cjs/src/embed/bodyless-conversation.d.ts +23 -7
- package/cjs/src/embed/bodyless-conversation.d.ts.map +1 -1
- package/cjs/src/embed/bodyless-conversation.js +31 -5
- package/cjs/src/embed/bodyless-conversation.js.map +1 -1
- package/cjs/src/embed/bodyless-conversation.spec.js +8 -190
- package/cjs/src/embed/bodyless-conversation.spec.js.map +1 -1
- package/cjs/src/embed/conversation.spec.js +28 -0
- package/cjs/src/embed/conversation.spec.js.map +1 -1
- package/cjs/src/embed/embedConfig.d.ts +9 -7
- package/cjs/src/embed/embedConfig.d.ts.map +1 -1
- package/cjs/src/embed/embedConfig.js +9 -7
- package/cjs/src/embed/embedConfig.js.map +1 -1
- package/cjs/src/embed/liveboard.d.ts +0 -17
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js +2 -4
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/liveboard.spec.js +12 -11
- package/cjs/src/embed/liveboard.spec.js.map +1 -1
- package/cjs/src/errors.d.ts +1 -0
- package/cjs/src/errors.d.ts.map +1 -1
- package/cjs/src/errors.js +1 -0
- package/cjs/src/errors.js.map +1 -1
- package/cjs/src/index.d.ts +2 -2
- package/cjs/src/index.d.ts.map +1 -1
- package/cjs/src/index.js +2 -1
- package/cjs/src/index.js.map +1 -1
- package/cjs/src/react/all-types-export.d.ts +1 -1
- package/cjs/src/react/all-types-export.d.ts.map +1 -1
- package/cjs/src/react/all-types-export.js +3 -2
- package/cjs/src/react/all-types-export.js.map +1 -1
- package/cjs/src/react/index.d.ts +73 -20
- package/cjs/src/react/index.d.ts.map +1 -1
- package/cjs/src/react/index.js +79 -42
- package/cjs/src/react/index.js.map +1 -1
- package/cjs/src/react/index.spec.js +438 -100
- package/cjs/src/react/index.spec.js.map +1 -1
- package/cjs/src/types.d.ts +262 -6
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +228 -2
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/utils/global-styles.js +1 -1
- package/cjs/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
- package/cjs/src/utils/graphql/nlsService/conversation-service.js +7 -1
- package/cjs/src/utils/graphql/nlsService/conversation-service.js.map +1 -1
- package/cjs/src/utils.spec.js +25 -0
- package/cjs/src/utils.spec.js.map +1 -1
- package/dist/{index-CmEQfuE3.js → index-ZrE8YYq8.js} +1 -1
- package/dist/src/embed/app.d.ts +19 -15
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/bodyless-conversation.d.ts +23 -7
- package/dist/src/embed/bodyless-conversation.d.ts.map +1 -1
- package/dist/src/embed/embedConfig.d.ts +9 -7
- package/dist/src/embed/embedConfig.d.ts.map +1 -1
- package/dist/src/embed/liveboard.d.ts +0 -17
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/errors.d.ts +1 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/react/all-types-export.d.ts +1 -1
- package/dist/src/react/all-types-export.d.ts.map +1 -1
- package/dist/src/react/index.d.ts +73 -20
- package/dist/src/react/index.d.ts.map +1 -1
- package/dist/src/types.d.ts +262 -6
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
- package/dist/tsembed-react.es.js +385 -71
- package/dist/tsembed-react.js +385 -69
- package/dist/tsembed.es.js +303 -24
- package/dist/tsembed.js +301 -22
- package/dist/visual-embed-sdk-react-full.d.ts +386 -72
- package/dist/visual-embed-sdk-react.d.ts +386 -72
- package/dist/visual-embed-sdk.d.ts +314 -53
- package/lib/package.json +1 -1
- package/lib/src/config.spec.js +9 -0
- package/lib/src/config.spec.js.map +1 -1
- package/lib/src/embed/app.d.ts +19 -15
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js +22 -1
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/app.spec.js +58 -10
- package/lib/src/embed/app.spec.js.map +1 -1
- package/lib/src/embed/bodyless-conversation.d.ts +23 -7
- package/lib/src/embed/bodyless-conversation.d.ts.map +1 -1
- package/lib/src/embed/bodyless-conversation.js +30 -5
- package/lib/src/embed/bodyless-conversation.js.map +1 -1
- package/lib/src/embed/bodyless-conversation.spec.js +9 -191
- package/lib/src/embed/bodyless-conversation.spec.js.map +1 -1
- package/lib/src/embed/conversation.spec.js +30 -2
- package/lib/src/embed/conversation.spec.js.map +1 -1
- package/lib/src/embed/embedConfig.d.ts +9 -7
- package/lib/src/embed/embedConfig.d.ts.map +1 -1
- package/lib/src/embed/embedConfig.js +9 -7
- package/lib/src/embed/embedConfig.js.map +1 -1
- package/lib/src/embed/liveboard.d.ts +0 -17
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js +2 -4
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/liveboard.spec.js +12 -11
- package/lib/src/embed/liveboard.spec.js.map +1 -1
- package/lib/src/errors.d.ts +1 -0
- package/lib/src/errors.d.ts.map +1 -1
- package/lib/src/errors.js +1 -0
- package/lib/src/errors.js.map +1 -1
- package/lib/src/index.d.ts +2 -2
- package/lib/src/index.d.ts.map +1 -1
- package/lib/src/index.js +2 -2
- package/lib/src/index.js.map +1 -1
- package/lib/src/react/all-types-export.d.ts +1 -1
- package/lib/src/react/all-types-export.d.ts.map +1 -1
- package/lib/src/react/all-types-export.js +1 -1
- package/lib/src/react/all-types-export.js.map +1 -1
- package/lib/src/react/index.d.ts +73 -20
- package/lib/src/react/index.d.ts.map +1 -1
- package/lib/src/react/index.js +79 -43
- package/lib/src/react/index.js.map +1 -1
- package/lib/src/react/index.spec.js +441 -103
- package/lib/src/react/index.spec.js.map +1 -1
- package/lib/src/types.d.ts +262 -6
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +228 -2
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/global-styles.js +1 -1
- package/lib/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
- package/lib/src/utils/graphql/nlsService/conversation-service.js +7 -1
- package/lib/src/utils/graphql/nlsService/conversation-service.js.map +1 -1
- package/lib/src/utils.spec.js +26 -1
- package/lib/src/utils.spec.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +315 -54
- package/package.json +1 -1
- package/src/config.spec.ts +11 -0
- package/src/embed/app.spec.ts +90 -23
- package/src/embed/app.ts +27 -15
- package/src/embed/bodyless-conversation.spec.ts +9 -203
- package/src/embed/bodyless-conversation.ts +34 -10
- package/src/embed/conversation.spec.ts +40 -2
- package/src/embed/embedConfig.ts +10 -8
- package/src/embed/liveboard.spec.ts +5 -4
- package/src/embed/liveboard.ts +2 -22
- package/src/errors.ts +1 -0
- package/src/index.ts +2 -0
- package/src/react/all-types-export.ts +2 -1
- package/src/react/index.spec.tsx +558 -157
- package/src/react/index.tsx +117 -51
- package/src/types.ts +301 -44
- package/src/utils/global-styles.ts +1 -1
- package/src/utils/graphql/nlsService/conversation-service.ts +7 -1
- package/src/utils.spec.ts +29 -0
package/src/react/index.spec.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
2
|
import '@testing-library/jest-dom';
|
|
3
3
|
import '@testing-library/jest-dom/extend-expect';
|
|
4
4
|
import {
|
|
@@ -15,8 +15,7 @@ import {
|
|
|
15
15
|
mockMessageChannel,
|
|
16
16
|
} from '../test/test-utils';
|
|
17
17
|
import {
|
|
18
|
-
SearchEmbed, AppEmbed, LiveboardEmbed, useEmbedRef, SearchBarEmbed, PreRenderedLiveboardEmbed,
|
|
19
|
-
SpotterAgentEmbed
|
|
18
|
+
SearchEmbed, AppEmbed, LiveboardEmbed, useEmbedRef, SearchBarEmbed, PreRenderedLiveboardEmbed, PreRenderedSearchEmbed, PreRenderedAppEmbed, useSpotterAgent, SpotterMessage, useInit
|
|
20
19
|
} from './index';
|
|
21
20
|
import * as allExports from './index';
|
|
22
21
|
import {
|
|
@@ -236,199 +235,601 @@ describe('React Components', () => {
|
|
|
236
235
|
});
|
|
237
236
|
});
|
|
238
237
|
|
|
239
|
-
describe('
|
|
240
|
-
|
|
241
|
-
|
|
238
|
+
describe('SpotterMessage', () => {
|
|
239
|
+
const mockMessage = {
|
|
240
|
+
sessionId: "session123",
|
|
241
|
+
genNo: 1,
|
|
242
|
+
acSessionId: "acSession123",
|
|
243
|
+
acGenNo: 2,
|
|
244
|
+
worksheetId: "worksheet123",
|
|
245
|
+
convId: "conv123",
|
|
246
|
+
messageId: "message123"
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
it('Should render the SpotterMessage component with required props', async () => {
|
|
250
|
+
const { container } = render(
|
|
251
|
+
<SpotterMessage message={mockMessage} />,
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
await waitFor(() => getIFrameEl(container));
|
|
255
|
+
|
|
256
|
+
expect(getIFrameEl(container)).not.toBe(null);
|
|
257
|
+
expect(getIFrameSrc(container)).toContain('sessionId=session123');
|
|
258
|
+
expect(getIFrameSrc(container)).toContain('genNo=1');
|
|
259
|
+
expect(getIFrameSrc(container)).toContain('acSessionId=acSession123');
|
|
260
|
+
expect(getIFrameSrc(container)).toContain('acGenNo=2');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('Should render the SpotterMessage component with optional query', async () => {
|
|
264
|
+
const { container } = render(
|
|
265
|
+
<SpotterMessage
|
|
266
|
+
message={mockMessage}
|
|
267
|
+
query="show me sales"
|
|
268
|
+
/>,
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
await waitFor(() => getIFrameEl(container));
|
|
272
|
+
|
|
273
|
+
expect(getIFrameEl(container)).not.toBe(null);
|
|
274
|
+
expect(getIFrameSrc(container)).toContain('sessionId=session123');
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('Should have the correct container element with className', async () => {
|
|
278
|
+
const { container } = render(
|
|
279
|
+
<SpotterMessage
|
|
280
|
+
message={mockMessage}
|
|
281
|
+
className="custom-class"
|
|
282
|
+
/>,
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
await waitFor(() => getIFrameEl(container));
|
|
286
|
+
|
|
287
|
+
expect(
|
|
288
|
+
getIFrameEl(container).parentElement.classList.contains('custom-class'),
|
|
289
|
+
).toBe(true);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Note: insertAsSibling is not supported for SpotterMessage as it's not part of the allowed props
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe('Component Factory Coverage', () => {
|
|
296
|
+
it('Should test basic component creation', () => {
|
|
297
|
+
expect(() => {
|
|
298
|
+
render(<LiveboardEmbed liveboardId="test" />);
|
|
299
|
+
}).not.toThrow();
|
|
242
300
|
|
|
243
|
-
|
|
301
|
+
expect(() => {
|
|
302
|
+
render(<SearchEmbed dataSource="test" />);
|
|
303
|
+
}).not.toThrow();
|
|
244
304
|
|
|
245
|
-
|
|
246
|
-
<
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
305
|
+
expect(() => {
|
|
306
|
+
render(<AppEmbed showPrimaryNavbar={false} />);
|
|
307
|
+
}).not.toThrow();
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('Should test component factory existence', () => {
|
|
311
|
+
expect(PreRenderedLiveboardEmbed).toBeDefined();
|
|
312
|
+
expect(PreRenderedSearchEmbed).toBeDefined();
|
|
313
|
+
expect(PreRenderedAppEmbed).toBeDefined();
|
|
314
|
+
expect(typeof PreRenderedLiveboardEmbed).toBe('object');
|
|
315
|
+
expect(typeof PreRenderedSearchEmbed).toBe('object');
|
|
316
|
+
expect(typeof PreRenderedAppEmbed).toBe('object');
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe('Components with insertAsSibling', () => {
|
|
321
|
+
it('Should render LiveboardEmbed with insertAsSibling', async () => {
|
|
322
|
+
const { container } = render(
|
|
323
|
+
<LiveboardEmbed
|
|
324
|
+
liveboardId="test-liveboard"
|
|
325
|
+
insertAsSibling={true}
|
|
326
|
+
/>,
|
|
258
327
|
);
|
|
259
328
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
329
|
+
await waitFor(() => getIFrameEl(container));
|
|
330
|
+
expect(container.querySelector('span')).not.toBe(null);
|
|
331
|
+
expect(container.querySelector('span')?.style.position).toBe('absolute');
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('Should render SearchEmbed with insertAsSibling', async () => {
|
|
335
|
+
const { container } = render(
|
|
336
|
+
<SearchEmbed
|
|
337
|
+
dataSource="test-datasource"
|
|
338
|
+
insertAsSibling={true}
|
|
339
|
+
/>,
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
await waitFor(() => getIFrameEl(container));
|
|
343
|
+
expect(container.querySelector('span')).not.toBe(null);
|
|
344
|
+
expect(container.querySelector('span')?.style.position).toBe('absolute');
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('Should render AppEmbed with insertAsSibling', async () => {
|
|
348
|
+
const { container } = render(
|
|
349
|
+
<AppEmbed
|
|
350
|
+
showPrimaryNavbar={false}
|
|
351
|
+
insertAsSibling={true}
|
|
352
|
+
/>,
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
await waitFor(() => getIFrameEl(container));
|
|
356
|
+
expect(container.querySelector('span')).not.toBe(null);
|
|
357
|
+
expect(container.querySelector('span')?.style.position).toBe('absolute');
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('Should render SearchBarEmbed with insertAsSibling', async () => {
|
|
361
|
+
const { container } = render(
|
|
362
|
+
<SearchBarEmbed
|
|
363
|
+
dataSource="test-datasource"
|
|
364
|
+
insertAsSibling={true}
|
|
365
|
+
/>,
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
await waitFor(() => getIFrameEl(container));
|
|
369
|
+
expect(container.querySelector('span')).not.toBe(null);
|
|
370
|
+
expect(container.querySelector('span')?.style.position).toBe('absolute');
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('Should render components with both insertAsSibling and className', async () => {
|
|
374
|
+
const { container } = render(
|
|
375
|
+
<LiveboardEmbed
|
|
376
|
+
liveboardId="test-liveboard"
|
|
377
|
+
insertAsSibling={true}
|
|
378
|
+
className="custom-class"
|
|
379
|
+
/>,
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
await waitFor(() => getIFrameEl(container));
|
|
383
|
+
expect(container.querySelector('span')).not.toBe(null);
|
|
384
|
+
expect(getIFrameEl(container).classList.contains('custom-class')).toBe(true);
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
describe('useSpotterAgent', () => {
|
|
389
|
+
it('Should return an object with sendMessage function', () => {
|
|
390
|
+
const TestComponent = () => {
|
|
391
|
+
const spotterAgent = useSpotterAgent({ worksheetId: 'test-worksheet' });
|
|
392
|
+
expect(typeof spotterAgent).toBe('object');
|
|
393
|
+
expect(typeof spotterAgent.sendMessage).toBe('function');
|
|
394
|
+
return <div>Test</div>;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
render(<TestComponent />);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('Should have proper sendMessage callback structure', () => {
|
|
401
|
+
const TestComponent = () => {
|
|
402
|
+
const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
|
|
403
|
+
|
|
404
|
+
// Test that sendMessage is a function that accepts a string
|
|
405
|
+
expect(typeof sendMessage).toBe('function');
|
|
406
|
+
expect(sendMessage.length).toBe(1); // Should accept one parameter
|
|
264
407
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
408
|
+
return <div>Test</div>;
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
render(<TestComponent />);
|
|
268
412
|
});
|
|
269
413
|
|
|
270
|
-
it('Should
|
|
271
|
-
|
|
272
|
-
container: document.createElement('div'),
|
|
273
|
-
viz: {}
|
|
274
|
-
});
|
|
414
|
+
it('Should return error when service is not initialized', async () => {
|
|
415
|
+
let sendMessageResult: any;
|
|
275
416
|
|
|
276
417
|
const TestComponent = () => {
|
|
277
|
-
const
|
|
418
|
+
const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
|
|
278
419
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
conversationRef.current.sendMessage = mockSendMessage;
|
|
282
|
-
|
|
283
|
-
conversationRef.current.sendMessage("Test message");
|
|
284
|
-
}
|
|
285
|
-
};
|
|
420
|
+
// Call sendMessage immediately before service has time to initialize
|
|
421
|
+
sendMessageResult = sendMessage('test query');
|
|
286
422
|
|
|
287
|
-
return
|
|
288
|
-
<>
|
|
289
|
-
<SpotterAgentEmbed
|
|
290
|
-
ref={conversationRef}
|
|
291
|
-
worksheetId="test-worksheet-id"
|
|
292
|
-
/>
|
|
293
|
-
<button data-testid="test-button" onClick={handleClick}>
|
|
294
|
-
Send Message
|
|
295
|
-
</button>
|
|
296
|
-
</>
|
|
297
|
-
);
|
|
423
|
+
return <div>Test</div>;
|
|
298
424
|
};
|
|
425
|
+
|
|
426
|
+
render(<TestComponent />);
|
|
299
427
|
|
|
300
|
-
const
|
|
428
|
+
const result = await sendMessageResult;
|
|
429
|
+
expect(result).toEqual({
|
|
430
|
+
error: expect.any(Error)
|
|
431
|
+
});
|
|
432
|
+
expect(result.error.message).toBe('SpotterAgent not initialized');
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('Should call sendMessage and handle async behavior', async () => {
|
|
436
|
+
let sendMessageFunction: any;
|
|
301
437
|
|
|
302
|
-
|
|
438
|
+
const TestComponent = () => {
|
|
439
|
+
const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
|
|
440
|
+
sendMessageFunction = sendMessage;
|
|
441
|
+
return <div>Test</div>;
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
render(<TestComponent />);
|
|
303
445
|
|
|
304
|
-
|
|
446
|
+
// Test that sendMessage is a function
|
|
447
|
+
expect(typeof sendMessageFunction).toBe('function');
|
|
448
|
+
|
|
449
|
+
// Call sendMessage - should not throw
|
|
450
|
+
expect(() => {
|
|
451
|
+
sendMessageFunction('test query');
|
|
452
|
+
}).not.toThrow();
|
|
305
453
|
});
|
|
306
454
|
|
|
307
|
-
it('Should
|
|
308
|
-
|
|
309
|
-
container: document.createElement('div'),
|
|
310
|
-
viz: {}
|
|
311
|
-
});
|
|
455
|
+
it('Should handle multiple calls to sendMessage', async () => {
|
|
456
|
+
let sendMessageFunction: any;
|
|
312
457
|
|
|
313
458
|
const TestComponent = () => {
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (embedRef.current) {
|
|
318
|
-
const service = embedRef.current as unknown as {
|
|
319
|
-
sendMessage: typeof mockSendMessage
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
service.sendMessage = mockSendMessage;
|
|
323
|
-
|
|
324
|
-
service.sendMessage("Test with useEmbedRef");
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
return (
|
|
329
|
-
<>
|
|
330
|
-
<SpotterAgentEmbed
|
|
331
|
-
ref={embedRef}
|
|
332
|
-
worksheetId="test-worksheet-id"
|
|
333
|
-
/>
|
|
334
|
-
<button data-testid="use-embed-ref-button" onClick={handleClick}>
|
|
335
|
-
Send with useEmbedRef
|
|
336
|
-
</button>
|
|
337
|
-
</>
|
|
338
|
-
);
|
|
459
|
+
const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
|
|
460
|
+
sendMessageFunction = sendMessage;
|
|
461
|
+
return <div>Test</div>;
|
|
339
462
|
};
|
|
463
|
+
|
|
464
|
+
render(<TestComponent />);
|
|
340
465
|
|
|
341
|
-
|
|
466
|
+
// Multiple calls should not throw
|
|
467
|
+
expect(() => {
|
|
468
|
+
sendMessageFunction('query 1');
|
|
469
|
+
sendMessageFunction('query 2');
|
|
470
|
+
sendMessageFunction('query 3');
|
|
471
|
+
}).not.toThrow();
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it('Should handle config object changes', () => {
|
|
475
|
+
const TestComponent = ({ config }: { config: any }) => {
|
|
476
|
+
const { sendMessage } = useSpotterAgent(config);
|
|
477
|
+
expect(sendMessage).toBeDefined();
|
|
478
|
+
return <div>Test</div>;
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
const config1 = { worksheetId: 'test1' };
|
|
482
|
+
const config2 = { worksheetId: 'test2' };
|
|
342
483
|
|
|
343
|
-
|
|
484
|
+
const { rerender } = render(<TestComponent config={config1} />);
|
|
344
485
|
|
|
345
|
-
|
|
486
|
+
// Should not throw when config changes
|
|
487
|
+
expect(() => {
|
|
488
|
+
rerender(<TestComponent config={config2} />);
|
|
489
|
+
}).not.toThrow();
|
|
346
490
|
});
|
|
347
491
|
|
|
348
|
-
it('Should
|
|
349
|
-
let capturedInstance: any = null;
|
|
350
|
-
|
|
492
|
+
it('Should handle unmounting without errors', () => {
|
|
351
493
|
const TestComponent = () => {
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
capturedInstance = embedRef.current;
|
|
356
|
-
|
|
357
|
-
if (capturedInstance) {
|
|
358
|
-
const mockConversationService = {
|
|
359
|
-
sendMessage: jest.fn().mockResolvedValue({
|
|
360
|
-
data: {
|
|
361
|
-
sessionId: 'test-session',
|
|
362
|
-
genNo: 1,
|
|
363
|
-
stateKey: {
|
|
364
|
-
transactionId: 'test-transaction',
|
|
365
|
-
generationNumber: 1
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
})
|
|
369
|
-
};
|
|
370
|
-
(capturedInstance as any).conversationService = mockConversationService;
|
|
371
|
-
}
|
|
372
|
-
}, []);
|
|
373
|
-
|
|
374
|
-
return (
|
|
375
|
-
<SpotterAgentEmbed
|
|
376
|
-
ref={embedRef}
|
|
377
|
-
worksheetId="test-worksheet-id"
|
|
378
|
-
className="embedClass"
|
|
379
|
-
/>
|
|
380
|
-
);
|
|
494
|
+
const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
|
|
495
|
+
expect(sendMessage).toBeDefined();
|
|
496
|
+
return <div>Test</div>;
|
|
381
497
|
};
|
|
498
|
+
|
|
499
|
+
const { unmount } = render(<TestComponent />);
|
|
382
500
|
|
|
383
|
-
|
|
501
|
+
// Should not throw when unmounting
|
|
502
|
+
expect(() => {
|
|
503
|
+
unmount();
|
|
504
|
+
}).not.toThrow();
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
it('Should create stable hook structure', () => {
|
|
508
|
+
let hookResult1: any, hookResult2: any;
|
|
384
509
|
|
|
385
|
-
|
|
510
|
+
const TestComponent = ({ counter }: { counter: number }) => {
|
|
511
|
+
const result = useSpotterAgent({ worksheetId: 'test-worksheet' });
|
|
512
|
+
|
|
513
|
+
if (counter === 1) {
|
|
514
|
+
hookResult1 = result;
|
|
515
|
+
} else {
|
|
516
|
+
hookResult2 = result;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return <div>Test</div>;
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
const { rerender } = render(<TestComponent counter={1} />);
|
|
523
|
+
rerender(<TestComponent counter={2} />);
|
|
386
524
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
}
|
|
525
|
+
// Both should have same structure
|
|
526
|
+
expect(hookResult1).toEqual({ sendMessage: expect.any(Function) });
|
|
527
|
+
expect(hookResult2).toEqual({ sendMessage: expect.any(Function) });
|
|
391
528
|
});
|
|
529
|
+
|
|
530
|
+
it('Should handle different worksheet IDs', () => {
|
|
531
|
+
const TestComponent = ({ worksheetId }: { worksheetId: string }) => {
|
|
532
|
+
const { sendMessage } = useSpotterAgent({ worksheetId });
|
|
533
|
+
expect(sendMessage).toBeDefined();
|
|
534
|
+
return <div>Test</div>;
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
const { rerender } = render(<TestComponent worksheetId="worksheet1" />);
|
|
538
|
+
|
|
539
|
+
// Should handle different worksheet IDs
|
|
540
|
+
expect(() => {
|
|
541
|
+
rerender(<TestComponent worksheetId="worksheet2" />);
|
|
542
|
+
rerender(<TestComponent worksheetId="worksheet3" />);
|
|
543
|
+
}).not.toThrow();
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
it('Should handle empty query strings', () => {
|
|
547
|
+
let sendMessageFunction: any;
|
|
548
|
+
|
|
549
|
+
const TestComponent = () => {
|
|
550
|
+
const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
|
|
551
|
+
sendMessageFunction = sendMessage;
|
|
552
|
+
return <div>Test</div>;
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
render(<TestComponent />);
|
|
556
|
+
|
|
557
|
+
// Should handle empty strings
|
|
558
|
+
expect(() => {
|
|
559
|
+
sendMessageFunction('');
|
|
560
|
+
sendMessageFunction(' ');
|
|
561
|
+
}).not.toThrow();
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it('Should handle complex config objects', () => {
|
|
565
|
+
const complexConfig = {
|
|
566
|
+
worksheetId: 'test-worksheet',
|
|
567
|
+
hiddenActions: [Action.ReportError],
|
|
568
|
+
className: 'test-class',
|
|
569
|
+
searchOptions: {
|
|
570
|
+
searchQuery: 'test query'
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
const TestComponent = () => {
|
|
575
|
+
const { sendMessage } = useSpotterAgent(complexConfig);
|
|
576
|
+
expect(sendMessage).toBeDefined();
|
|
577
|
+
return <div>Test</div>;
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
// Should not throw with complex config
|
|
581
|
+
expect(() => {
|
|
582
|
+
render(<TestComponent />);
|
|
583
|
+
}).not.toThrow();
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
it('Should maintain function identity across re-renders with same config', () => {
|
|
587
|
+
let sendMessage1: any, sendMessage2: any;
|
|
588
|
+
|
|
589
|
+
const TestComponent = ({ forceRender }: { forceRender: number }) => {
|
|
590
|
+
const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
|
|
591
|
+
|
|
592
|
+
if (forceRender === 1) {
|
|
593
|
+
sendMessage1 = sendMessage;
|
|
594
|
+
} else {
|
|
595
|
+
sendMessage2 = sendMessage;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return <div>Test</div>;
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
const { rerender } = render(<TestComponent forceRender={1} />);
|
|
602
|
+
rerender(<TestComponent forceRender={2} />);
|
|
603
|
+
|
|
604
|
+
// Functions should exist
|
|
605
|
+
expect(sendMessage1).toBeDefined();
|
|
606
|
+
expect(sendMessage2).toBeDefined();
|
|
607
|
+
expect(typeof sendMessage1).toBe('function');
|
|
608
|
+
expect(typeof sendMessage2).toBe('function');
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it('Should handle sendMessage calls with null service ref', async () => {
|
|
612
|
+
let capturedSendMessage: any;
|
|
613
|
+
|
|
614
|
+
const TestComponent = () => {
|
|
615
|
+
const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
|
|
616
|
+
capturedSendMessage = sendMessage;
|
|
617
|
+
return <div>Test</div>;
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
const { unmount } = render(<TestComponent />);
|
|
621
|
+
|
|
622
|
+
// Unmount to trigger cleanup
|
|
623
|
+
unmount();
|
|
624
|
+
|
|
625
|
+
// Now call sendMessage after unmount - should return error
|
|
626
|
+
const result = await capturedSendMessage('test query');
|
|
627
|
+
expect(result).toEqual({
|
|
628
|
+
error: expect.any(Error)
|
|
629
|
+
});
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
it('Should test service ref cleanup on config change', () => {
|
|
633
|
+
const TestComponent = ({ worksheetId }: { worksheetId: string }) => {
|
|
634
|
+
const { sendMessage } = useSpotterAgent({ worksheetId });
|
|
635
|
+
expect(sendMessage).toBeDefined();
|
|
636
|
+
return <div>Test</div>;
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
const { rerender } = render(<TestComponent worksheetId="worksheet1" />);
|
|
640
|
+
|
|
641
|
+
// This should trigger the cleanup and create new service
|
|
642
|
+
rerender(<TestComponent worksheetId="worksheet2" />);
|
|
643
|
+
|
|
644
|
+
// Should still work after rerender
|
|
645
|
+
expect(() => {
|
|
646
|
+
rerender(<TestComponent worksheetId="worksheet3" />);
|
|
647
|
+
}).not.toThrow();
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
it('Should test different config variations', () => {
|
|
651
|
+
const configs = [
|
|
652
|
+
{ worksheetId: 'test1' },
|
|
653
|
+
{ worksheetId: 'test2', hiddenActions: [Action.ReportError] },
|
|
654
|
+
{ worksheetId: 'test3', className: 'test-class' },
|
|
655
|
+
{ worksheetId: 'test4', searchOptions: { searchQuery: 'test' } }
|
|
656
|
+
];
|
|
657
|
+
|
|
658
|
+
configs.forEach((config, index) => {
|
|
659
|
+
const TestComponent = () => {
|
|
660
|
+
const { sendMessage } = useSpotterAgent(config);
|
|
661
|
+
expect(sendMessage).toBeDefined();
|
|
662
|
+
return <div>Test {index}</div>;
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
expect(() => {
|
|
666
|
+
const { unmount } = render(<TestComponent />);
|
|
667
|
+
unmount();
|
|
668
|
+
}).not.toThrow();
|
|
669
|
+
});
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
it('Should handle rapid config changes', () => {
|
|
673
|
+
const TestComponent = ({ worksheetId }: { worksheetId: string }) => {
|
|
674
|
+
const { sendMessage } = useSpotterAgent({ worksheetId });
|
|
675
|
+
expect(sendMessage).toBeDefined();
|
|
676
|
+
return <div>Test</div>;
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
const { rerender } = render(<TestComponent worksheetId="worksheet1" />);
|
|
680
|
+
|
|
681
|
+
// Rapid config changes to test cleanup logic
|
|
682
|
+
for (let i = 2; i <= 10; i++) {
|
|
683
|
+
rerender(<TestComponent worksheetId={`worksheet${i}`} />);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Should still work after many changes
|
|
687
|
+
expect(() => {
|
|
688
|
+
rerender(<TestComponent worksheetId="final-worksheet" />);
|
|
689
|
+
}).not.toThrow();
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
it('Should handle sendMessage with different query types', () => {
|
|
693
|
+
let sendMessageFunction: any;
|
|
694
|
+
|
|
695
|
+
const TestComponent = () => {
|
|
696
|
+
const { sendMessage } = useSpotterAgent({ worksheetId: 'test-worksheet' });
|
|
697
|
+
sendMessageFunction = sendMessage;
|
|
698
|
+
return <div>Test</div>;
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
render(<TestComponent />);
|
|
702
|
+
|
|
703
|
+
// Test different query types
|
|
704
|
+
const queries = [
|
|
705
|
+
'simple query',
|
|
706
|
+
'query with numbers 123',
|
|
707
|
+
'query with special chars !@#$%',
|
|
708
|
+
'very long query that might test different code paths in the system when processing',
|
|
709
|
+
'',
|
|
710
|
+
' whitespace ',
|
|
711
|
+
'null',
|
|
712
|
+
'undefined'
|
|
713
|
+
];
|
|
714
|
+
|
|
715
|
+
queries.forEach(query => {
|
|
716
|
+
expect(() => {
|
|
717
|
+
sendMessageFunction(query);
|
|
718
|
+
}).not.toThrow();
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
it('Should handle service ref cleanup when it already exists', () => {
|
|
723
|
+
const TestComponent = ({ worksheetId }: { worksheetId: string }) => {
|
|
724
|
+
const { sendMessage } = useSpotterAgent({ worksheetId });
|
|
725
|
+
expect(sendMessage).toBeDefined();
|
|
726
|
+
return <div>Test</div>;
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
const { rerender } = render(<TestComponent worksheetId="worksheet1" />);
|
|
730
|
+
|
|
731
|
+
// This should trigger the "if (serviceRef.current)" branch in useEffect
|
|
732
|
+
rerender(<TestComponent worksheetId="worksheet1" />);
|
|
733
|
+
rerender(<TestComponent worksheetId="worksheet2" />);
|
|
734
|
+
rerender(<TestComponent worksheetId="worksheet3" />);
|
|
735
|
+
|
|
736
|
+
// Multiple rapid changes should exercise the cleanup logic
|
|
737
|
+
for (let i = 0; i < 5; i++) {
|
|
738
|
+
rerender(<TestComponent worksheetId={`worksheet${i}`} />);
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
it('Should test various config combinations to hit all branches', () => {
|
|
743
|
+
const testConfigs = [
|
|
744
|
+
{ worksheetId: 'test1' },
|
|
745
|
+
{ worksheetId: 'test2', className: 'custom-class' },
|
|
746
|
+
{ worksheetId: 'test3', hiddenActions: [Action.ReportError] },
|
|
747
|
+
{ worksheetId: 'test4', searchOptions: { searchQuery: 'test' } },
|
|
748
|
+
{ worksheetId: 'test5', insertAsSibling: true },
|
|
749
|
+
{ worksheetId: 'test6', insertAsSibling: false },
|
|
750
|
+
];
|
|
751
|
+
|
|
752
|
+
testConfigs.forEach((config, index) => {
|
|
753
|
+
const TestComponent = () => {
|
|
754
|
+
const { sendMessage } = useSpotterAgent(config);
|
|
755
|
+
expect(sendMessage).toBeDefined();
|
|
756
|
+
return <div>Test {index}</div>;
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
const { unmount } = render(<TestComponent />);
|
|
760
|
+
unmount();
|
|
761
|
+
});
|
|
762
|
+
});
|
|
392
763
|
});
|
|
393
764
|
|
|
394
|
-
describe('
|
|
395
|
-
it('
|
|
396
|
-
|
|
765
|
+
describe('Component Props and Functions', () => {
|
|
766
|
+
it('Should have PreRenderedLiveboardEmbed component', () => {
|
|
767
|
+
expect(PreRenderedLiveboardEmbed).toBeDefined();
|
|
768
|
+
expect(typeof PreRenderedLiveboardEmbed).toBe('object');
|
|
769
|
+
});
|
|
397
770
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
preRenderId="test"
|
|
402
|
-
liveboardId="libId"
|
|
403
|
-
/>,
|
|
404
|
-
);
|
|
771
|
+
it('Should have useInit hook', () => {
|
|
772
|
+
expect(typeof useInit).toBe('function');
|
|
773
|
+
});
|
|
405
774
|
|
|
406
|
-
|
|
407
|
-
|
|
775
|
+
it('Should test basic component factory patterns', () => {
|
|
776
|
+
// Test that components can be created without errors
|
|
777
|
+
expect(() => {
|
|
778
|
+
const TestComponent = () => <div>Test</div>;
|
|
779
|
+
render(<TestComponent />);
|
|
780
|
+
}).not.toThrow();
|
|
781
|
+
});
|
|
782
|
+
});
|
|
408
783
|
|
|
409
|
-
|
|
410
|
-
|
|
784
|
+
describe('Hook Coverage', () => {
|
|
785
|
+
it('Should have useInit function available', () => {
|
|
786
|
+
expect(typeof useInit).toBe('function');
|
|
787
|
+
});
|
|
411
788
|
|
|
412
|
-
|
|
413
|
-
const
|
|
414
|
-
|
|
789
|
+
it('Should test useInit hook basic functionality', () => {
|
|
790
|
+
const TestComponent = () => {
|
|
791
|
+
const authEE = useInit({
|
|
792
|
+
thoughtSpotHost: 'localhost',
|
|
793
|
+
authType: AuthType.None
|
|
794
|
+
});
|
|
795
|
+
expect(authEE).toBeDefined();
|
|
796
|
+
expect(authEE.current).toBeDefined();
|
|
797
|
+
return <div>Test</div>;
|
|
798
|
+
};
|
|
415
799
|
|
|
416
|
-
(
|
|
417
|
-
|
|
418
|
-
observe: jest.fn(),
|
|
419
|
-
unobserve: jest.fn(),
|
|
420
|
-
}));
|
|
421
|
-
const { container: libContainer } = render(
|
|
422
|
-
<LiveboardEmbed
|
|
423
|
-
className="embedClass"
|
|
424
|
-
preRenderId="test"
|
|
425
|
-
liveboardId="libId"
|
|
426
|
-
/>,
|
|
427
|
-
);
|
|
800
|
+
render(<TestComponent />);
|
|
801
|
+
});
|
|
428
802
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
803
|
+
it('Should handle useInit with different config changes', () => {
|
|
804
|
+
const TestComponent = ({ host }: { host: string }) => {
|
|
805
|
+
const authEE = useInit({
|
|
806
|
+
thoughtSpotHost: host,
|
|
807
|
+
authType: AuthType.None
|
|
808
|
+
});
|
|
809
|
+
expect(authEE).toBeDefined();
|
|
810
|
+
return <div>Test</div>;
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
const { rerender } = render(<TestComponent host="localhost" />);
|
|
814
|
+
|
|
815
|
+
// Change config to test useDeepCompareEffect
|
|
816
|
+
rerender(<TestComponent host="localhost2" />);
|
|
817
|
+
rerender(<TestComponent host="localhost3" />);
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
it('Should test useInit with complex config objects', () => {
|
|
821
|
+
const TestComponent = () => {
|
|
822
|
+
const authEE = useInit({
|
|
823
|
+
thoughtSpotHost: 'localhost',
|
|
824
|
+
authType: AuthType.None,
|
|
825
|
+
suppressNoCookieAccessAlert: true,
|
|
826
|
+
suppressErrorAlerts: true
|
|
827
|
+
});
|
|
828
|
+
expect(authEE).toBeDefined();
|
|
829
|
+
return <div>Test</div>;
|
|
830
|
+
};
|
|
831
|
+
|
|
832
|
+
render(<TestComponent />);
|
|
432
833
|
});
|
|
433
834
|
});
|
|
434
835
|
});
|