mirror of
https://github.com/discourse/discourse-ai.git
synced 2025-07-08 23:32:45 +00:00
We were getting an error in this logic causing Ember to fail to render the non-bot-topic that we navigate to. I believe this is because the getter of participants is re-calculating (due to this.header.topicInfo being updated) before the args to this connector changes. Adding some safe navigation here fixes the issue.
424 lines
14 KiB
Ruby
424 lines
14 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe "AI Bot - Homepage", type: :system do
|
|
let(:topic_page) { PageObjects::Pages::Topic.new }
|
|
let(:composer) { PageObjects::Components::Composer.new }
|
|
let(:ai_pm_homepage) { PageObjects::Components::AiPmHomepage.new }
|
|
let(:header) { PageObjects::Pages::DiscourseAi::Header.new }
|
|
let(:sidebar) { PageObjects::Components::NavigationMenu::Sidebar.new }
|
|
let(:header_dropdown) { PageObjects::Components::NavigationMenu::HeaderDropdown.new }
|
|
let(:dialog) { PageObjects::Components::Dialog.new }
|
|
|
|
fab!(:user) { Fabricate(:user, refresh_auto_groups: true) }
|
|
fab!(:user_2) { Fabricate(:user, refresh_auto_groups: true) }
|
|
|
|
fab!(:claude_2) do
|
|
Fabricate(
|
|
:llm_model,
|
|
provider: "anthropic",
|
|
url: "https://api.anthropic.com/v1/messages",
|
|
name: "claude-2",
|
|
display_name: "Claude 2",
|
|
)
|
|
end
|
|
fab!(:claude_2_dup) do
|
|
Fabricate(
|
|
:llm_model,
|
|
provider: "anthropic",
|
|
url: "https://api.anthropic.com/v1/messages",
|
|
name: "claude-2",
|
|
display_name: "Duplicate",
|
|
)
|
|
end
|
|
fab!(:bot_user) do
|
|
toggle_enabled_bots(bots: [claude_2, claude_2_dup])
|
|
SiteSetting.ai_bot_enabled = true
|
|
claude_2.reload.user
|
|
end
|
|
fab!(:bot) do
|
|
persona =
|
|
AiPersona
|
|
.find(DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::General])
|
|
.class_instance
|
|
.new
|
|
DiscourseAi::Personas::Bot.as(bot_user, persona: persona)
|
|
end
|
|
|
|
fab!(:pm) do
|
|
Fabricate(
|
|
:private_message_topic,
|
|
title: "This is my special PM",
|
|
user: user,
|
|
last_posted_at: Time.zone.now,
|
|
topic_allowed_users: [
|
|
Fabricate.build(:topic_allowed_user, user: user),
|
|
Fabricate.build(:topic_allowed_user, user: bot_user),
|
|
],
|
|
)
|
|
end
|
|
fab!(:first_post) do
|
|
Fabricate(:post, topic: pm, user: user, post_number: 1, raw: "This is a reply by the user")
|
|
end
|
|
fab!(:second_post) do
|
|
Fabricate(:post, topic: pm, user: bot_user, post_number: 2, raw: "This is a bot reply")
|
|
end
|
|
fab!(:third_post) do
|
|
Fabricate(
|
|
:post,
|
|
topic: pm,
|
|
user: user,
|
|
post_number: 3,
|
|
raw: "This is a second reply by the user",
|
|
)
|
|
end
|
|
fab!(:topic_user) { Fabricate(:topic_user, topic: pm, user: user) }
|
|
fab!(:topic_bot_user) { Fabricate(:topic_user, topic: pm, user: bot_user) }
|
|
|
|
fab!(:persona) do
|
|
persona =
|
|
AiPersona.create!(
|
|
name: "Test Persona",
|
|
description: "A test persona",
|
|
allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]],
|
|
enabled: true,
|
|
system_prompt: "You are a helpful bot",
|
|
)
|
|
|
|
persona.create_user!
|
|
persona.update!(
|
|
default_llm_id: claude_2.id,
|
|
allow_chat_channel_mentions: true,
|
|
allow_topic_mentions: true,
|
|
)
|
|
persona
|
|
end
|
|
|
|
before do
|
|
pm.custom_fields[DiscourseAi::AiBot::TOPIC_AI_BOT_PM_FIELD] = "t"
|
|
pm.save!
|
|
|
|
SiteSetting.ai_bot_enable_dedicated_ux = true
|
|
SiteSetting.ai_bot_enabled = true
|
|
SiteSetting.navigation_menu = "sidebar"
|
|
Jobs.run_immediately!
|
|
SiteSetting.ai_bot_allowed_groups = "#{Group::AUTO_GROUPS[:trust_level_0]}"
|
|
sign_in(user)
|
|
end
|
|
|
|
%w[enabled disabled].each do |value|
|
|
before { SiteSetting.glimmer_post_stream_mode = value }
|
|
|
|
context "when glimmer_post_stream_mode=#{value}" do
|
|
context "when mobile", mobile: true do
|
|
it "allows navigating from AI conversation to regular topic, and loads new post stream" do
|
|
regular_topic = Fabricate(:topic)
|
|
regular_post = Fabricate(:post, topic: regular_topic)
|
|
|
|
post_url = Topic.relative_url(regular_topic.id, regular_topic.slug)
|
|
post_with_link =
|
|
Fabricate(
|
|
:post,
|
|
topic: pm,
|
|
user: user,
|
|
post_number: 4,
|
|
raw: "This is a second reply by the user [link](#{post_url})",
|
|
)
|
|
|
|
topic_page.visit_topic(pm)
|
|
page.find(
|
|
"article[data-post-id='#{post_with_link.id}'] .cooked a[href='#{post_url}']",
|
|
).click
|
|
|
|
try_until_success do
|
|
expect(topic_page.current_topic).to eq(regular_topic)
|
|
expect(page).to have_css("article[data-post-id='#{regular_post.id}']")
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when `ai_bot_enable_dedicated_ux` is enabled" do
|
|
it "allows uploading files to a new conversation" do
|
|
ai_pm_homepage.visit
|
|
expect(ai_pm_homepage).to have_homepage
|
|
|
|
file_path_1 = file_from_fixtures("logo.png", "images").path
|
|
file_path_2 = file_from_fixtures("logo.jpg", "images").path
|
|
|
|
attach_file([file_path_1, file_path_2]) do
|
|
find(".ai-bot-upload-btn", visible: true).click
|
|
end
|
|
|
|
expect(page).to have_css(".ai-bot-upload", count: 2)
|
|
|
|
ai_pm_homepage.input.fill_in(with: "Here are two image attachments")
|
|
|
|
responses = ["hello user", "topic title"]
|
|
DiscourseAi::Completions::Llm.with_prepared_responses(responses) do
|
|
ai_pm_homepage.submit
|
|
expect(topic_page).to have_content("Here are two image attachments")
|
|
expect(page).to have_css(".cooked img", count: 2)
|
|
end
|
|
|
|
find(".ai-new-question-button").click
|
|
expect(ai_pm_homepage).to have_homepage
|
|
expect(page).to have_no_css(".ai-bot-upload")
|
|
end
|
|
|
|
it "allows removing an upload before submission" do
|
|
ai_pm_homepage.visit
|
|
expect(ai_pm_homepage).to have_homepage
|
|
|
|
file_path = file_from_fixtures("logo.png", "images").path
|
|
attach_file([file_path]) { find(".ai-bot-upload-btn", visible: true).click }
|
|
expect(page).to have_css(".ai-bot-upload", count: 1)
|
|
|
|
find(".ai-bot-upload__remove").click
|
|
|
|
expect(page).to have_no_css(".ai-bot-upload")
|
|
|
|
ai_pm_homepage.input.fill_in(with: "Message without attachments")
|
|
|
|
responses = ["hello user", "topic title"]
|
|
DiscourseAi::Completions::Llm.with_prepared_responses(responses) do
|
|
ai_pm_homepage.submit
|
|
expect(topic_page).to have_content("Message without attachments")
|
|
expect(page).to have_no_css(".cooked img")
|
|
end
|
|
end
|
|
|
|
it "renders landing page on bot click" do
|
|
visit "/"
|
|
header.click_bot_button
|
|
expect(ai_pm_homepage).to have_homepage
|
|
expect(sidebar).to be_visible
|
|
end
|
|
|
|
it "displays error when message is too short" do
|
|
visit "/"
|
|
header.click_bot_button
|
|
|
|
ai_pm_homepage.input.fill_in(with: "a")
|
|
ai_pm_homepage.submit
|
|
expect(ai_pm_homepage).to have_too_short_dialog
|
|
dialog.click_yes
|
|
expect(composer).to be_closed
|
|
end
|
|
|
|
it "hides default content in the sidebar" do
|
|
visit "/"
|
|
header.click_bot_button
|
|
|
|
expect(ai_pm_homepage).to have_homepage
|
|
expect(sidebar).to have_no_tags_section
|
|
expect(sidebar).to have_no_section("categories")
|
|
expect(sidebar).to have_no_section("messages")
|
|
expect(sidebar).to have_no_section("chat-dms")
|
|
expect(sidebar).to have_no_section("chat-channels")
|
|
expect(sidebar).to have_no_section("user-threads")
|
|
end
|
|
|
|
it "shows the bot conversation in the sidebar" do
|
|
visit "/"
|
|
header.click_bot_button
|
|
|
|
expect(ai_pm_homepage).to have_homepage
|
|
expect(sidebar).to have_section("ai-conversations-history")
|
|
expect(sidebar).to have_section_link("Today")
|
|
expect(sidebar).to have_section_link(pm.title)
|
|
end
|
|
|
|
it "displays last_7_days label in the sidebar" do
|
|
pm.update!(last_posted_at: Time.zone.now - 5.days)
|
|
visit "/"
|
|
header.click_bot_button
|
|
|
|
expect(ai_pm_homepage).to have_homepage
|
|
expect(sidebar).to have_section_link("Last 7 days")
|
|
end
|
|
|
|
it "displays last_30_days label in the sidebar" do
|
|
pm.update!(last_posted_at: Time.zone.now - 28.days)
|
|
visit "/"
|
|
header.click_bot_button
|
|
|
|
expect(ai_pm_homepage).to have_homepage
|
|
expect(sidebar).to have_section_link("Last 30 days")
|
|
end
|
|
|
|
it "displays month and year label in the sidebar for older conversations" do
|
|
pm.update!(last_posted_at: "2024-04-10 15:39:11.406192000 +00:00")
|
|
visit "/"
|
|
header.click_bot_button
|
|
|
|
expect(ai_pm_homepage).to have_homepage
|
|
expect(sidebar).to have_section_link("Apr 2024")
|
|
end
|
|
|
|
it "navigates to the bot conversation when clicked" do
|
|
visit "/"
|
|
header.click_bot_button
|
|
|
|
expect(ai_pm_homepage).to have_homepage
|
|
ai_pm_homepage.click_fist_sidebar_conversation
|
|
expect(topic_page).to have_topic_title(pm.title)
|
|
end
|
|
|
|
it "displays the shuffle icon when on homepage or bot PM" do
|
|
visit "/"
|
|
expect(header).to have_icon_in_bot_button(icon: "robot")
|
|
header.click_bot_button
|
|
|
|
expect(header).to have_icon_in_bot_button(icon: "shuffle")
|
|
|
|
# Go to a PM and assert that the icon is still shuffle
|
|
ai_pm_homepage.click_fist_sidebar_conversation
|
|
expect(header).to have_icon_in_bot_button(icon: "shuffle")
|
|
|
|
# Go back home and assert that the icon is now robot again
|
|
header.click_bot_button
|
|
expect(header).to have_icon_in_bot_button(icon: "robot")
|
|
end
|
|
|
|
it "displays 'new question' button on homepage and topic page" do
|
|
topic_page.visit_topic(pm)
|
|
expect(ai_pm_homepage).to have_new_question_button
|
|
|
|
ai_pm_homepage.visit
|
|
expect(ai_pm_homepage).to have_new_question_button
|
|
end
|
|
|
|
it "redirect to the homepage when 'new question' is clicked" do
|
|
topic_page.visit_topic(pm)
|
|
expect(sidebar).to be_visible
|
|
ai_pm_homepage.click_new_question_button
|
|
expect(ai_pm_homepage).to have_homepage
|
|
end
|
|
|
|
it "can send a new message to the bot" do
|
|
topic_page.visit_topic(pm)
|
|
topic_page.click_reply_button
|
|
expect(composer).to be_opened
|
|
|
|
composer.fill_in(with: "Hello bot replying to you")
|
|
composer.submit
|
|
expect(page).to have_content("Hello bot replying to you")
|
|
end
|
|
|
|
it "does not render custom sidebar on non-authored bot pms" do
|
|
# Include user_2 in the PM by creating a new post and topic_allowed_user association
|
|
Fabricate(:post, topic: pm, user: user_2, post_number: 4)
|
|
Fabricate(:topic_allowed_user, topic: pm, user: user_2)
|
|
sign_in(user_2)
|
|
|
|
topic_page.visit_topic(pm)
|
|
|
|
expect(sidebar).to be_visible
|
|
expect(sidebar).to have_no_section("ai-conversations-history")
|
|
expect(sidebar).to have_no_css("button.ai-new-question-button")
|
|
end
|
|
|
|
it "does not include non-authored bot pms in sidebar" do
|
|
# Include user_2 in the PM by creating a new post and topic_allowed_user association
|
|
Fabricate(:post, topic: pm, user: user_2, post_number: 4)
|
|
Fabricate(:topic_allowed_user, topic: pm, user: user_2)
|
|
sign_in(user_2)
|
|
visit "/"
|
|
header.click_bot_button
|
|
expect(ai_pm_homepage).to have_homepage
|
|
expect(sidebar).to have_no_section_link(pm.title)
|
|
end
|
|
|
|
it "renders empty state in sidebar with no bot PM history" do
|
|
sign_in(user_2)
|
|
ai_pm_homepage.visit
|
|
expect(ai_pm_homepage).to have_empty_state
|
|
end
|
|
|
|
it "Allows choosing persona and LLM" do
|
|
ai_pm_homepage.visit
|
|
|
|
ai_pm_homepage.llm_selector.expand
|
|
ai_pm_homepage.llm_selector.select_row_by_name(claude_2_dup.display_name)
|
|
ai_pm_homepage.llm_selector.collapse
|
|
|
|
# confirm memory works for llm selection
|
|
ai_pm_homepage.visit
|
|
expect(ai_pm_homepage.llm_selector).to have_selected_name(claude_2_dup.display_name)
|
|
end
|
|
|
|
it "does not render back to forum link" do
|
|
ai_pm_homepage.visit
|
|
expect(ai_pm_homepage).to have_no_sidebar_back_link
|
|
end
|
|
|
|
context "with hamburger menu" do
|
|
before { SiteSetting.navigation_menu = "header dropdown" }
|
|
it "keeps robot icon in the header and doesn't display sidebar back link" do
|
|
visit "/"
|
|
expect(header).to have_icon_in_bot_button(icon: "robot")
|
|
header.click_bot_button
|
|
expect(ai_pm_homepage).to have_homepage
|
|
expect(header).to have_icon_in_bot_button(icon: "robot")
|
|
expect(ai_pm_homepage).to have_no_sidebar_back_link
|
|
end
|
|
|
|
it "still renders the sidebar" do
|
|
visit "/"
|
|
header.click_bot_button
|
|
expect(ai_pm_homepage).to have_homepage
|
|
expect(sidebar).to be_visible
|
|
expect(header_dropdown).to be_visible
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when `ai_bot_enable_dedicated_ux` is disabled" do
|
|
before { SiteSetting.ai_bot_enable_dedicated_ux = false }
|
|
|
|
it "opens composer on bot click" do
|
|
visit "/"
|
|
header.click_bot_button
|
|
|
|
expect(ai_pm_homepage).to have_no_homepage
|
|
expect(composer).to be_opened
|
|
end
|
|
|
|
it "does not render sidebar when navigation menu is set to header on pm" do
|
|
SiteSetting.navigation_menu = "header dropdown"
|
|
topic_page.visit_topic(pm)
|
|
|
|
expect(ai_pm_homepage).to have_no_homepage
|
|
expect(sidebar).to be_not_visible
|
|
expect(header_dropdown).to be_visible
|
|
end
|
|
|
|
it "shows default content in the sidebar" do
|
|
topic_page.visit_topic(pm)
|
|
|
|
expect(sidebar).to have_section("categories")
|
|
expect(sidebar).to have_section("messages")
|
|
expect(sidebar).to have_section("chat-dms")
|
|
expect(sidebar).to have_no_css("button.ai-new-question-button")
|
|
end
|
|
end
|
|
|
|
context "with header dropdown on mobile", mobile: true do
|
|
before { SiteSetting.navigation_menu = "header dropdown" }
|
|
|
|
it "displays the new question button in the menu when viewing a PM" do
|
|
ai_pm_homepage.visit
|
|
header_dropdown.open
|
|
expect(ai_pm_homepage).to have_new_question_button
|
|
|
|
topic_page.visit_topic(pm)
|
|
header_dropdown.open
|
|
ai_pm_homepage.click_new_question_button
|
|
|
|
# Hamburger sidebar is closed
|
|
expect(header_dropdown).to have_no_dropdown_visible
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|