diff --git a/apps/test-site/src/App.tsx b/apps/test-site/src/App.tsx
index b9c4372..780e445 100644
--- a/apps/test-site/src/App.tsx
+++ b/apps/test-site/src/App.tsx
@@ -24,6 +24,7 @@ function App() {
function ChatComponent() {
const isLoading = useChatState((s) => s.conversation.isLoading);
+ const canSendMessage = useChatState((s) => s.conversation.canSendMessage);
const messages = useChatState((s) => s.conversation.messages);
const [input, setInput] = useState("");
const actions = useChatActions();
@@ -57,7 +58,12 @@ function ChatComponent() {
{`${m.source}: ${m.text}`}
))}
{isLoading && loading...
}
-
+
diff --git a/package-lock.json b/package-lock.json
index 58cffb5..3730a5c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18447,7 +18447,7 @@
},
"packages/chat-headless": {
"name": "@yext/chat-headless",
- "version": "0.3.0",
+ "version": "0.3.1",
"license": "BSD-3-Clause",
"dependencies": {
"@reduxjs/toolkit": "^1.9.5",
diff --git a/packages/chat-headless/docs/chat-headless.chatheadless._constructor_.md b/packages/chat-headless/docs/chat-headless.chatheadless._constructor_.md
index 77d2f02..c8c1ae1 100644
--- a/packages/chat-headless/docs/chat-headless.chatheadless._constructor_.md
+++ b/packages/chat-headless/docs/chat-headless.chatheadless._constructor_.md
@@ -9,13 +9,12 @@ Constructs a new instance of the [ChatHeadless](./chat-headless.chatheadless.md)
**Signature:**
```typescript
-constructor(config: ChatConfig, saveToSessionStorage?: boolean);
+constructor(config: HeadlessConfig);
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
-| config | ChatConfig | The configuration for the [ChatHeadless](./chat-headless.chatheadless.md) instance |
-| saveToSessionStorage | boolean | _(Optional)_ Whether to save the instance's [ConversationState](./chat-headless.conversationstate.md) to session storage. Defaults to true. |
+| config | [HeadlessConfig](./chat-headless.headlessconfig.md) | The configuration for the [ChatHeadless](./chat-headless.chatheadless.md) instance |
diff --git a/packages/chat-headless/docs/chat-headless.chatheadless.getnextmessage.md b/packages/chat-headless/docs/chat-headless.chatheadless.getnextmessage.md
index f0f4bd9..7a430e7 100644
--- a/packages/chat-headless/docs/chat-headless.chatheadless.getnextmessage.md
+++ b/packages/chat-headless/docs/chat-headless.chatheadless.getnextmessage.md
@@ -9,7 +9,7 @@ Performs a Chat API request for the next message generated by chat bot using the
**Signature:**
```typescript
-getNextMessage(text?: string, source?: MessageSource): Promise;
+getNextMessage(text?: string, source?: MessageSource): Promise;
```
## Parameters
@@ -21,7 +21,7 @@ getNextMessage(text?: string, source?: MessageSource): Promise;
**Returns:**
-Promise<MessageResponse>
+Promise<MessageResponse \| undefined>
a Promise of a response from the Chat API
diff --git a/packages/chat-headless/docs/chat-headless.chatheadless.md b/packages/chat-headless/docs/chat-headless.chatheadless.md
index cb6e6bc..7bcc37c 100644
--- a/packages/chat-headless/docs/chat-headless.chatheadless.md
+++ b/packages/chat-headless/docs/chat-headless.chatheadless.md
@@ -16,7 +16,7 @@ export declare class ChatHeadless
| Constructor | Modifiers | Description |
| --- | --- | --- |
-| [(constructor)(config, saveToSessionStorage)](./chat-headless.chatheadless._constructor_.md) | | Constructs a new instance of the [ChatHeadless](./chat-headless.chatheadless.md) class. |
+| [(constructor)(config)](./chat-headless.chatheadless._constructor_.md) | | Constructs a new instance of the [ChatHeadless](./chat-headless.chatheadless.md) class. |
## Properties
diff --git a/packages/chat-headless/docs/chat-headless.chatheadless.streamnextmessage.md b/packages/chat-headless/docs/chat-headless.chatheadless.streamnextmessage.md
index 6a9bdb4..99ef110 100644
--- a/packages/chat-headless/docs/chat-headless.chatheadless.streamnextmessage.md
+++ b/packages/chat-headless/docs/chat-headless.chatheadless.streamnextmessage.md
@@ -9,7 +9,7 @@ Performs a Chat Stream API request for the next message generated by chat bot us
**Signature:**
```typescript
-streamNextMessage(text?: string, source?: MessageSource): Promise;
+streamNextMessage(text?: string, source?: MessageSource): Promise;
```
## Parameters
@@ -21,7 +21,7 @@ streamNextMessage(text?: string, source?: MessageSource): Promise
+
+[Home](./index.md) > [@yext/chat-headless](./chat-headless.md) > [ConversationState](./chat-headless.conversationstate.md) > [canSendMessage](./chat-headless.conversationstate.cansendmessage.md)
+
+## ConversationState.canSendMessage property
+
+Whether a new message can be sent to Chat API. This is set to false when a previous message is being processed.
+
+**Signature:**
+
+```typescript
+canSendMessage: boolean;
+```
diff --git a/packages/chat-headless/docs/chat-headless.conversationstate.isloading.md b/packages/chat-headless/docs/chat-headless.conversationstate.isloading.md
index 1eda62d..25cd1da 100644
--- a/packages/chat-headless/docs/chat-headless.conversationstate.isloading.md
+++ b/packages/chat-headless/docs/chat-headless.conversationstate.isloading.md
@@ -4,10 +4,10 @@
## ConversationState.isLoading property
-Whether the next message is currently processing or has finished processing.
+Whether the next message is currently processing or has started responding.
**Signature:**
```typescript
-isLoading?: boolean;
+isLoading: boolean;
```
diff --git a/packages/chat-headless/docs/chat-headless.conversationstate.md b/packages/chat-headless/docs/chat-headless.conversationstate.md
index c1a00a6..790af63 100644
--- a/packages/chat-headless/docs/chat-headless.conversationstate.md
+++ b/packages/chat-headless/docs/chat-headless.conversationstate.md
@@ -16,8 +16,9 @@ export interface ConversationState
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
+| [canSendMessage](./chat-headless.conversationstate.cansendmessage.md) | | boolean | Whether a new message can be sent to Chat API. This is set to false when a previous message is being processed. |
| [conversationId?](./chat-headless.conversationstate.conversationid.md) | | string | _(Optional)_ The id of the current conversation. |
-| [isLoading?](./chat-headless.conversationstate.isloading.md) | | boolean | _(Optional)_ Whether the next message is currently processing or has finished processing. |
+| [isLoading](./chat-headless.conversationstate.isloading.md) | | boolean | Whether the next message is currently processing or has started responding. |
| [messages](./chat-headless.conversationstate.messages.md) | | Message\[\] | The messages in a conversation. |
| [notes?](./chat-headless.conversationstate.notes.md) | | MessageNotes | _(Optional)_ Information relevant to the current state of the conversation, generated and provided by Chat API. |
diff --git a/packages/chat-headless/docs/chat-headless.headlessconfig.md b/packages/chat-headless/docs/chat-headless.headlessconfig.md
new file mode 100644
index 0000000..da5a390
--- /dev/null
+++ b/packages/chat-headless/docs/chat-headless.headlessconfig.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [@yext/chat-headless](./chat-headless.md) > [HeadlessConfig](./chat-headless.headlessconfig.md)
+
+## HeadlessConfig interface
+
+The configuration for a SearchHeadless instance.
+
+**Signature:**
+
+```typescript
+export interface HeadlessConfig extends ChatConfig
+```
+**Extends:** ChatConfig
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [saveToSessionStorage?](./chat-headless.headlessconfig.savetosessionstorage.md) | | boolean | _(Optional)_ Whether to save the instance's [ConversationState](./chat-headless.conversationstate.md) to session storage. Defaults to true. |
+
diff --git a/packages/chat-headless/docs/chat-headless.headlessconfig.savetosessionstorage.md b/packages/chat-headless/docs/chat-headless.headlessconfig.savetosessionstorage.md
new file mode 100644
index 0000000..08cba97
--- /dev/null
+++ b/packages/chat-headless/docs/chat-headless.headlessconfig.savetosessionstorage.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [@yext/chat-headless](./chat-headless.md) > [HeadlessConfig](./chat-headless.headlessconfig.md) > [saveToSessionStorage](./chat-headless.headlessconfig.savetosessionstorage.md)
+
+## HeadlessConfig.saveToSessionStorage property
+
+Whether to save the instance's [ConversationState](./chat-headless.conversationstate.md) to session storage. Defaults to true.
+
+**Signature:**
+
+```typescript
+saveToSessionStorage?: boolean;
+```
diff --git a/packages/chat-headless/docs/chat-headless.md b/packages/chat-headless/docs/chat-headless.md
index e79d723..3b0d023 100644
--- a/packages/chat-headless/docs/chat-headless.md
+++ b/packages/chat-headless/docs/chat-headless.md
@@ -15,6 +15,7 @@
| Interface | Description |
| --- | --- |
| [ConversationState](./chat-headless.conversationstate.md) | Maintains the data for the current conversation. |
+| [HeadlessConfig](./chat-headless.headlessconfig.md) | The configuration for a SearchHeadless instance. |
| [MetaState](./chat-headless.metastate.md) | Maintains the metadata for Chat Headless. |
| [State](./chat-headless.state.md) | The state representing a ChatHeadless instance. |
| [StateListener](./chat-headless.statelistener.md) | Represents a listener for a specific value of type T in the state. |
diff --git a/packages/chat-headless/etc/chat-headless.api.md b/packages/chat-headless/etc/chat-headless.api.md
index 974dd76..b16d23f 100644
--- a/packages/chat-headless/etc/chat-headless.api.md
+++ b/packages/chat-headless/etc/chat-headless.api.md
@@ -25,9 +25,9 @@ export { ChatConfig }
// @public
export class ChatHeadless {
- constructor(config: ChatConfig, saveToSessionStorage?: boolean);
+ constructor(config: HeadlessConfig);
addListener(listener: StateListener): Unsubscribe;
- getNextMessage(text?: string, source?: MessageSource): Promise;
+ getNextMessage(text?: string, source?: MessageSource): Promise;
restartConversation(): void;
setChatLoadingStatus(isLoading: boolean): void;
setContext(context: any): void;
@@ -37,19 +37,25 @@ export class ChatHeadless {
get state(): State;
// @internal
get store(): Store;
- streamNextMessage(text?: string, source?: MessageSource): Promise;
+ streamNextMessage(text?: string, source?: MessageSource): Promise;
}
// @public
export interface ConversationState {
+ canSendMessage: boolean;
conversationId?: string;
- isLoading?: boolean;
+ isLoading: boolean;
messages: Message[];
notes?: MessageNotes;
}
export { EndEvent }
+// @public
+export interface HeadlessConfig extends ChatConfig {
+ saveToSessionStorage?: boolean;
+}
+
export { Message }
export { MessageNotes }
diff --git a/packages/chat-headless/package.json b/packages/chat-headless/package.json
index 0be208d..a72a63c 100644
--- a/packages/chat-headless/package.json
+++ b/packages/chat-headless/package.json
@@ -1,6 +1,6 @@
{
"name": "@yext/chat-headless",
- "version": "0.3.0",
+ "version": "0.3.1",
"description": "A library for powering UI components for Yext Chat integrations",
"main": "./dist/commonjs/index.js",
"module": "./dist/esm/index.js",
diff --git a/packages/chat-headless/src/ChatHeadless.ts b/packages/chat-headless/src/ChatHeadless.ts
index 4aa6dda..c454b3f 100644
--- a/packages/chat-headless/src/ChatHeadless.ts
+++ b/packages/chat-headless/src/ChatHeadless.ts
@@ -1,5 +1,4 @@
import {
- ChatConfig,
ChatCore,
Message,
MessageNotes,
@@ -11,6 +10,7 @@ import { State } from "./models/state";
import { ReduxStateManager } from "./ReduxStateManager";
import {
loadSessionState,
+ setCanSendMessage,
setConversationId,
setIsLoading,
setMessageNotes,
@@ -20,6 +20,7 @@ import {
import { Store, Unsubscribe } from "@reduxjs/toolkit";
import { StateListener } from "./models";
import { setContext } from "./slices/meta";
+import { HeadlessConfig } from "./models/HeadlessConfig";
/**
* Provides the functionality for interacting with a Chat Bot
@@ -37,12 +38,15 @@ export class ChatHeadless {
* @public
*
* @param config - The configuration for the {@link ChatHeadless} instance
- * @param saveToSessionStorage - Whether to save the instance's {@link ConversationState} to session storage. Defaults to true.
*/
- constructor(config: ChatConfig, saveToSessionStorage = true) {
- this.chatCore = new ChatCore(config);
+ constructor(config: HeadlessConfig) {
+ const defaultConfig: Partial = {
+ saveToSessionStorage: true,
+ };
+ const mergedConfig = { ...defaultConfig, ...config };
+ this.chatCore = new ChatCore(mergedConfig);
this.stateManager = new ReduxStateManager();
- if (saveToSessionStorage) {
+ if (mergedConfig.saveToSessionStorage) {
this.setState({
...this.state,
conversation: loadSessionState(),
@@ -149,6 +153,17 @@ export class ChatHeadless {
this.stateManager.dispatch(setConversationId(id));
}
+ /**
+ * Sets {@link ConversationState.canSendMessage} to the specified state
+ *
+ * @internal
+ *
+ * @param canSendMessage - the state to set
+ */
+ private setCanSendMessage(canSendMessage: boolean) {
+ this.stateManager.dispatch(setCanSendMessage(canSendMessage));
+ }
+
/**
* Resets all fields within {@link ConversationState}
*
@@ -157,6 +172,7 @@ export class ChatHeadless {
restartConversation() {
this.setConversationId(undefined);
this.setChatLoadingStatus(false);
+ this.setCanSendMessage(true);
this.setMessageNotes({});
this.setMessages([]);
}
@@ -190,7 +206,7 @@ export class ChatHeadless {
async getNextMessage(
text?: string,
source: MessageSource = MessageSource.USER
- ): Promise {
+ ): Promise {
return this.nextMessageHandler(
async () => {
const { messages, conversationId, notes } = this.state.conversation;
@@ -229,7 +245,7 @@ export class ChatHeadless {
async streamNextMessage(
text?: string,
source: MessageSource = MessageSource.USER
- ): Promise {
+ ): Promise {
return this.nextMessageHandler(
async () => {
let messageResponse: MessageResponse | undefined = undefined;
@@ -245,6 +261,7 @@ export class ChatHeadless {
context: this.state.meta.context,
});
stream.addEventListener(StreamEventName.StartEvent, ({ data }) => {
+ this.setChatLoadingStatus(false);
this.setMessageNotes(data);
});
stream.addEventListener(
@@ -277,8 +294,10 @@ export class ChatHeadless {
/**
* Setup relevant state before hitting Chat API endpoint for next message, such as
- * setting loading status and appending new user's message in conversation state.
- * Also update loading state when the next message is received or an error occurred.
+ * setting loading status, "canSendMessage" status, and appending new user's message
+ * in conversation state.
+ *
+ * @internal
*
* @param nextMessageFn - function to invoke to get next message
* @param text - the text of the next message
@@ -289,7 +308,14 @@ export class ChatHeadless {
nextMessageFn: () => Promise,
text?: string,
source: MessageSource = MessageSource.USER
- ): Promise {
+ ): Promise {
+ if (!this.state.conversation.canSendMessage) {
+ console.warn(
+ "Unable to process new message at the moment. Another message is still being processed."
+ );
+ return;
+ }
+ this.setCanSendMessage(false);
this.setChatLoadingStatus(true);
let messages: Message[] = this.state.conversation.messages;
if (text && text.length > 0) {
@@ -307,9 +333,11 @@ export class ChatHeadless {
try {
messageResponse = await nextMessageFn();
} catch (e) {
+ this.setCanSendMessage(true);
this.setChatLoadingStatus(false);
return Promise.reject(e as Error);
}
+ this.setCanSendMessage(true);
this.setChatLoadingStatus(false);
return messageResponse;
}
diff --git a/packages/chat-headless/src/models/HeadlessConfig.ts b/packages/chat-headless/src/models/HeadlessConfig.ts
new file mode 100644
index 0000000..51289f8
--- /dev/null
+++ b/packages/chat-headless/src/models/HeadlessConfig.ts
@@ -0,0 +1,11 @@
+import { ChatConfig } from "@yext/chat-core";
+
+/**
+ * The configuration for a SearchHeadless instance.
+ *
+ * @public
+ */
+export interface HeadlessConfig extends ChatConfig {
+ /** Whether to save the instance's {@link ConversationState} to session storage. Defaults to true. */
+ saveToSessionStorage?: boolean;
+}
diff --git a/packages/chat-headless/src/models/index.ts b/packages/chat-headless/src/models/index.ts
index 4375a99..1aff6a0 100644
--- a/packages/chat-headless/src/models/index.ts
+++ b/packages/chat-headless/src/models/index.ts
@@ -1,4 +1,5 @@
export { State } from "./state";
-export { ConversationState } from "./slices/conversation";
-export { MetaState } from "./slices/meta";
+export { ConversationState } from "./slices/ConversationState";
+export { MetaState } from "./slices/MetaState";
export { StateListener } from "./utils/StateListeners";
+export { HeadlessConfig } from "./HeadlessConfig";
diff --git a/packages/chat-headless/src/models/slices/conversation.ts b/packages/chat-headless/src/models/slices/ConversationState.ts
similarity index 62%
rename from packages/chat-headless/src/models/slices/conversation.ts
rename to packages/chat-headless/src/models/slices/ConversationState.ts
index 9cb6eee..df5e2dc 100644
--- a/packages/chat-headless/src/models/slices/conversation.ts
+++ b/packages/chat-headless/src/models/slices/ConversationState.ts
@@ -12,6 +12,11 @@ export interface ConversationState {
messages: Message[];
/** Information relevant to the current state of the conversation, generated and provided by Chat API. */
notes?: MessageNotes;
- /** Whether the next message is currently processing or has finished processing. */
- isLoading?: boolean;
+ /** Whether the next message is currently processing or has started responding. */
+ isLoading: boolean;
+ /**
+ * Whether a new message can be sent to Chat API.
+ * This is set to false when a previous message is being processed.
+ */
+ canSendMessage: boolean;
}
diff --git a/packages/chat-headless/src/models/slices/meta.ts b/packages/chat-headless/src/models/slices/MetaState.ts
similarity index 100%
rename from packages/chat-headless/src/models/slices/meta.ts
rename to packages/chat-headless/src/models/slices/MetaState.ts
diff --git a/packages/chat-headless/src/models/state.ts b/packages/chat-headless/src/models/state.ts
index 8168192..69a3baa 100644
--- a/packages/chat-headless/src/models/state.ts
+++ b/packages/chat-headless/src/models/state.ts
@@ -1,5 +1,5 @@
-import { ConversationState } from "./slices/conversation";
-import { MetaState } from "./slices/meta";
+import { ConversationState } from "./slices/ConversationState";
+import { MetaState } from "./slices/MetaState";
/**
* The state representing a ChatHeadless instance.
diff --git a/packages/chat-headless/src/slices/conversation.ts b/packages/chat-headless/src/slices/conversation.ts
index e7bfd9c..6da1030 100644
--- a/packages/chat-headless/src/slices/conversation.ts
+++ b/packages/chat-headless/src/slices/conversation.ts
@@ -1,5 +1,5 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
-import { ConversationState } from "../models/slices/conversation";
+import { ConversationState } from "../models/slices/ConversationState";
import { Message, MessageNotes } from "@yext/chat-core";
export const STATE_SESSION_STORAGE_KEY = "yext_chat_conversation_state";
@@ -7,6 +7,7 @@ export const STATE_SESSION_STORAGE_KEY = "yext_chat_conversation_state";
export const initialState: ConversationState = {
messages: [],
isLoading: false,
+ canSendMessage: true,
};
/**
@@ -55,9 +56,20 @@ export const conversationSlice = createSlice({
) => {
state.isLoading = action.payload;
},
+ setCanSendMessage: (
+ state: ConversationState,
+ action: PayloadAction
+ ) => {
+ state.canSendMessage = action.payload;
+ },
},
});
-export const { setMessages, setMessageNotes, setIsLoading, setConversationId } =
- conversationSlice.actions;
+export const {
+ setMessages,
+ setMessageNotes,
+ setIsLoading,
+ setConversationId,
+ setCanSendMessage,
+} = conversationSlice.actions;
export default conversationSlice.reducer;
diff --git a/packages/chat-headless/src/slices/meta.ts b/packages/chat-headless/src/slices/meta.ts
index 077654b..55a2fa8 100644
--- a/packages/chat-headless/src/slices/meta.ts
+++ b/packages/chat-headless/src/slices/meta.ts
@@ -1,5 +1,5 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
-import { MetaState } from "../models/slices/meta";
+import { MetaState } from "../models/slices/MetaState";
export const initialState: MetaState = {};
diff --git a/packages/chat-headless/tests/chatheadless.chatapi.test.ts b/packages/chat-headless/tests/chatheadless.chatapi.test.ts
index 204e387..1003f5b 100644
--- a/packages/chat-headless/tests/chatheadless.chatapi.test.ts
+++ b/packages/chat-headless/tests/chatheadless.chatapi.test.ts
@@ -51,7 +51,7 @@ describe("Chat API methods work as expected", () => {
async function testAPI(
chatHeadless: ChatHeadless,
- testFn: (text: string) => Promise,
+ testFn: (text: string) => Promise,
coreTestFnSpy: unknown
) {
chatHeadless.setState({
@@ -64,6 +64,7 @@ describe("Chat API methods work as expected", () => {
conversation: {
messages: [expectedUserMessage],
isLoading: true,
+ canSendMessage: false,
},
meta: mockedMetaState,
};
@@ -77,6 +78,7 @@ describe("Chat API methods work as expected", () => {
messages: [expectedUserMessage, expectedResponse.message],
notes: expectedResponse.notes,
isLoading: false,
+ canSendMessage: true,
},
meta: mockedMetaState,
};
@@ -201,7 +203,32 @@ describe("Chat API methods work as expected", () => {
expect(coreStreamNextMessageSpy).toBeCalledTimes(1);
});
- it("updates loading status and throw error when an API request returns an error", async () => {
+ it("logs warning when attempt to send next message to API when it is still processing", async () => {
+ const chatHeadless = new ChatHeadless(config);
+ const coreGetNextMessageSpy = jest
+ .spyOn(ChatCore.prototype, "getNextMessage")
+ .mockResolvedValueOnce(expectedResponse);
+ const consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation();
+ chatHeadless.getNextMessage("message 1");
+ const secondResponse = await chatHeadless.getNextMessage("message 2");
+ expect(consoleWarnSpy).toBeCalledTimes(1);
+ expect(consoleWarnSpy).toBeCalledWith(
+ "Unable to process new message at the moment. Another message is still being processed."
+ );
+ expect(secondResponse).toBeUndefined();
+ expect(coreGetNextMessageSpy).toBeCalledTimes(1);
+ expect(coreGetNextMessageSpy).toBeCalledWith({
+ messages: [
+ {
+ source: MessageSource.USER,
+ text: "message 1",
+ timestamp: expect.any(String),
+ },
+ ],
+ });
+ });
+
+ it("updates state and throw error when an API request returns an error", async () => {
const errorMessage =
"Chat API error: FATAL_ERROR: Invalid API Key. (code: 1)";
const chatHeadless = new ChatHeadless(config);
@@ -215,14 +242,16 @@ describe("Chat API methods work as expected", () => {
} catch (e) {
// eslint-disable-next-line jest/no-conditional-expect
expect(e).toEqual(errorMessage);
- // eslint-disable-next-line jest/no-conditional-expect
- expect(chatHeadless.state).toEqual({
+ const expectedState: State = {
conversation: {
messages: [expectedUserMessage],
isLoading: false,
+ canSendMessage: true,
},
meta: {},
- });
+ };
+ // eslint-disable-next-line jest/no-conditional-expect
+ expect(chatHeadless.state).toEqual(expectedState);
}
expect(coreGetNextMessageSpy).toBeCalledTimes(1);
});
diff --git a/packages/chat-headless/tests/chatheadless.test.ts b/packages/chat-headless/tests/chatheadless.test.ts
index d020827..ddda6f5 100644
--- a/packages/chat-headless/tests/chatheadless.test.ts
+++ b/packages/chat-headless/tests/chatheadless.test.ts
@@ -1,13 +1,13 @@
import {
ChatHeadless,
ConversationState,
+ HeadlessConfig,
Message,
MessageNotes,
MessageSource,
MetaState,
State,
} from "../src";
-import { ChatConfig } from "@yext/chat-core";
import { ReduxStateManager } from "../src/ReduxStateManager";
import {
initialState,
@@ -16,7 +16,7 @@ import {
jest.mock("@yext/chat-core");
-const config: ChatConfig = {
+const config: HeadlessConfig = {
botId: "MY_BOT",
apiKey: "MY_API_KEY",
};
@@ -51,6 +51,7 @@ describe("setters work as expected", () => {
currentGoal: "NEW_GOAL",
},
isLoading: true,
+ canSendMessage: false,
},
meta: mockedMetaState,
};
@@ -209,12 +210,13 @@ it("restartConversation works as expected", () => {
currentGoal: "GOAL",
},
isLoading: true,
+ canSendMessage: false,
},
meta: mockedMetaState,
});
const stateDispatchSpy = jest.spyOn(ReduxStateManager.prototype, "dispatch");
chatHeadless.restartConversation();
- expect(stateDispatchSpy).toBeCalledTimes(4);
+ expect(stateDispatchSpy).toBeCalledTimes(5);
expect(stateDispatchSpy).toHaveBeenCalledWith({
type: "conversation/setConversationId",
payload: undefined,
@@ -231,12 +233,17 @@ it("restartConversation works as expected", () => {
type: "conversation/setMessages",
payload: [],
});
+ expect(stateDispatchSpy).toHaveBeenCalledWith({
+ type: "conversation/setCanSendMessage",
+ payload: true,
+ });
const expectedState: State = {
conversation: {
messages: [],
notes: {},
isLoading: false,
+ canSendMessage: true,
},
meta: mockedMetaState,
};
@@ -257,6 +264,7 @@ describe("loadSessionState works as expected", () => {
currentGoal: "GOAL",
},
isLoading: true,
+ canSendMessage: true,
};
it("loads valid state from session storage", () => {
sessionStorage.setItem(
@@ -275,7 +283,10 @@ describe("loadSessionState works as expected", () => {
STATE_SESSION_STORAGE_KEY,
JSON.stringify(expectedState)
);
- const chatHeadless = new ChatHeadless(config, false);
+ const chatHeadless = new ChatHeadless({
+ ...config,
+ saveToSessionStorage: false,
+ });
expect(chatHeadless.state).toEqual({
conversation: initialState,
meta: {},