Skip to content

Commit

Permalink
chore: Added coverage for experiment on Sidebar
Browse files Browse the repository at this point in the history
  • Loading branch information
rijuma committed Jul 8, 2024
1 parent 58691b9 commit bd16de8
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 18 deletions.
8 changes: 5 additions & 3 deletions src/components/Sidebar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const Sidebar = ({
messageList,
experiments,
} = useSelector(state => state.learningAssistant);
const { variationKey } = experiments ? experiments[PROMPT_EXPERIMENT_FLAG] : {};
const { variationKey } = experiments?.[PROMPT_EXPERIMENT_FLAG] || {};
const chatboxContainerRef = useRef(null);
const dispatch = useDispatch();

Expand Down Expand Up @@ -95,7 +95,7 @@ const Sidebar = ({
);

const getSidebar = () => (
<div className="h-100 d-flex flex-column justify-content-stretch">
<div className="h-100 d-flex flex-column justify-content-stretch" data-testid="sidebar-xpert">
<div className="d-flex flex-column align-items-center p-3">
<h1 className="font-weight-bold mb-3">
Hi, I&apos;m Xpert!
Expand Down Expand Up @@ -137,16 +137,18 @@ const Sidebar = ({
isOpen && (
<div
className="sidebar position-fixed"
data-testid="sidebar"
>
<IconButton
className="chat-close position-absolute m-2 border-0"
src={Close}
iconAs={Icon}
data-testid="close-button"
onClick={handleClick}
alt="close"
aria-label="close"
variant="primary"
invertColors={!disclosureAcknowledged}
data-testid="close-button"
/>
{disclosureAcknowledged ? (getSidebar()) : (<Disclosure>{getMessageForm()}</Disclosure>)}
</div>
Expand Down
115 changes: 115 additions & 0 deletions src/components/Sidebar/index.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import React from 'react';
import { screen } from '@testing-library/react';
import { render, act } from '../../utils/utils.test';
import { initialState } from '../../data/slice';
import { PROMPT_EXPERIMENT_FLAG, PROMPT_EXPERIMENT_KEY } from '../../constants/experiments';
import { showControlSurvey, showVariationSurvey } from '../../utils/surveyMonkey';

import Sidebar from '.';

jest.mock('../../utils/surveyMonkey', () => ({
showControlSurvey: jest.fn(),
showVariationSurvey: jest.fn(),
}));

const defaultProps = {
courseId: 'some-course-id',
isOpen: true,
setIsOpen: jest.fn(),
unitId: 'some-unit-id',
};

const renderSidebar = async (props = {}, sliceState = {}) => {
const componentProps = {
...defaultProps,
...props,
};

const initState = {
preloadedState: {
learningAssistant: {
...initialState,
...sliceState,
},
},
};
return act(async () => render(
<Sidebar {...componentProps} />,
initState,
));
};

describe('<Sidebar />', () => {
beforeEach(() => {
jest.resetAllMocks();
});

describe('when it\'s open', () => {
it('should render normally', () => {
renderSidebar();
expect(screen.queryByTestId('sidebar')).toBeInTheDocument();
});

it('should not render xpert if no disclosureAcknowledged', () => {
renderSidebar();
expect(screen.queryByTestId('sidebar-xpert')).not.toBeInTheDocument();
});

it('should render xpert if disclosureAcknowledged', () => {
renderSidebar(undefined, { disclosureAcknowledged: true });
expect(screen.queryByTestId('sidebar-xpert')).toBeInTheDocument();
});
});

describe('when it\'s not open', () => {
it('should not render', () => {
renderSidebar({ isOpen: false });
expect(screen.queryByTestId('sidebar')).not.toBeInTheDocument();
});
});

describe('prompt experiment', () => {
const defaultState = {
messageList: [{
role: 'user',
content: 'Testing message 1',
timestamp: +Date.now(),
}, {
role: 'user',
content: 'Testing message 2',
timestamp: +Date.now(),
}],
experiments: {
[PROMPT_EXPERIMENT_FLAG]: {
enabled: true,
variationKey: PROMPT_EXPERIMENT_KEY,
},
},
};

it('should call showVariationSurvey if experiment is active', async () => {
renderSidebar(undefined, defaultState);

await act(() => {
screen.queryByTestId('close-button').click();
});

expect(showVariationSurvey).toHaveBeenCalled();
expect(showControlSurvey).not.toHaveBeenCalled();
});

it('should call showControlSurvey if experiment is not active', async () => {
renderSidebar(undefined, {
...defaultState,
experiments: {},
});

await act(() => {
screen.queryByTestId('close-button').click();
});

expect(showControlSurvey).toHaveBeenCalled();
expect(showVariationSurvey).not.toHaveBeenCalled();
});
});
});
2 changes: 1 addition & 1 deletion src/components/ToggleXpertButton/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const ToggleXpert = ({
contentToolsEnabled,
}) => {
const { experiments } = useSelector(state => state.learningAssistant);
const { variationKey } = experiments ? experiments[PROMPT_EXPERIMENT_FLAG] : {};
const { variationKey } = experiments?.[PROMPT_EXPERIMENT_FLAG] || {};
const [hasDismissedCTA, setHasDismissedCTA] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(true);
const [target, setTarget] = useState(null);
Expand Down
24 changes: 13 additions & 11 deletions src/data/slice.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@
import { createSlice } from '@reduxjs/toolkit';
import { v4 as uuidv4 } from 'uuid';

export const initialState = {
currentMessage: '',
messageList: [],
apiError: false,
apiIsLoading: false,
conversationId: uuidv4(),
disclosureAcknowledged: false,
sidebarIsOpen: false,
isEnabled: false,
experiments: {},
};

export const learningAssistantSlice = createSlice({
name: 'learning-assistant',
initialState: {
currentMessage: '',
messageList: [],
apiError: false,
apiIsLoading: false,
conversationId: uuidv4(),
disclosureAcknowledged: false,
sidebarIsOpen: false,
isEnabled: false,
experiments: {},
},
initialState,
reducers: {
setCurrentMessage: (state, { payload }) => {
state.currentMessage = payload.currentMessage;
Expand Down
2 changes: 1 addition & 1 deletion src/data/thunks.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { PROMPT_EXPERIMENT_FLAG } from '../constants/experiments';
export function addChatMessage(role, content, courseId) {
return (dispatch, getState) => {
const { messageList, conversationId, experiments } = getState().learningAssistant;
const { variationKey } = experiments ? experiments[PROMPT_EXPERIMENT_FLAG] : {};
const { variationKey } = experiments?.[PROMPT_EXPERIMENT_FLAG] || {};

// Redux recommends only serializable values in the store, so we'll stringify the timestap to store in Redux.
// When we need to operate on the Date object, we'll deserialize the string.
Expand Down
13 changes: 13 additions & 0 deletions src/setupTest.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
import 'core-js/stable';
import 'regenerator-runtime/runtime';
import '@testing-library/jest-dom';

jest.mock(
'@optimizely/react-sdk',
() => {
const originalModule = jest.requireActual('@optimizely/react-sdk');
return {
__esModule: true,
...originalModule,
useDecision: jest.fn(() => [{ enabled: true, variationKey: 'control' }]),
};
},
{ virtual: true },
);
13 changes: 11 additions & 2 deletions src/utils/utils.test.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';

import { render } from '@testing-library/react';
import { render, act } from '@testing-library/react';
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import { IntlProvider } from '@edx/frontend-platform/i18n';
Expand Down Expand Up @@ -51,4 +51,13 @@ const createRandomResponseForTesting = () => {
return { role: 'assistant', content: message.join(' ') };
};

export { renderWithProviders as render, createRandomResponseForTesting };
// Helper, that is used to forcibly finalize all promises
// in thunk before running matcher against state.
const executeThunk = async (thunk, dispatch, getState) => {
await thunk(dispatch, getState);
await new Promise(setImmediate);
};

export {
renderWithProviders as render, act, createRandomResponseForTesting, executeThunk,
};

0 comments on commit bd16de8

Please sign in to comment.