Skip to content

Commit fde987e

Browse files
author
kim
committed
test: write tests
1 parent 5755ae6 commit fde987e

File tree

11 files changed

+160
-65
lines changed

11 files changed

+160
-65
lines changed

cypress/e2e/player/main.cy.ts

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { AppDataVisibility, Context, PermissionLevel } from '@graasp/sdk';
22

33
import {
4-
PLAYER_VIEW_CY,
54
buildCommentContainerDataCy,
65
buildDataCy,
76
} from '../../../src/config/selectors';
@@ -24,7 +23,7 @@ const defaultAppData = [
2423
];
2524

2625
describe('Player View', () => {
27-
beforeEach(() => {
26+
it('Show messages and write a new one', () => {
2827
cy.setUpApi(
2928
{
3029
appData: defaultAppData,
@@ -36,10 +35,6 @@ describe('Player View', () => {
3635
},
3736
);
3837
cy.visit('/');
39-
});
40-
41-
it('Show messages and write a new one', () => {
42-
cy.get(buildDataCy(PLAYER_VIEW_CY)).should('be.visible');
4338

4439
// expect previously saved app data
4540
const previousAppData = defaultAppData[0];
@@ -65,4 +60,82 @@ describe('Player View', () => {
6560
'i am a bot', // default return value of the mocked chatbot
6661
);
6762
});
63+
64+
it('Show cue and write a message', () => {
65+
cy.setUpApi(
66+
{
67+
appData: [],
68+
appSettings: [MOCK_APP_SETTING],
69+
},
70+
{
71+
context: Context.Player,
72+
permission: PermissionLevel.Write,
73+
},
74+
);
75+
cy.visit('/');
76+
77+
// expect cue
78+
cy.get(buildDataCy(buildCommentContainerDataCy('cue'))).should(
79+
'contain',
80+
MOCK_APP_SETTING.data.chatbotCue,
81+
);
82+
83+
// type and send message
84+
const message = 'My message';
85+
cy.get('[role="textbox"]').type(message);
86+
cy.get('[name="send"]').click();
87+
88+
// expect user message
89+
cy.get(buildDataCy(buildCommentContainerDataCy('2'))).should(
90+
'contain',
91+
message,
92+
);
93+
94+
// expect chatbot message
95+
cy.get(buildDataCy(buildCommentContainerDataCy('3'))).should(
96+
'contain',
97+
'i am a bot', // default return value of the mocked chatbot
98+
);
99+
});
100+
101+
it('Show dates', () => {
102+
cy.setUpApi(
103+
{
104+
appData: [
105+
{
106+
account: CURRENT_MEMBER,
107+
createdAt: '2025-11-18T16:35:22.010Z',
108+
creator: CURRENT_MEMBER,
109+
data: { content: 'A previously saved message' },
110+
id: '0',
111+
item: MOCK_SERVER_ITEM,
112+
type: 'comment',
113+
updatedAt: '2025-11-18T16:35:22.010Z',
114+
visibility: AppDataVisibility.Member,
115+
},
116+
{
117+
account: CURRENT_MEMBER,
118+
createdAt: '2024-11-18T16:35:22.010Z',
119+
creator: CURRENT_MEMBER,
120+
data: { content: 'A previously saved message' },
121+
id: '1',
122+
item: MOCK_SERVER_ITEM,
123+
type: 'comment',
124+
updatedAt: '2025-11-18T16:35:22.010Z',
125+
visibility: AppDataVisibility.Member,
126+
},
127+
],
128+
appSettings: [MOCK_APP_SETTING],
129+
},
130+
{
131+
context: Context.Player,
132+
permission: PermissionLevel.Write,
133+
},
134+
);
135+
cy.visit('/');
136+
137+
// expect dates
138+
cy.get('#root').should('contain', 'November 18, 2024');
139+
cy.get('#root').should('contain', 'November 18, 2025');
140+
});
68141
});

src/langs/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"SEARCH_BY_OTHER_KEYWORDS": "Search by other keywords or regex",
6363
"CHECK_WHOLE_CHAT": "Check the whole chat",
6464
"CONFIGURE_BUTTON": "Configure",
65-
"CLOSE": "Close"
65+
"CLOSE": "Close",
66+
"SIGN_OUT_ALERT": "You should be signed in to interact with the chatbot"
6667
}
6768
}

src/modules/comment/Conversation.tsx

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
} from '@mui/material';
1212

1313
import { ChatbotPromptSettings } from '@/config/appSetting';
14-
import { DEFAULT_BOT_USERNAME } from '@/constants';
1514

1615
import CommentEditor from '../common/CommentEditor';
1716
import CommentThread from '../common/CommentThread';
@@ -33,21 +32,7 @@ function Conversation({
3332
const { t } = useTranslation();
3433

3534
if (chatbotPrompt) {
36-
const { chatbotCue, chatbotName } = chatbotPrompt;
37-
38-
// construct cue message if there is no messages yet
39-
let commentsToShow = comments;
40-
if (0 === comments.length) {
41-
commentsToShow = [
42-
{
43-
id: 'cue',
44-
isBot: true,
45-
createdAt: new Date().toISOString(),
46-
body: chatbotCue,
47-
username: DEFAULT_BOT_USERNAME,
48-
},
49-
];
50-
}
35+
const { chatbotName } = chatbotPrompt;
5136

5237
return (
5338
<Box
@@ -62,7 +47,7 @@ function Conversation({
6247
<Stack gap={3} px={2}>
6348
<ChatbotHeader name={chatbotName} />
6449
<Divider />
65-
<CommentThread threadSx={threadSx} comments={commentsToShow} />
50+
<CommentThread threadSx={threadSx} comments={comments} />
6651
<CommentEditor chatbotPrompt={chatbotPrompt} />
6752
</Stack>
6853
</CommentContainer>

src/modules/common/ChatbotPrompt.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DEFAULT_BOT_USERNAME } from '@/constants';
22

3-
import Comment from './Comment';
3+
import { Comment } from './Comment';
44

55
type Props = {
66
chatbotCue?: string;
@@ -12,9 +12,7 @@ function ChatbotPrompt({
1212
chatbotName = DEFAULT_BOT_USERNAME,
1313
}: Readonly<Props>): JSX.Element | null {
1414
if (chatbotCue) {
15-
return (
16-
<Comment id={'cue'} isBot body={chatbotCue} username={chatbotName} />
17-
);
15+
return <Comment id="cue" isBot body={chatbotCue} username={chatbotName} />;
1816
}
1917
return null;
2018
}

src/modules/common/Comment.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,18 @@ import CommentBody from './CommentBody';
99
import CustomAvatar from './CustomAvatar';
1010

1111
type Props = {
12+
id: string;
1213
body: string;
1314
isBot: boolean;
14-
id: string;
1515
username: string;
1616
};
1717

18-
function Comment({ id, isBot, body, username }: Readonly<Props>): JSX.Element {
18+
export function Comment({
19+
id,
20+
isBot,
21+
body,
22+
username,
23+
}: Readonly<Props>): JSX.Element {
1924
const commentRef = useRef<HTMLDivElement>(null);
2025

2126
const avatar = isBot ? (
@@ -30,19 +35,18 @@ function Comment({ id, isBot, body, username }: Readonly<Props>): JSX.Element {
3035
direction={isBot ? 'row' : 'row-reverse'}
3136
alignItems="end"
3237
justifyContent={isBot ? 'start' : 'end'}
38+
gap={1}
39+
pl={isBot ? 0 : 10}
40+
pr={isBot ? 10 : 0}
3341
>
3442
<Stack>{avatar}</Stack>
35-
<Stack
36-
sx={{ p: 2, py: 0, '&:last-child': { pb: 0 } }}
37-
alignItems={isBot ? 'start' : 'end'}
38-
gap={1}
39-
>
43+
<Stack sx={{ py: 0 }} alignItems={isBot ? 'start' : 'end'} gap={1}>
4044
<Typography variant="subtitle2">{username}</Typography>
41-
<CommentBody>{body}</CommentBody>
45+
<CommentBody background={isBot ? undefined : '#ddd'}>
46+
{body}
47+
</CommentBody>
4248
</Stack>
4349
</Stack>
4450
</Stack>
4551
);
4652
}
47-
48-
export default Comment;

src/modules/common/CommentBody.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { BIG_BORDER_RADIUS } from '@/constants';
1111

1212
// oxlint-disable no-magic-numbers
1313
const StyledReactMarkdown = styled(ReactMarkdown)(({ theme }) => ({
14-
background: '#efefef',
1514
borderRadius: 8,
1615
padding: theme.spacing(1),
1716
'& .prism-code': {
@@ -115,11 +114,17 @@ function code(props: {
115114
);
116115
}
117116

118-
function CommentBody({ children }: Readonly<{ children: string }>) {
117+
function CommentBody({
118+
children,
119+
background = '#efefef',
120+
}: Readonly<{ children: string; background?: string }>) {
119121
return (
120122
<StyledReactMarkdown
121123
remarkPlugins={[remarkGfm, remarkBreaks]}
122124
components={{ code }}
125+
sx={{
126+
background,
127+
}}
123128
>
124129
{children}
125130
</StyledReactMarkdown>

src/modules/common/CommentEditor.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ function CommentEditor({
6161

6262
const { generateChatbotAnswer, isLoading: askChatbotLoading } =
6363
useAskChatbot(chatbotPrompt);
64-
const { sendMessage, isLoading: sendMessageLoading } = useSendMessage();
64+
const { sendMessage, isLoading: sendMessageLoading } =
65+
useSendMessage(chatbotPrompt);
6566

6667
const onSendHandler = async (newUserComment: string) => {
6768
if (!chatbotPrompt) {

src/modules/common/CommentThread.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Stack, Typography } from '@mui/material';
77
import { intlFormat } from 'date-fns';
88
import groupby from 'lodash.groupby';
99

10-
import Comment from './Comment';
10+
import { Comment } from './Comment';
1111
import { type Comment as CommentType } from './useConversation';
1212

1313
type Props = {
@@ -56,9 +56,9 @@ function CommentThread({
5656
return (
5757
<Comment
5858
key={c.id}
59+
id={c.id}
5960
isBot={c.isBot}
6061
username={c.username}
61-
id="hello"
6262
body={c.body}
6363
/>
6464
);

src/modules/common/useConversation.tsx

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,41 @@ export const useConversation = (accountId?: string) => {
2020
});
2121

2222
// get comments for given user only
23-
const comments = appData
24-
?.filter((res) => res.creator?.id === accountId)
25-
?.toSorted((c1, c2) => (c1.createdAt > c2.createdAt ? 1 : -1))
26-
?.map((c) => {
27-
const isBot = c.type === AppDataTypes.BotComment;
28-
return {
29-
id: c.id,
30-
createdAt: c.createdAt,
31-
isBot,
32-
body: c.data.content,
33-
username: isBot
34-
? DEFAULT_BOT_USERNAME
35-
: (c.account.name ?? ANONYMOUS_USER),
36-
};
37-
});
23+
const comments =
24+
appData
25+
?.filter((res) => res.creator?.id === accountId)
26+
?.toSorted((c1, c2) => (c1.createdAt > c2.createdAt ? 1 : -1))
27+
?.map((c) => {
28+
const isBot = c.type === AppDataTypes.BotComment;
29+
return {
30+
id: c.id,
31+
createdAt: c.createdAt,
32+
isBot,
33+
body: c.data.content,
34+
username: isBot
35+
? DEFAULT_BOT_USERNAME
36+
: (c.account.name ?? ANONYMOUS_USER),
37+
};
38+
}) ?? [];
3839

3940
const chatbotPrompt = chatbotPromptSettings?.[0]?.data;
4041

42+
// include cue as comment if there is no comments
43+
const chatbotCueComment =
44+
chatbotPrompt?.chatbotCue && 0 === comments.length
45+
? [
46+
{
47+
id: 'cue',
48+
isBot: true,
49+
createdAt: new Date().toISOString(),
50+
body: chatbotPrompt.chatbotCue,
51+
username: DEFAULT_BOT_USERNAME,
52+
},
53+
]
54+
: [];
55+
4156
return {
42-
comments: comments ?? [],
57+
comments: [...chatbotCueComment, ...comments],
4358
chatbotPrompt,
4459
isLoading: isAppDataLoading || isChatbotSettingsLoading,
4560
};

src/modules/common/useSendMessage.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
import { useCallback } from 'react';
22

33
import { AppActionsType } from '@/config/appActions';
4-
import { AppDataTypes } from '@/config/appData';
5-
import { mutations } from '@/config/queryClient';
4+
import { AppDataTypes, CommentData } from '@/config/appData';
5+
import { hooks, mutations } from '@/config/queryClient';
66

77
/**
88
* Create a function that save an app data with the user message
99
* @returns sendMessage function
1010
*/
11-
export const useSendMessage = () => {
11+
export const useSendMessage = ({ chatbotCue }: { chatbotCue?: string }) => {
12+
const { data: comments } = hooks.useAppData<CommentData>();
1213
const { mutateAsync: postAppDataAsync, isLoading } =
1314
mutations.usePostAppData();
1415
const { mutateAsync: postAppActionAsync } = mutations.usePostAppAction();
1516

1617
const sendMessage = useCallback(
1718
async (newUserComment: string) => {
18-
// post new user comment as appData with normal call
19+
// save cue on first comment
20+
if (chatbotCue && 0 === comments?.length) {
21+
await postAppDataAsync({
22+
data: {
23+
content: chatbotCue,
24+
},
25+
type: AppDataTypes.BotComment,
26+
});
27+
}
28+
1929
const userMessage = await postAppDataAsync({
2030
data: {
2131
content: newUserComment,
@@ -30,7 +40,7 @@ export const useSendMessage = () => {
3040

3141
return userMessage;
3242
},
33-
[postAppDataAsync, postAppActionAsync],
43+
[comments?.length, chatbotCue, postAppDataAsync, postAppActionAsync],
3444
);
3545

3646
return { sendMessage, isLoading };

0 commit comments

Comments
 (0)