Skip to content

Commit 0c64fb9

Browse files
committed
Add test on custom typing notification
1 parent 62dfbc8 commit 0c64fb9

File tree

2 files changed

+250
-202
lines changed

2 files changed

+250
-202
lines changed
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
/*
2+
* Copyright (c) Jupyter Development Team.
3+
* Distributed under the terms of the Modified BSD License.
4+
*/
5+
6+
import {
7+
IJupyterLabPageFixture,
8+
expect,
9+
galata,
10+
test
11+
} from '@jupyterlab/galata';
12+
import { User } from '@jupyterlab/services';
13+
14+
import {
15+
exposeDepsJs,
16+
getPlugin,
17+
openChat,
18+
openSettings,
19+
sendMessage,
20+
USER
21+
} from './test-utils';
22+
23+
const FILENAME = 'my-chat.chat';
24+
25+
test.use({
26+
mockUser: USER,
27+
mockSettings: { ...galata.DEFAULT_SETTINGS }
28+
});
29+
30+
test.describe('#typingNotification', () => {
31+
let guestPage: IJupyterLabPageFixture;
32+
test.beforeEach(
33+
async ({ baseURL, browser, page, tmpPath, waitForApplication }) => {
34+
// Create a chat file
35+
await page.filebrowser.contents.uploadContent('{}', 'text', FILENAME);
36+
37+
// Create a new user.
38+
const user2: Partial<User.IUser> = {
39+
identity: {
40+
username: 'jovyan_2',
41+
name: 'jovyan_2',
42+
display_name: 'jovyan_2',
43+
initials: 'JP',
44+
color: 'var(--jp-collaborator-color2)'
45+
}
46+
};
47+
48+
// Create a new page for guest.
49+
const { page: newPage } = await galata.newPage({
50+
baseURL: baseURL!,
51+
browser,
52+
mockUser: user2,
53+
tmpPath,
54+
waitForApplication
55+
});
56+
await newPage.evaluate(() => {
57+
// Acknowledge any dialog
58+
window.galataip.on('dialog', d => {
59+
d?.resolve();
60+
});
61+
});
62+
guestPage = newPage;
63+
}
64+
);
65+
66+
test.afterEach(async ({ page }) => {
67+
await guestPage.close();
68+
if (await page.filebrowser.contents.fileExists(FILENAME)) {
69+
await page.filebrowser.contents.deleteFile(FILENAME);
70+
}
71+
});
72+
73+
test('should display typing user', async ({ page }) => {
74+
const chatPanel = await openChat(page, FILENAME);
75+
const writers = chatPanel.locator('.jp-chat-writers');
76+
77+
const guestChatPanel = await openChat(guestPage, FILENAME);
78+
const guestInput = guestChatPanel
79+
.locator('.jp-chat-input-container')
80+
.getByRole('combobox');
81+
82+
await guestInput.press('a');
83+
await expect(writers).toBeAttached();
84+
const start = Date.now();
85+
await expect(writers).toHaveText(/jovyan_2 is writing/);
86+
await expect(writers).not.toBeAttached();
87+
88+
// Message should disappear after 1s, but this delay include the awareness update.
89+
expect(Date.now() - start).toBeLessThanOrEqual(2000);
90+
});
91+
92+
test('should display typing user editing a message', async ({ page }) => {
93+
const chatPanel = await openChat(page, FILENAME);
94+
const writers = chatPanel.locator('.jp-chat-writers');
95+
96+
const guestChatPanel = await openChat(guestPage, FILENAME);
97+
98+
await sendMessage(guestPage, FILENAME, 'test');
99+
await expect(writers).not.toBeAttached();
100+
const message = guestChatPanel
101+
.locator('.jp-chat-messages-container .jp-chat-message')
102+
.first();
103+
const messageContent = message.locator('.jp-chat-rendered-markdown');
104+
105+
// Should display the message toolbar
106+
await messageContent.hover({ position: { x: 5, y: 5 } });
107+
await messageContent.locator('.jp-chat-toolbar jp-button').first().click();
108+
109+
const editInput = guestChatPanel
110+
.locator('.jp-chat-messages-container .jp-chat-input-container')
111+
.getByRole('combobox');
112+
113+
await editInput.focus();
114+
115+
await editInput.press('a');
116+
await expect(writers).toBeAttached();
117+
const start = Date.now();
118+
await expect(writers).toHaveText(/jovyan_2 is writing/);
119+
await expect(writers).not.toBeAttached();
120+
121+
// Message should disappear after 1s, but this delay include the awareness update.
122+
expect(Date.now() - start).toBeLessThanOrEqual(2000);
123+
});
124+
125+
test('should not display typing users if disabled', async ({ page }) => {
126+
const chatPanel = await openChat(page, FILENAME);
127+
const writers = chatPanel.locator('.jp-chat-writers');
128+
129+
// Modify the guest settings
130+
const settings = await openSettings(guestPage);
131+
const sendTypingNotification = settings?.getByRole('checkbox', {
132+
name: 'sendTypingNotification'
133+
});
134+
await sendTypingNotification?.uncheck();
135+
// wait for the settings to be saved
136+
await expect(guestPage.activity.getTabLocator('Settings')).toHaveAttribute(
137+
'class',
138+
/jp-mod-dirty/
139+
);
140+
await expect(
141+
guestPage.activity.getTabLocator('Settings')
142+
).not.toHaveAttribute('class', /jp-mod-dirty/);
143+
144+
const guestChatPanel = await openChat(guestPage, FILENAME);
145+
const guestInput = guestChatPanel
146+
.locator('.jp-chat-input-container')
147+
.getByRole('combobox');
148+
149+
await guestInput.press('a');
150+
151+
let visible = true;
152+
try {
153+
await page.waitForCondition(() => writers.isVisible(), 3000);
154+
} catch {
155+
visible = false;
156+
}
157+
158+
if (visible) {
159+
throw Error('The typing notification should not be attached.');
160+
}
161+
});
162+
163+
test('should display several typing users', async ({
164+
baseURL,
165+
browser,
166+
page,
167+
tmpPath,
168+
waitForApplication
169+
}) => {
170+
// Create a new user.
171+
const user3: Partial<User.IUser> = {
172+
identity: {
173+
username: 'jovyan_3',
174+
name: 'jovyan_3',
175+
display_name: 'jovyan_3',
176+
initials: 'JP',
177+
color: 'var(--jp-collaborator-color3)'
178+
}
179+
};
180+
181+
// Create a new page for guest.
182+
const { page: newPage } = await galata.newPage({
183+
baseURL: baseURL!,
184+
browser,
185+
mockUser: user3,
186+
tmpPath,
187+
waitForApplication
188+
});
189+
await newPage.evaluate(() => {
190+
// Acknowledge any dialog
191+
window.galataip.on('dialog', d => {
192+
d?.resolve();
193+
});
194+
});
195+
const guestPage2 = newPage;
196+
197+
const chatPanel = await openChat(page, FILENAME);
198+
const writers = chatPanel.locator('.jp-chat-writers');
199+
200+
const guestChatPanel = await openChat(guestPage, FILENAME);
201+
const guestInput = guestChatPanel
202+
.locator('.jp-chat-input-container')
203+
.getByRole('combobox');
204+
205+
const guest2ChatPanel = await openChat(guestPage2, FILENAME);
206+
const guest2Input = guest2ChatPanel
207+
.locator('.jp-chat-input-container')
208+
.getByRole('combobox');
209+
210+
await guestInput.press('a');
211+
await guest2Input.press('a');
212+
213+
await expect(writers).toBeAttached();
214+
const regexp = /JP(jovyan_[2|3]) and JP(jovyan_[2|3]) are writing/;
215+
await expect(writers).toHaveText(regexp);
216+
217+
const result = regexp.exec((await writers.textContent()) ?? '');
218+
expect(result?.[1] !== undefined);
219+
expect(result?.[1] !== result?.[2]);
220+
await expect(writers).not.toBeAttached();
221+
});
222+
223+
test('should display custom typing notification', async ({ page }) => {
224+
const NOTIFICATION = 'is doing something awesome';
225+
const chatPanel = await openChat(page, FILENAME);
226+
const writers = chatPanel.locator('.jp-chat-writers');
227+
228+
const guestChatPanel = await openChat(guestPage, FILENAME);
229+
230+
// Update the typing notification for jovyan_2
231+
await guestPage.evaluate(notification => {
232+
const chatWidget = window.jupyterapp.shell.currentWidget as any;
233+
const clientID = chatWidget.model.sharedModel.awareness.clientID;
234+
chatWidget.model.sharedModel.awareness.states.get(
235+
clientID
236+
).typingIndicator = notification;
237+
}, NOTIFICATION);
238+
239+
const guestInput = guestChatPanel
240+
.locator('.jp-chat-input-container')
241+
.getByRole('combobox');
242+
await guestInput.press('a');
243+
244+
await expect(writers).toBeAttached();
245+
const regexp = new RegExp(`jovyan_2 ${NOTIFICATION}`);
246+
await expect(writers).toHaveText(regexp);
247+
});
248+
});

0 commit comments

Comments
 (0)