diff --git a/src/data/api.js b/src/data/api.js index 2ebfdb68..0f90283f 100644 --- a/src/data/api.js +++ b/src/data/api.js @@ -23,15 +23,8 @@ async function fetchChatResponse(courseId, messageList, unitId, customQueryParam return data; } -async function fetchLearningAssistantEnabled(courseId) { - const url = new URL(`${getConfig().CHAT_RESPONSE_URL}/${courseId}/enabled`); - - const { data } = await getAuthenticatedHttpClient().get(url.href); - return data; -} - -async function fetchLearningAssistantMessageHistory(courseId) { - const url = new URL(`${getConfig().CHAT_RESPONSE_URL}/${courseId}/history`); +async function fetchLearningAssistantChatSummary(courseId) { + const url = new URL(`${getConfig().CHAT_RESPONSE_URL}/${courseId}/chat-summary`); const { data } = await getAuthenticatedHttpClient().get(url.href); return data; @@ -39,6 +32,5 @@ async function fetchLearningAssistantMessageHistory(courseId) { export { fetchChatResponse, - fetchLearningAssistantEnabled, - fetchLearningAssistantMessageHistory, + fetchLearningAssistantChatSummary, }; diff --git a/src/data/api.test.js b/src/data/api.test.js index 7b747a5a..d685150d 100644 --- a/src/data/api.test.js +++ b/src/data/api.test.js @@ -1,7 +1,7 @@ /* eslint-disable no-import-assign */ import * as auth from '@edx/frontend-platform/auth'; -import { fetchLearningAssistantMessageHistory } from './api'; +import { fetchLearningAssistantChatSummary } from './api'; jest.mock('@edx/frontend-platform/auth'); @@ -16,38 +16,45 @@ describe('API', () => { jest.restoreAllMocks(); }); - describe('fetchLearningAssistantMessageHistory()', () => { - const fakeCourseId = 'course-v1:edx+test+23'; - const apiPayload = [ - { - role: 'user', - content: 'Marco', - timestamp: '2024-11-04T19:05:07.403363Z', + describe('fetchLearningAssistantChatSummary()', () => { + const courseId = 'course-v1:edx+test+23'; + const apiPayload = { + enabled: true, + message_history: [ + { + role: 'user', + content: 'Marco', + timestamp: '2024-11-04T19:05:07.403363Z', + }, + { + role: 'assistant', + content: 'Polo', + timestamp: '2024-11-04T19:05:21.357636Z', + }, + ], + audit_trial: { + start_date: '2024-12-02T14:59:16.148236Z', + expiration_date: '9999-12-16T14:59:16.148236Z', }, - { - role: 'assistant', - content: 'Polo', - timestamp: '2024-11-04T19:05:21.357636Z', - }, - ]; + }; - const fakeGet = jest.fn(async () => ({ + const mockGet = jest.fn(async () => ({ data: apiPayload, - catch: () => {}, + catch: () => { }, })); beforeEach(() => { auth.getAuthenticatedHttpClient = jest.fn(() => ({ - get: fakeGet, + get: mockGet, })); }); it('should call the endpoint and process the results', async () => { - const response = await fetchLearningAssistantMessageHistory(fakeCourseId); + const response = await fetchLearningAssistantChatSummary(courseId); expect(response).toEqual(apiPayload); - expect(fakeGet).toHaveBeenCalledTimes(1); - expect(fakeGet).toHaveBeenCalledWith(`${CHAT_RESPONSE_URL}/${fakeCourseId}/history`); + expect(mockGet).toHaveBeenCalledTimes(1); + expect(mockGet).toHaveBeenCalledWith(`${CHAT_RESPONSE_URL}/${courseId}/chat-summary`); }); }); }); diff --git a/src/data/slice.js b/src/data/slice.js index ffc981af..91982422 100644 --- a/src/data/slice.js +++ b/src/data/slice.js @@ -11,6 +11,7 @@ export const initialState = { disclosureAcknowledged: false, sidebarIsOpen: false, isEnabled: false, + auditTrial: {}, }; export const learningAssistantSlice = createSlice({ @@ -44,6 +45,9 @@ export const learningAssistantSlice = createSlice({ setIsEnabled: (state, { payload }) => { state.isEnabled = payload; }, + setAuditTrial: (state, { payload }) => { + state.auditTrial = payload; + }, }, }); @@ -57,6 +61,7 @@ export const { setDisclosureAcknowledged, setSidebarIsOpen, setIsEnabled, + setAuditTrial, } = learningAssistantSlice.actions; export const { diff --git a/src/data/thunks.js b/src/data/thunks.js index 13c64e51..fb84955c 100644 --- a/src/data/thunks.js +++ b/src/data/thunks.js @@ -2,7 +2,10 @@ import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import trackChatBotMessageOptimizely from '../utils/optimizelyExperiment'; -import { fetchChatResponse, fetchLearningAssistantMessageHistory, fetchLearningAssistantEnabled } from './api'; +import { + fetchChatResponse, + fetchLearningAssistantChatSummary, +} from './api'; import { setCurrentMessage, clearCurrentMessage, @@ -13,6 +16,7 @@ import { setDisclosureAcknowledged, setSidebarIsOpen, setIsEnabled, + setAuditTrial, } from './slice'; import { OPTIMIZELY_PROMPT_EXPERIMENT_KEY } from './optimizely'; @@ -73,33 +77,6 @@ export function getChatResponse(courseId, unitId, promptExperimentVariationKey = }; } -export function getLearningAssistantMessageHistory(courseId) { - return async (dispatch) => { - dispatch(setApiIsLoading(true)); - - try { - const rawMessageList = await fetchLearningAssistantMessageHistory(courseId); - - if (rawMessageList.length) { - const messageList = rawMessageList - .map(({ timestamp, ...msg }) => ({ - ...msg, - timestamp: new Date(timestamp), // Parse ISO time to Date() - })); - - dispatch(setMessageList({ messageList })); - - // If it has chat history, then we assume the user already aknowledged. - dispatch(setDisclosureAcknowledged(true)); - } - } catch (e) { - // If fetching the messages fail, we just won't show it. - } - - dispatch(setApiIsLoading(false)); - }; -} - export function updateCurrentMessage(content) { return (dispatch) => { dispatch(setCurrentMessage({ currentMessage: content })); @@ -124,13 +101,43 @@ export function updateSidebarIsOpen(isOpen) { }; } -export function getIsEnabled(courseId) { +export function getLearningAssistantChatSummary(courseId) { return async (dispatch) => { + dispatch(setApiIsLoading(true)); + try { - const data = await fetchLearningAssistantEnabled(courseId); + const data = await fetchLearningAssistantChatSummary(courseId); + + // Enabled dispatch(setIsEnabled(data.enabled)); + + // Message History + const rawMessageList = data.message_history; + + // If returned message history data is not empty + if (rawMessageList.length) { + const messageList = rawMessageList + .map(({ timestamp, ...msg }) => ({ + ...msg, + timestamp: new Date(timestamp).toString(), // Parse ISO time to Date() + })); + + dispatch(setMessageList({ messageList })); + + // If it has chat history, then we assume the user already aknowledged. + dispatch(setDisclosureAcknowledged(true)); + } + + // Audit Trial + const auditTrial = data.audit_trial; + + // If returned audit trial data is not empty + if (Object.keys(auditTrial).length !== 0) { + dispatch(setAuditTrial(auditTrial)); + } } catch (error) { dispatch(setApiError()); } + dispatch(setApiIsLoading(false)); }; } diff --git a/src/data/thunks.test.js b/src/data/thunks.test.js index 2a5039aa..439b9e49 100644 --- a/src/data/thunks.test.js +++ b/src/data/thunks.test.js @@ -1,6 +1,6 @@ -import { fetchLearningAssistantMessageHistory } from './api'; +import { fetchLearningAssistantChatSummary } from './api'; -import { getLearningAssistantMessageHistory } from './thunks'; +import { getLearningAssistantChatSummary } from './thunks'; jest.mock('./api'); @@ -9,118 +9,201 @@ describe('Thunks unit tests', () => { afterEach(() => jest.resetAllMocks()); - describe('getLearningAssistantMessageHistory()', () => { - const fakeCourseId = 'course-v1:edx+test+23'; - - describe('when returning results', () => { - const apiResponse = [ - { - role: 'user', - content: 'Marco', - timestamp: '2024-11-04T19:05:07.403363Z', - }, - { - role: 'assistant', - content: 'Polo', - timestamp: '2024-11-04T19:05:21.357636Z', + describe('getLearningAssistantChatSummary()', () => { + const courseId = 'course-v1:edx+test+23'; + + it('with message_history and audit_trial data returned, call all expected dispatches', async () => { + const apiResponse = { + enabled: true, + message_history: [ + { + role: 'user', + content: 'Marco', + timestamp: '2024-11-04T19:05:07.403363Z', + }, + { + role: 'assistant', + content: 'Polo', + timestamp: '2024-11-04T19:05:21.357636Z', + }, + ], + audit_trial: { + start_date: '2024-12-02T14:59:16.148236Z', + expiration_date: '9999-12-16T14:59:16.148236Z', }, - ]; + }; + + fetchLearningAssistantChatSummary.mockResolvedValue(apiResponse); - beforeEach(() => { - fetchLearningAssistantMessageHistory.mockResolvedValue(apiResponse); + await getLearningAssistantChatSummary(courseId)(dispatch); + + expect(dispatch).toHaveBeenNthCalledWith(1, { + type: 'learning-assistant/setApiIsLoading', + payload: true, }); - it('should set the loading state, fetch, parse and set the messages and remove the loading state', async () => { - await getLearningAssistantMessageHistory(fakeCourseId)(dispatch); + expect(fetchLearningAssistantChatSummary).toHaveBeenCalledWith(courseId); - expect(dispatch).toHaveBeenNthCalledWith(1, { - type: 'learning-assistant/setApiIsLoading', - payload: true, - }); + expect(dispatch).toHaveBeenNthCalledWith(2, { + type: 'learning-assistant/setIsEnabled', + payload: true, + }); - expect(fetchLearningAssistantMessageHistory).toHaveBeenCalledWith(fakeCourseId); + expect(dispatch).toHaveBeenNthCalledWith(3, { + type: 'learning-assistant/setMessageList', + payload: { + messageList: apiResponse.message_history.map(({ timestamp, ...msg }) => ({ + ...msg, + timestamp: new Date(timestamp).toString(), // Parse ISO time to Date() + })), + }, + }); - expect(dispatch).toHaveBeenNthCalledWith(2, { - type: 'learning-assistant/setMessageList', - payload: { - messageList: apiResponse.map(({ timestamp, ...msg }) => ({ - ...msg, - timestamp: new Date(timestamp), // Parse ISO time to Date() - })), - }, - }); + expect(dispatch).toHaveBeenNthCalledWith(4, { + type: 'learning-assistant/setDisclosureAcknowledged', + payload: true, + }); - expect(dispatch).toHaveBeenNthCalledWith(3, { - type: 'learning-assistant/setDisclosureAcknowledged', - payload: true, - }); + expect(dispatch).toHaveBeenNthCalledWith(5, { + type: 'learning-assistant/setAuditTrial', + payload: { + start_date: '2024-12-02T14:59:16.148236Z', + expiration_date: '9999-12-16T14:59:16.148236Z', + }, + }); - expect(dispatch).toHaveBeenNthCalledWith(4, { - type: 'learning-assistant/setApiIsLoading', - payload: false, - }); + expect(dispatch).toHaveBeenNthCalledWith(6, { + type: 'learning-assistant/setApiIsLoading', + payload: false, }); }); - describe('when returning no messages', () => { - const apiResponse = []; + it('with no message_history data returned, do not call message history related dispatches', async () => { + const apiResponse = { + enabled: true, + message_history: [], + audit_trial: { + start_date: '2024-12-02T14:59:16.148236Z', + expiration_date: '9999-12-16T14:59:16.148236Z', + }, + }; + + fetchLearningAssistantChatSummary.mockResolvedValue(apiResponse); - beforeEach(() => { - fetchLearningAssistantMessageHistory.mockResolvedValue(apiResponse); + await getLearningAssistantChatSummary(courseId)(dispatch); + + expect(dispatch).toHaveBeenNthCalledWith(1, { + type: 'learning-assistant/setApiIsLoading', + payload: true, }); - it('should only set and remove the loading state', async () => { - await getLearningAssistantMessageHistory(fakeCourseId)(dispatch); + expect(fetchLearningAssistantChatSummary).toHaveBeenCalledWith(courseId); - expect(dispatch).toHaveBeenNthCalledWith(1, { - type: 'learning-assistant/setApiIsLoading', - payload: true, - }); + expect(dispatch).toHaveBeenNthCalledWith(2, { + type: 'learning-assistant/setIsEnabled', + payload: true, + }); - expect(fetchLearningAssistantMessageHistory).toHaveBeenCalledWith(fakeCourseId); + expect(dispatch).not.toHaveBeenCalledWith( + expect.objectContaining({ type: 'learning-assistant/setMessageList' }), + ); - expect(dispatch).not.toHaveBeenCalledWith( - expect.objectContaining({ type: 'learning-assistant/setMessageList' }), - ); + expect(dispatch).not.toHaveBeenCalledWith( + expect.objectContaining({ type: 'learning-assistant/setDisclosureAcknowledged' }), + ); - expect(dispatch).not.toHaveBeenCalledWith( - expect.objectContaining({ type: 'learning-assistant/setDisclosureAcknowledged' }), - ); + expect(dispatch).toHaveBeenNthCalledWith(3, { + type: 'learning-assistant/setAuditTrial', + payload: { + start_date: '2024-12-02T14:59:16.148236Z', + expiration_date: '9999-12-16T14:59:16.148236Z', + }, + }); - expect(dispatch).toHaveBeenNthCalledWith(2, { - type: 'learning-assistant/setApiIsLoading', - payload: false, - }); + expect(dispatch).toHaveBeenNthCalledWith(4, { + type: 'learning-assistant/setApiIsLoading', + payload: false, }); }); - describe('when throwing on fetching', () => { - beforeEach(() => { - fetchLearningAssistantMessageHistory.mockRejectedValue('Whoopsie!'); + it('with no audit_trial data returned, do not call audit trial dispatch', async () => { + const apiResponse = { + enabled: true, + message_history: [ + { + role: 'user', + content: 'Marco', + timestamp: '2024-11-04T19:05:07.403363Z', + }, + { + role: 'assistant', + content: 'Polo', + timestamp: '2024-11-04T19:05:21.357636Z', + }, + ], + audit_trial: {}, + }; + + fetchLearningAssistantChatSummary.mockResolvedValue(apiResponse); + + await getLearningAssistantChatSummary(courseId)(dispatch); + + expect(dispatch).toHaveBeenNthCalledWith(1, { + type: 'learning-assistant/setApiIsLoading', + payload: true, + }); + + expect(fetchLearningAssistantChatSummary).toHaveBeenCalledWith(courseId); + + expect(dispatch).toHaveBeenNthCalledWith(2, { + type: 'learning-assistant/setIsEnabled', + payload: true, + }); + + expect(dispatch).toHaveBeenNthCalledWith(3, { + type: 'learning-assistant/setMessageList', + payload: { + messageList: apiResponse.message_history.map(({ timestamp, ...msg }) => ({ + ...msg, + timestamp: new Date(timestamp).toString(), // Parse ISO time to Date() + })), + }, }); - it('should only set and remove the loading state', async () => { - await getLearningAssistantMessageHistory(fakeCourseId)(dispatch); + expect(dispatch).toHaveBeenNthCalledWith(4, { + type: 'learning-assistant/setDisclosureAcknowledged', + payload: true, + }); - expect(dispatch).toHaveBeenNthCalledWith(1, { - type: 'learning-assistant/setApiIsLoading', - payload: true, - }); + expect(dispatch).toHaveBeenNthCalledWith(5, { + type: 'learning-assistant/setApiIsLoading', + payload: false, + }); + }); + + it('when throwing on fetching, should set the loading state and throw error', async () => { + fetchLearningAssistantChatSummary.mockRejectedValue('Whoopsie!'); + + await getLearningAssistantChatSummary(courseId)(dispatch); + + expect(dispatch).toHaveBeenNthCalledWith(1, { + type: 'learning-assistant/setApiIsLoading', + payload: true, + }); - expect(fetchLearningAssistantMessageHistory).toHaveBeenCalledWith(fakeCourseId); + expect(fetchLearningAssistantChatSummary).toHaveBeenCalledWith(courseId); - expect(dispatch).not.toHaveBeenCalledWith( - expect.objectContaining({ type: 'learning-assistant/setMessageList' }), - ); + expect(dispatch).not.toHaveBeenCalledWith( + expect.objectContaining({ type: 'learning-assistant/setMessageList' }), + ); - expect(dispatch).not.toHaveBeenCalledWith( - expect.objectContaining({ type: 'learning-assistant/setDisclosureAcknowledged' }), - ); + expect(dispatch).not.toHaveBeenCalledWith( + expect.objectContaining({ type: 'learning-assistant/setDisclosureAcknowledged' }), + ); - expect(dispatch).toHaveBeenNthCalledWith(2, { - type: 'learning-assistant/setApiIsLoading', - payload: false, - }); + expect(dispatch).toHaveBeenNthCalledWith(2, { + type: 'learning-assistant/setApiError', + // payload: false, }); }); }); diff --git a/src/hooks/index.js b/src/hooks/index.js deleted file mode 100644 index bfa4b76e..00000000 --- a/src/hooks/index.js +++ /dev/null @@ -1,2 +0,0 @@ -/* eslint-disable import/prefer-default-export */ -export { useMessageHistory } from './message-history'; diff --git a/src/hooks/message-history.js b/src/hooks/message-history.js deleted file mode 100644 index c27d5f46..00000000 --- a/src/hooks/message-history.js +++ /dev/null @@ -1,15 +0,0 @@ -/* eslint-disable import/prefer-default-export */ -import { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { getLearningAssistantMessageHistory } from '../data/thunks'; - -export const useMessageHistory = (courseId) => { - const dispatch = useDispatch(); - const { isEnabled } = useSelector(state => state.learningAssistant); - - useEffect(() => { - if (!courseId || !isEnabled) { return; } - - dispatch(getLearningAssistantMessageHistory(courseId)); - }, [dispatch, isEnabled, courseId]); -}; diff --git a/src/hooks/message-history.test.js b/src/hooks/message-history.test.js deleted file mode 100644 index b3c86b7f..00000000 --- a/src/hooks/message-history.test.js +++ /dev/null @@ -1,57 +0,0 @@ -import { renderHook } from '@testing-library/react-hooks'; - -import { useSelector } from 'react-redux'; -import { useMessageHistory } from './message-history'; -import { getLearningAssistantMessageHistory } from '../data/thunks'; - -const mockDispatch = jest.fn(); -jest.mock('react-redux', () => ({ - ...jest.requireActual('react-redux'), - useSelector: jest.fn(), - useDispatch: () => mockDispatch, -})); - -const getLearningAssistantMessageHistorySignature = { getLearningAssistantMessageHistory: 'getLearningAssistantMessageHistory' }; -jest.mock('../data/thunks', () => ({ - getLearningAssistantMessageHistory: jest.fn().mockReturnValue(getLearningAssistantMessageHistorySignature), -})); - -describe('Learning Assistant Message History Hooks', () => { - afterEach(() => { - jest.resetAllMocks(); - }); - - describe('useMessageHistory()', () => { - let hook; - const fakeCourseId = 'course-v1:edx+test+23'; - - const renderTestHook = (courseId, isEnabled) => { - const mockedStoreState = { learningAssistant: { isEnabled } }; - useSelector.mockImplementation(selector => selector(mockedStoreState)); - hook = renderHook(() => useMessageHistory(courseId)); - return hook; - }; - - it('should dispatch getLearningAssistantMessageHistory() with the chat history', () => { - renderTestHook(fakeCourseId, true); - - expect(mockDispatch).toHaveBeenCalledTimes(1); - expect(mockDispatch).toHaveBeenCalledWith(getLearningAssistantMessageHistorySignature); - expect(getLearningAssistantMessageHistory).toHaveBeenCalledWith(fakeCourseId); - }); - - it('should NOT dispatch getLearningAssistantMessageHistory() when disabled', () => { - renderTestHook(fakeCourseId, false); - - expect(mockDispatch).not.toHaveBeenCalled(); - expect(getLearningAssistantMessageHistory).not.toHaveBeenCalled(); - }); - - it('should NOT dispatch getLearningAssistantMessageHistory() with no courseId', () => { - renderTestHook(null, true); - - expect(mockDispatch).not.toHaveBeenCalled(); - expect(getLearningAssistantMessageHistory).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/src/utils/utils.test.jsx b/src/utils/utils.test.jsx index 3edeef44..870c0a04 100644 --- a/src/utils/utils.test.jsx +++ b/src/utils/utils.test.jsx @@ -48,7 +48,9 @@ const createRandomResponseForTesting = () => { message.push(words[Math.floor(Math.random() * words.length)]); } - return { role: 'assistant', content: message.join(' ') }; + const timestamp = new Date(); + + return [{ role: 'assistant', content: message.join(' '), timestamp }]; }; export { renderWithProviders as render, createRandomResponseForTesting }; diff --git a/src/widgets/Xpert.jsx b/src/widgets/Xpert.jsx index 88ee461d..75591e7f 100644 --- a/src/widgets/Xpert.jsx +++ b/src/widgets/Xpert.jsx @@ -2,19 +2,18 @@ import PropTypes from 'prop-types'; import { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { updateSidebarIsOpen, getIsEnabled } from '../data/thunks'; +import { updateSidebarIsOpen, getLearningAssistantChatSummary } from '../data/thunks'; import ToggleXpert from '../components/ToggleXpertButton'; import Sidebar from '../components/Sidebar'; import { ExperimentsProvider } from '../experiments'; -import { useMessageHistory } from '../hooks'; const Xpert = ({ courseId, contentToolsEnabled, unitId }) => { const dispatch = useDispatch(); - useMessageHistory(courseId); const { isEnabled, sidebarIsOpen, + auditTrial, } = useSelector(state => state.learningAssistant); const setSidebarIsOpen = (isOpen) => { @@ -22,9 +21,19 @@ const Xpert = ({ courseId, contentToolsEnabled, unitId }) => { }; useEffect(() => { - dispatch(getIsEnabled(courseId)); + dispatch(getLearningAssistantChatSummary(courseId)); }, [dispatch, courseId]); + // NOTE: This value can be used later on if/when we pass the enrollment mode to this component + const isAuditTrialNotExpired = () => { // eslint-disable-line no-unused-vars + const auditTrialExpirationDate = new Date(auditTrial.expirationDate); + + if ((Date.now() - auditTrialExpirationDate) > 0) { + return true; + } + return false; + }; + return isEnabled ? ( <> diff --git a/src/widgets/Xpert.test.jsx b/src/widgets/Xpert.test.jsx index 532669f3..24819f66 100644 --- a/src/widgets/Xpert.test.jsx +++ b/src/widgets/Xpert.test.jsx @@ -9,7 +9,6 @@ import Xpert from './Xpert'; import * as surveyMonkey from '../utils/surveyMonkey'; import { render, createRandomResponseForTesting } from '../utils/utils.test'; import { usePromptExperimentDecision } from '../experiments'; -import { useMessageHistory } from '../hooks'; jest.mock('@edx/frontend-platform/analytics'); jest.mock('@edx/frontend-platform/auth', () => ({ @@ -21,8 +20,6 @@ jest.mock('../experiments', () => ({ usePromptExperimentDecision: jest.fn(), })); -jest.mock('../hooks'); - const initialState = { learningAssistant: { currentMessage: '', @@ -48,8 +45,15 @@ const assertSidebarElementsNotInDOM = () => { beforeEach(() => { const responseMessage = createRandomResponseForTesting(); - jest.spyOn(api, 'fetchChatResponse').mockResolvedValue(responseMessage); - jest.spyOn(api, 'fetchLearningAssistantEnabled').mockResolvedValue({ enabled: true }); + jest.spyOn(api, 'fetchLearningAssistantChatSummary').mockResolvedValue({ + enabled: true, + message_history: responseMessage, + audit_trial: { + start_date: '2024-12-02T14:59:16.148236Z', + expiration_date: '9999-12-16T14:59:16.148236Z', + }, + }); + usePromptExperimentDecision.mockReturnValue([]); window.localStorage.clear(); @@ -59,7 +63,7 @@ beforeEach(() => { }); test('doesn\'t load if not enabled', async () => { - jest.spyOn(api, 'fetchLearningAssistantEnabled').mockResolvedValue({ enabled: false }); + jest.spyOn(api, 'fetchLearningAssistantChatSummary').mockResolvedValue({ enabled: false }); render(, { preloadedState: initialState }); @@ -78,11 +82,6 @@ test('initial load displays correct elements', async () => { // assert that UI elements in the sidebar are not in the DOM assertSidebarElementsNotInDOM(); }); -test('calls useMessageHistory() hook', () => { - render(, { preloadedState: initialState }); - - expect(useMessageHistory).toHaveBeenCalledWith(courseId); -}); test('clicking the call to action dismiss button removes the message', async () => { const user = userEvent.setup(); render(, { preloadedState: initialState }); @@ -115,7 +114,6 @@ test('clicking the call to action opens the sidebar', async () => { expect(screen.getByRole('button', { name: 'submit' })).toBeVisible(); expect(screen.getByTestId('close-button')).toBeVisible(); - // assert that text input has focus expect(screen.getByRole('textbox')).toHaveFocus(); }); test('clicking the toggle button opens the sidebar', async () => { @@ -169,7 +167,7 @@ test('response text appears as message in the sidebar', async () => { // re-mock the fetchChatResponse API function so that we can assert that the // responseMessage appears in the DOM - const responseMessage = createRandomResponseForTesting(); + const responseMessage = createRandomResponseForTesting()[0]; jest.spyOn(api, 'fetchChatResponse').mockResolvedValue(responseMessage); render(, { preloadedState: initialState }); @@ -318,23 +316,22 @@ test('popup modal should close and display CTA', async () => { expect(screen.queryByTestId('action-message')).toBeVisible(); }); test('survey monkey survey should appear after closing sidebar', async () => { + jest.spyOn(api, 'fetchLearningAssistantChatSummary').mockResolvedValue({ + enabled: true, + message_history: [ + // A length >= 2 is required to load the survey + { role: 'user', content: 'hi', timestamp: new Date().toString() }, + { role: 'user', content: 'hi', timestamp: new Date().toString() }, + ], + audit_trial: { + start_date: '2024-12-02T14:59:16.148236Z', + expiration_date: '9999-12-16T14:59:16.148236Z', + }, + }); const survey = jest.spyOn(surveyMonkey, 'default').mockReturnValueOnce(1); const user = userEvent.setup(); - const surveyState = { - learningAssistant: { - currentMessage: '', - messageList: [ - { role: 'user', content: 'hi', timestamp: new Date() }, - { role: 'user', content: 'hi', timestamp: new Date() + 1 }, - ], - apiIsLoading: false, - apiError: false, - disclosureAcknowledged: true, - sidebarIsOpen: false, - }, - }; - render(, { preloadedState: surveyState }); + render(); // wait for button to appear await screen.findByTestId('toggle-button'); @@ -345,6 +342,6 @@ test('survey monkey survey should appear after closing sidebar', async () => { await user.click(screen.queryByTestId('close-button')); // assert mock called - expect(survey).toBeCalledTimes(1); + expect(survey).toHaveBeenCalledTimes(1); survey.mockRestore(); });