Skip to content

Commit 88b491d

Browse files
authored
Merge pull request #1591 from GetStream/develop
v8.1.3
2 parents 51250f0 + a1cfdda commit 88b491d

File tree

58 files changed

+1478
-694
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1478
-694
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ E2E_TEST_USER_2_TOKEN='test-user-2-jwt'
77
E2E_JUMP_TO_MESSAGE_CHANNEL='jump-to-message'
88
E2E_ADD_MESSAGE_CHANNEL='add-message'
99
E2E_ADDITIONAL_CHANNELS="mr-channel-1,mr-channel-2"
10+
E2E_LONG_MESSAGE_LISTS_CHANNEL='navigate-long-message-lists'

.github/workflows/ci.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ jobs:
5555
E2E_TEST_USER_1_TOKEN: ${{ secrets.E2E_TEST_USER_1_TOKEN }}
5656
E2E_TEST_USER_2_TOKEN: ${{ secrets.E2E_TEST_USER_2_TOKEN }}
5757
E2E_ADDITIONAL_CHANNELS: mr-channel-1, mr-channel-2
58+
E2E_LONG_MESSAGE_LISTS_CHANNEL: navigate-long-message-lists
59+
60+
- name: 🎥 Upload Artifacts
61+
uses: actions/upload-artifact@v3
62+
if: ${{ always() }}
63+
with:
64+
name: E2E_Artifacts
65+
path: ./test-results
66+
retention-days: 1
5867

5968
test:
6069
runs-on: ubuntu-latest

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ coverage
2424
# nyc test coverage
2525
.nyc_output
2626

27-
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
27+
# playwright artifacts
28+
test-results
29+
30+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
2831
.grunt
2932

3033
# Bower dependency directory (https://bower.io/)

.ladle/components.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import React from 'react';
33

44
import "./styles.css";
55

6-
6+
// https://ladle.dev/docs/providers
7+
// At the moment used to provide the styles from ./styles.css to all the stories
78
export const Provider: GlobalProvider = ({ children }) => (
89
<>
910
{children}

.releaserc.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88
}
99
],
1010
"plugins": [
11-
"@semantic-release/commit-analyzer",
11+
[
12+
"@semantic-release/commit-analyzer",
13+
{
14+
"preset": "angular",
15+
"releaseRules": [{ "type": "chore", "scope": "deps", "release": "patch" }]
16+
}
17+
],
1218
"@semantic-release/release-notes-generator",
1319
[
1420
"@semantic-release/changelog",

e2e/.eslintrc.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"extends": "../.eslintrc.json",
3+
"rules": {
4+
"jest/require-top-level-describe": "off",
5+
"jest/no-done-callback": "off"
6+
}
7+
}

e2e/add-message.test.ts

Lines changed: 103 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,123 @@
11
/* eslint-disable jest/no-done-callback */
22
/* eslint-disable jest/require-top-level-describe */
3-
import { expect, Page, test } from '@playwright/test';
4-
5-
function getPreview(page: Page) {
6-
return page.locator('*data-testid=channel-preview-button >> text=add-message');
7-
}
8-
9-
test.describe('add a message', () => {
10-
test.beforeEach(async ({ baseURL, page }) => {
11-
await page.goto(`${baseURL}/?story=connected-user--user1`);
12-
await page.waitForSelector('[data-storyloaded]');
13-
// Select correct channel
14-
const preview = getPreview(page);
15-
await preview.click();
3+
import selectors from './user/selectors';
4+
import { test } from './user/test';
5+
6+
const CHANNEL_NAME = 'add-message' as const;
7+
const ADDED_MESSAGE_MAIN_LIST = 'Hello world!' as const;
8+
const ADDED_MESSAGE_THREAD = 'Hello world back!' as const;
9+
10+
test.describe('add text message', () => {
11+
12+
test.beforeEach(async ({controller, user}) => {
13+
await controller.openStory('add-message--user1', selectors.channelPreviewButton);
14+
await user.clicks.ChannelPreview.text(CHANNEL_NAME);
1615
});
1716

18-
test('message list should clear', async ({ page }) => {
19-
await page.click('data-testid=truncate');
20-
const list = page.locator('.str-chat__list');
21-
await expect(list).toBeEmpty();
17+
test('message list and preview button should be clear', async ({controller, user}) => {
18+
await controller.clearChannel()
19+
await user.sees.MessageList.empty();
20+
await user.sees.ChannelPreview(CHANNEL_NAME).empty();
2221
});
2322

24-
test('channel list preview should be cleared', async ({ page }) => {
25-
const preview = getPreview(page);
26-
await expect(preview).toContainText('Nothing yet...');
23+
test('message list should update for current user', async ({controller, user}) => {
24+
await controller.sendMessage();
25+
26+
await user.sees.MessageList.not.empty();
27+
await user.sees.MessageList.contains.nthMessage(ADDED_MESSAGE_MAIN_LIST);
28+
await user.sees.ChannelPreview(CHANNEL_NAME).contains.lastMessage(ADDED_MESSAGE_MAIN_LIST);
2729
});
30+
});
2831

29-
test('message list should update for current user', async ({ page }) => {
30-
await page.click('data-testid=add-message');
31-
const list = page.locator('.str-chat__list');
32-
await expect(list).not.toBeEmpty();
33-
const newMessage = page.locator('.str-chat__message').first();
34-
await expect(newMessage).toContainText('Hello world!');
32+
test.describe('receive a message', () => {
33+
34+
test.beforeEach(async ({controller, user}) => {
35+
await controller.openStory('add-message--user1', selectors.channelPreviewButton);
36+
37+
await user.clicks.ChannelPreview.text(CHANNEL_NAME);
38+
39+
await controller.sendMessage();
40+
41+
await controller.openStory('add-message--user2', selectors.channelPreviewButton);
3542
});
3643

37-
test('channel list should update for current user', async ({ page }) => {
38-
const preview = getPreview(page);
39-
await expect(preview).toContainText('Hello world!');
44+
test('channel list should update for channel members and show unread', async ({user}) => {
45+
await user.sees.ChannelPreview(CHANNEL_NAME).not.read();
46+
await user.sees.ChannelPreview(CHANNEL_NAME).contains.lastMessage(ADDED_MESSAGE_MAIN_LIST);
47+
});
48+
49+
test('message list should update for different users on the channel', async ({user, page}) => {
50+
await Promise.all([
51+
page.waitForResponse((r) => r.url().includes('/read') && r.ok()),
52+
user.clicks.ChannelPreview.text(CHANNEL_NAME)
53+
]);
54+
await user.sees.MessageList.not.empty();
55+
await user.sees.ChannelPreview(CHANNEL_NAME).read();
56+
await user.sees.MessageList.contains.nthMessage(ADDED_MESSAGE_MAIN_LIST);
4057
});
4158
});
4259

43-
test.describe('receive a message', () => {
44-
test.beforeEach(async ({ baseURL, page }) => {
45-
await page.goto(`${baseURL}/?story=connected-user--user2`);
46-
await page.waitForSelector('[data-storyloaded]');
60+
61+
test.describe('reply to a message', () => {
62+
test.beforeEach(async ({controller, user}) => {
63+
await controller.openStory('add-message--user1', selectors.channelPreviewButton);
64+
await user.clicks.ChannelPreview.text(CHANNEL_NAME);
65+
});
66+
67+
test.afterEach(async ({user}) => {
68+
await user.clicks.Thread.close();
69+
})
70+
71+
test('thread with no replies contains only parent message', async ({controller, user}) => {
72+
await controller.clearChannel();
73+
await controller.sendMessage();
74+
await user.clicks.MessageActions.reply(ADDED_MESSAGE_MAIN_LIST);
75+
await user.sees.Thread.empty();
76+
});
77+
78+
test('reply to a message should appear at the bottom of the thread and in channel preview', async ({ user}) => {
79+
await user.clicks.MessageActions.reply(ADDED_MESSAGE_MAIN_LIST);
80+
await user.submits.MessageInput.reply(ADDED_MESSAGE_THREAD);
81+
await user.sees.Thread.not.empty();
82+
await user.sees.Thread.inViewport.nthMessage(ADDED_MESSAGE_THREAD, -1);
83+
// todo: channel preview does not reflect new messages from thread
84+
// await user.sees.ChannelPreview(CHANNEL_NAME).contains.lastMessage(ADDED_MESSAGE_THREAD);
4785
});
86+
})
4887

49-
test('channel list should update for channel members and show unread', async ({ page }) => {
50-
const preview = getPreview(page);
51-
await expect(preview).toHaveClass(/str-chat__channel-preview-messenger--unread/);
52-
await expect(preview).toContainText('Hello world!');
88+
test.describe('receive the reply', () => {
89+
test.beforeEach(async ({controller, user: user1, page}) => {
90+
await controller.openStory('add-message--user1', selectors.channelPreviewButton);
91+
await user1.clicks.ChannelPreview.text(CHANNEL_NAME);
92+
await controller.clearChannel();
93+
await controller.sendMessage();
94+
await user1.clicks.MessageActions.reply(ADDED_MESSAGE_MAIN_LIST);
95+
await Promise.all([
96+
page.waitForResponse((r) => r.url().includes('/message') && r.ok()),
97+
user1.submits.MessageInput.reply(ADDED_MESSAGE_THREAD)
98+
])
99+
await controller.openStory('add-message--user2', selectors.channelPreviewButton);
53100
});
54101

55-
test('message list should update for different users on the channel', async ({ page }) => {
56-
const preview = getPreview(page);
57-
await preview.click();
58-
await expect(preview).not.toHaveClass(/str-chat__channel-preview-messenger--unread/);
102+
// todo: channel preview does not reflect new messages from thread
103+
// test('for the other user channel preview displays correct last message showed unread', async ({user: user2}) => {
104+
// await user2.sees.ChannelPreview(CHANNEL_NAME).not.read();
105+
// await user2.sees.ChannelPreview(CHANNEL_NAME).contains.lastMessage(ADDED_MESSAGE_THREAD);
106+
// });
107+
108+
test('the other user sees that reply to a message appeared at the bottom of the thread and in channel preview', async ({user: user2, page}) => {
109+
await Promise.all([
110+
page.waitForResponse((r) => r.url().includes('/read') && r.ok()),
111+
user2.clicks.ChannelPreview.text(CHANNEL_NAME)
112+
]);
113+
114+
await Promise.all([
115+
page.waitForResponse((r) => r.url().includes('/replies') && r.ok()),
116+
user2.clicks.MessageActions.reply(ADDED_MESSAGE_MAIN_LIST),
117+
]);
59118

60-
const list = page.locator('.str-chat__list');
61-
await expect(list).not.toBeEmpty();
62-
const newMessage = page.locator('.str-chat__message').first();
63-
await expect(newMessage).toContainText('Hello world!');
119+
await user2.sees.Thread.not.empty();
120+
await user2.sees.ChannelPreview(CHANNEL_NAME).read();
121+
await user2.sees.Thread.inViewport.nthMessage(ADDED_MESSAGE_THREAD, -1);
64122
});
65123
});

e2e/autocomplete-mention.test.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
/* eslint-disable jest/no-done-callback */
22
/* eslint-disable jest/require-top-level-describe */
3-
import { expect, test } from '@playwright/test';
3+
import { expect } from '@playwright/test';
4+
5+
import {test} from './user/test';
6+
import { getMessageInput } from './user/components/MessageInput';
7+
import selectors from './user/selectors';
8+
9+
const MENTION_TRIGGER = '@';
410

511
test.describe('autocomplete a mention', () => {
6-
test('should fill in textarea with username', async ({ baseURL, page }) => {
7-
await page.goto(`${baseURL}/?story=hello--basic-setup`);
8-
await page.waitForSelector('[data-storyloaded]');
9-
await page.fill('data-testid=message-input', '@');
10-
const button = await page.locator(
11-
'button.rta__entity >> :nth-match(span.str-chat__user-item--name, 1)',
12-
);
13-
button.click();
14-
const textContent = await button.textContent();
12+
test('should fill in textarea with username', async ({ controller, user, page }) => {
13+
await controller.openStory('hello--basic-setup', selectors.messageInput );
14+
15+
await user.typesTo.MessageInput.text(MENTION_TRIGGER);
16+
17+
const autocompleteSuggestionItem = await user.clicks.AutocompleteSuggestionItem.nth(1)
18+
const textContent = await autocompleteSuggestionItem.textContent();
1519

16-
await expect(page.locator('data-testid=message-input')).toContainText(`@${textContent}`);
20+
await expect(getMessageInput(page)).toContainText(`${MENTION_TRIGGER}${textContent}`);
1721
});
18-
});
22+
})

e2e/fixtures.mjs

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,35 @@ dotenv.config({ path: `.env.local` });
1111
E2E_APP_KEY,
1212
E2E_APP_SECRET,
1313
E2E_JUMP_TO_MESSAGE_CHANNEL,
14+
E2E_LONG_MESSAGE_LISTS_CHANNEL,
1415
E2E_TEST_USER_1,
1516
E2E_TEST_USER_2,
1617
} = process.env;
1718

19+
async function generateMessages(start, stop, channel, parent_id) {
20+
const count = stop - start;
21+
let messageToQuote;
22+
const messageResponses = [];
23+
for (let i = start; i < stop; i++) {
24+
if (process.stdout.clearLine && process.stdout.cursorTo) {
25+
printProgress((i-start) / count);
26+
}
27+
28+
const res = await channel.sendMessage({
29+
text: `Message ${i}`,
30+
user: { id: i % 2 ? E2E_TEST_USER_1 : E2E_TEST_USER_2 },
31+
...(i === (start + 140) ? { quoted_message_id: messageToQuote.message.id } : {}),
32+
...(parent_id ? { parent_id } : {}),
33+
});
34+
35+
if (i === (start + 20)) {
36+
messageToQuote = res;
37+
}
38+
messageResponses.push(res);
39+
}
40+
return messageResponses;
41+
}
42+
1843
const chat = StreamChat.getInstance(E2E_APP_KEY, E2E_APP_SECRET);
1944

2045
// Users
@@ -32,22 +57,27 @@ dotenv.config({ path: `.env.local` });
3257
});
3358
await channel.create();
3459
await channel.truncate();
35-
let messageToQuote;
36-
for (let i = 0; i < MESSAGES_COUNT; i++) {
37-
if (process.stdout.clearLine && process.stdout.cursorTo) {
38-
printProgress(i / MESSAGES_COUNT);
39-
}
4060

41-
const res = await channel.sendMessage({
42-
text: `Message ${i}`,
43-
user: { id: i % 2 ? E2E_TEST_USER_1 : E2E_TEST_USER_2 },
44-
...(i === 140 ? { quoted_message_id: messageToQuote.message.id } : {}),
45-
});
61+
await generateMessages(0, MESSAGES_COUNT, channel);
62+
63+
process.stdout.write('\n');
64+
}
65+
66+
// 'navigate-long-message-lists` channel
67+
{
68+
console.log(`Creating and populating channel '${E2E_LONG_MESSAGE_LISTS_CHANNEL}'...`);
69+
const channel = chat.channel('messaging', E2E_LONG_MESSAGE_LISTS_CHANNEL, {
70+
created_by_id: E2E_TEST_USER_1,
71+
members: [E2E_TEST_USER_1, E2E_TEST_USER_2],
72+
name: E2E_LONG_MESSAGE_LISTS_CHANNEL,
73+
});
74+
await channel.create();
75+
await channel.truncate();
76+
77+
const messages = await generateMessages(0, 150, channel);
78+
await generateMessages(150, 300, channel, messages.slice(-1)[0].message.id)
79+
4680

47-
if (i === 20) {
48-
messageToQuote = res;
49-
}
50-
}
5181
process.stdout.write('\n');
5282
}
5383

@@ -79,7 +109,7 @@ dotenv.config({ path: `.env.local` });
79109
await channel.truncate();
80110
}
81111
}
82-
})();
112+
})().catch(e => console.error(e));
83113

84114
const printProgress = (progress) => {
85115
process.stdout.clearLine();

0 commit comments

Comments
 (0)