discourse/plugins/chat/spec/system/chat_channel_spec.rb
Joffrey JAFFEUX bbb8595107
PERF: defer loading channels (#26155)
Prior to this change we would pre-load all the user channels which making initial page load slower. This change will make them be loaded right after initial load. In the past this was not possible as the channels would have to be loaded on each page transition. However since about a year, we made the channels to be cached on the frontend and no other request will be needed.

I have decided for now to not show a loading state in the sidebar as I think it would be noise, but we can reconsider this later.

Note given we don't have the channels loaded at first certain things where harder to accomplish. The biggest UX change of this commit is that we removed all the complex logic of computing the best channel to display when you load /chat. We will now store the id of the last channel you visited and will use this id to decide which channel to show.
2024-03-18 08:35:07 +01:00

389 lines
12 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# frozen_string_literal: true
RSpec.describe "Chat channel", type: :system do
fab!(:current_user) { Fabricate(:user) }
fab!(:channel_1) { Fabricate(:chat_channel) }
fab!(:message_1) { Fabricate(:chat_message, chat_channel: channel_1) }
let(:chat_page) { PageObjects::Pages::Chat.new }
let(:channel_page) { PageObjects::Pages::ChatChannel.new }
let(:sidebar_page) { PageObjects::Pages::Sidebar.new }
let(:side_panel_page) { PageObjects::Pages::ChatSidePanel.new }
before do
chat_system_bootstrap
channel_1.add(current_user)
sign_in(current_user)
end
context "when has unread threads" do
fab!(:thread_1) { Fabricate(:chat_thread, channel: channel_1) }
before do
channel_1.update!(threading_enabled: true)
thread_1.add(current_user)
Fabricate(:chat_message, thread: thread_1, use_service: true)
end
context "when visiting channel" do
it "opens thread panel" do
chat_page.visit_channel(channel_1)
expect(side_panel_page).to have_open_thread_list
end
end
context "when visiting channel on mobile", mobile: true do
it "doesnt open thread panel" do
chat_page.visit_channel(channel_1)
expect(side_panel_page).to have_no_open_thread_list
end
end
context "when visiting thread" do
it "doesnt open thread panel" do
chat_page.visit_thread(thread_1)
expect(side_panel_page).to have_no_open_thread_list
end
end
context "when opening channel message" do
it "doesnt open thread panel" do
chat_page.visit_channel(channel_1, message_id: message_1.id)
expect(side_panel_page).to have_no_open_thread_list
end
end
end
context "when first batch of messages doesnt fill page" do
before { 30.times { Fabricate(:chat_message, user: current_user, chat_channel: channel_1) } }
it "autofills for more messages" do
chat_page.prefers_full_page
visit("/")
# cheap trick to ensure the messages don't fill the initial page
page.execute_script(
"document.head.insertAdjacentHTML('beforeend', `<style>.chat-message-text{font-size:3px;}</style>`)",
)
sidebar_page.open_channel(channel_1)
expect(channel_page.messages).to have_message(id: message_1.id)
end
end
context "when sending a message" do
context "with lots of messages" do
before { 50.times { Fabricate(:chat_message, chat_channel: channel_1) } }
it "loads most recent messages" do
unloaded_message = Fabricate(:chat_message, chat_channel: channel_1)
chat_page.visit_channel(channel_1, message_id: message_1.id)
expect(channel_page.messages).to have_no_message(id: unloaded_message.id)
channel_page.send_message
expect(channel_page.messages).to have_message(id: unloaded_message.id)
end
end
context "with two sessions opened on same channel" do
it "syncs the messages" do
Jobs.run_immediately!
using_session(:tab_1) do
sign_in(current_user)
chat_page.visit_channel(channel_1)
end
using_session(:tab_2) do
sign_in(current_user)
chat_page.visit_channel(channel_1)
end
using_session(:tab_1) { channel_page.send_message("test_message") }
using_session(:tab_2) do
expect(channel_page.messages).to have_message(text: "test_message")
end
end
end
it "allows to edit this message once persisted" do
chat_page.visit_channel(channel_1)
channel_page.send_message("aaaaaa")
expect(channel_page.messages).to have_message(persisted: true, text: "aaaaaa")
last_message = find(".chat-message-container:last-child")
last_message.hover
expect(channel_page).to have_css(
".chat-message-actions-container[data-id='#{last_message["data-id"]}']",
)
end
end
context "when clicking the arrow button" do
before { 50.times { Fabricate(:chat_message, chat_channel: channel_1) } }
it "jumps to the bottom of the channel" do
unloaded_message = Fabricate(:chat_message, chat_channel: channel_1)
visit("/chat/c/-/#{channel_1.id}/#{message_1.id}")
expect(channel_page).to have_no_loading_skeleton
expect(page).to have_no_css("[data-id='#{unloaded_message.id}']")
find(".chat-scroll-to-bottom__button.visible").click
expect(channel_page).to have_no_loading_skeleton
expect(page).to have_css("[data-id='#{unloaded_message.id}']")
expect(page).to have_css(".-last-read[data-id='#{unloaded_message.id}']")
end
end
context "when returning to a channel where last read is not last message" do
it "jumps to the bottom of the channel" do
channel_1.membership_for(current_user).update!(last_read_message: message_1)
messages = 50.times.map { Fabricate(:chat_message, chat_channel: channel_1) }
chat_page.visit_channel(channel_1)
expect(page).to have_css("[data-id='#{messages.first.id}']")
expect(page).to have_no_css("[data-id='#{messages.last.id}']")
end
end
context "when a new message is created" do
fab!(:other_user) { Fabricate(:user) }
before do
channel_1.add(other_user)
50.times { Fabricate(:chat_message, chat_channel: channel_1) }
end
xit "doesnt scroll the pane" do
visit("/chat/c/-/#{channel_1.id}/#{message_1.id}")
new_message = Fabricate(:chat_message, chat_channel: channel_1)
expect(page).to have_no_content(new_message.message)
end
end
context "when a message contains mentions" do
fab!(:other_user) { Fabricate(:user) }
fab!(:message) do
Fabricate(
:chat_message,
chat_channel: channel_1,
message: "hello @here @all @#{current_user.username} @#{other_user.username} @unexisting",
user: other_user,
)
end
before do
SiteSetting.enable_user_status = true
current_user.set_status!("off to dentist", "tooth")
other_user.set_status!("surfing", "surfing_man")
channel_1.add(other_user)
end
it "highlights the mentions" do
chat_page.visit_channel(channel_1)
expect(page).to have_selector(".mention.highlighted.valid-mention", text: "@here")
expect(page).to have_selector(".mention.highlighted.valid-mention", text: "@all")
expect(page).to have_selector(
".mention.highlighted.valid-mention",
text: "@#{current_user.username}",
)
expect(page).to have_selector(".mention", text: "@#{other_user.username}")
expect(page).to have_selector(".mention", text: "@unexisting")
end
it "renders user status on mentions" do
Fabricate(:user_chat_mention, user: current_user, chat_message: message)
Fabricate(:user_chat_mention, user: other_user, chat_message: message)
chat_page.visit_channel(channel_1)
expect(page).to have_selector(
".mention .user-status-message img[alt='#{current_user.user_status.emoji}']",
)
expect(page).to have_selector(
".mention .user-status-message img[alt='#{other_user.user_status.emoji}']",
)
end
it "renders user status when expanding collapsed message" do
message_1 =
Fabricate(
:chat_message,
chat_channel: channel_1,
message: "hello @#{other_user.username}",
user: current_user,
)
chat_page.visit_channel(channel_1)
channel_page.messages.delete(message_1)
channel_page.messages.restore(message_1)
expect(page).to have_selector(
".chat-message-container[data-id=\"#{message_1.id}\"] .mention .user-status-message img[alt='#{other_user.user_status.emoji}']",
)
other_user.set_status!("hello", "heart")
expect(page).to have_selector(
".chat-message-container[data-id=\"#{message_1.id}\"] .mention .user-status-message img[alt='#{other_user.user_status.emoji}']",
)
end
end
context "when reply is right under" do
fab!(:other_user) { Fabricate(:user) }
before do
Fabricate(:chat_message, in_reply_to: message_1, user: other_user, chat_channel: channel_1)
channel_1.add(other_user)
end
it "doesnt show the reply-to line" do
chat_page.visit_channel(channel_1)
expect(page).to have_no_selector(".chat-reply__excerpt")
end
end
context "when reply is not directly connected" do
fab!(:other_user) { Fabricate(:user) }
before do
Fabricate(:chat_message, user: other_user, chat_channel: channel_1)
Fabricate(:chat_message, in_reply_to: message_1, user: other_user, chat_channel: channel_1)
channel_1.add(other_user)
end
it "shows the reply-to line" do
chat_page.visit_channel(channel_1)
expect(page).to have_selector(".chat-reply__excerpt")
end
end
context "when replying to message that has HTML tags" do
fab!(:other_user) { Fabricate(:user) }
fab!(:message_2) do
Fabricate(
:chat_message,
user: other_user,
chat_channel: channel_1,
message: "<mark>not marked</mark>",
)
end
before do
Fabricate(:chat_message, user: other_user, chat_channel: channel_1)
Fabricate(:chat_message, in_reply_to: message_2, user: current_user, chat_channel: channel_1)
channel_1.add(other_user)
end
it "renders text in the reply-to" do
chat_page.visit_channel(channel_1)
expect(find(".chat-reply .chat-reply__excerpt")["innerHTML"].strip).to eq(
"&lt;mark&gt;not marked&lt;/mark&gt;",
)
end
it "renders safe HTML like mentions (which are just links) in the reply-to" do
update_message!(
message_2,
user: other_user,
text: "@#{other_user.username} <mark>not marked</mark>",
)
chat_page.visit_channel(channel_1)
expect(find(".chat-reply .chat-reply__excerpt")["innerHTML"].strip).to eq(
"@#{other_user.username} &lt;mark&gt;not marked&lt;/mark&gt;",
)
end
end
context "when messages are separated by a day" do
before { Fabricate(:chat_message, chat_channel: channel_1, created_at: 2.days.ago) }
it "shows a date separator" do
chat_page.visit_channel(channel_1)
expect(page).to have_selector(".chat-message-separator__text", text: "Today")
end
end
context "when a message contains code fence" do
fab!(:message_2) { Fabricate(:chat_message, chat_channel: channel_1, message: <<~MESSAGE) }
Here's a message with code highlighting
\`\`\`ruby
Widget.triangulate(arg: "test")
\`\`\`
MESSAGE
it "adds the correct lang" do
chat_page.visit_channel(channel_1)
expect(page).to have_selector("code.lang-ruby")
end
end
context "when scrolling" do
before { 50.times { Fabricate(:chat_message, chat_channel: channel_1) } }
it "resets the active message" do
chat_page.visit_channel(channel_1)
last_message = find(".chat-message-container:last-child")
last_message.hover
expect(page).to have_css(
".chat-message-actions-container[data-id='#{last_message["data-id"]}']",
)
find(".chat-messages-scroll").scroll_to(0, -1000)
expect(page).to have_no_css(
".chat-message-actions-container[data-id='#{last_message["data-id"]}']",
)
end
end
context "when opening message secondary options" do
it "doesnt hide dropdown on mouseleave" do
chat_page.visit_channel(channel_1)
last_message = find(".chat-message-container:last-child")
last_message.hover
expect(page).to have_css(
".chat-message-actions-container[data-id='#{last_message["data-id"]}']",
)
find(".chat-message-actions-container .secondary-actions").click
expect(page).to have_css(
".chat-message-actions-container .secondary-actions .select-kit-body",
)
find("#site-logo").hover
expect(page).to have_css(
".chat-message-actions-container .secondary-actions .select-kit-body",
)
find("#site-logo").click
expect(page).to have_no_css(
".chat-message-actions-container .secondary-actions .select-kit-body",
)
end
end
end