UX: Move AI bot PM to navigation menu (#35189)

The header space is becoming very crowded with translations, chat, and
AI bot all enabled.
This commit makes the new default for AI conversations a link in the
community section in the sidebar instead of a header button.


<img width="1412" height="246" alt="CleanShot 2025-10-04 at 15 44 05@2x"
src="https://github.com/user-attachments/assets/45c48607-bbaa-4993-9e92-bb8db2d7f45a"
/>
<img width="478" height="650" alt="CleanShot 2025-10-04 at 15 44 32@2x"
src="https://github.com/user-attachments/assets/67af3afa-6a46-4c79-8aa9-fc789a086056"
/>

There is also a new back-to-forum button added in, conform with other
custom-sidebar pages such as /admin and /docs.
<img width="1966" height="1004" alt="CleanShot 2025-10-04 at 15 45
04@2x"
src="https://github.com/user-attachments/assets/5b167aba-9305-476f-a449-ec4a186bc6f7"
/>

---------

Co-authored-by: awesomerobot <kris.aubuchon@discourse.org>
This commit is contained in:
chapoi
2025-10-20 09:15:51 -04:00
committed by GitHub
co-authored by awesomerobot
parent 1522c181e8
commit 33e79f6272
11 changed files with 500 additions and 281 deletions
@@ -2,6 +2,7 @@ import Component from "@glimmer/component";
import { action } from "@ember/object";
import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import BackToForum from "discourse/components/sidebar/back-to-forum";
import { AI_CONVERSATIONS_PANEL } from "../services/ai-conversations-sidebar-manager";
const TEXTAREA_ID = "ai-bot-conversations-input";
@@ -10,11 +11,16 @@ export default class AiBotSidebarNewConversation extends Component {
@service appEvents;
@service router;
@service sidebarState;
@service siteSettings;
get shouldRender() {
return this.sidebarState.isCurrentPanel(AI_CONVERSATIONS_PANEL);
}
get shouldShowBackLink() {
return !this.siteSettings.ai_bot_add_to_header;
}
@action
focusTextarea() {
document.getElementById(TEXTAREA_ID)?.focus();
@@ -35,6 +41,9 @@ export default class AiBotSidebarNewConversation extends Component {
<template>
{{#if this.shouldRender}}
{{#if this.shouldShowBackLink}}
<BackToForum />
{{/if}}
<div class="ai-new-question-button__wrapper">
<DButton
@label="discourse_ai.ai_bot.conversations.new"
@@ -0,0 +1,39 @@
import { withPluginApi } from "discourse/lib/plugin-api";
import { i18n } from "discourse-i18n";
export default {
name: "add-ai-bot-to-community-section",
initialize(container) {
const siteSettings = container.lookup("service:site-settings");
const currentUser = container.lookup("service:current-user");
if (
!currentUser ||
!siteSettings.ai_bot_add_to_community_section ||
!currentUser.ai_enabled_chat_bots?.length
) {
return;
}
const availableBots = currentUser.ai_enabled_chat_bots.filter(
(bot) => !bot.is_persona || bot.has_default_llm
);
if (availableBots.length === 0) {
return;
}
withPluginApi((api) => {
api.addCommunitySectionLink((baseSectionLink) => {
return class AiBotSectionLink extends baseSectionLink {
name = "ai-bot";
route = "discourse-ai-bot-conversations";
text = i18n("discourse_ai.ai_bot.shortcut_link");
title = i18n("discourse_ai.ai_bot.shortcut_title");
defaultPrefixValue = "robot";
};
});
});
},
};
@@ -13,7 +13,7 @@ function ensureBotMap() {
"service:current-user"
);
enabledChatBotMap = {};
currentUser.ai_enabled_chat_bots.forEach((bot) => {
currentUser?.ai_enabled_chat_bots?.forEach((bot) => {
enabledChatBotMap[bot.id] = bot;
});
}
@@ -6,12 +6,21 @@
}
body.has-ai-conversations-sidebar {
.sidebar-sections__back-to-forum {
order: -1;
padding: var(--space-6) var(--space-4) var(--space-3);
+ .ai-new-question-button__wrapper {
margin-top: 0;
}
}
.ai-new-question-button {
width: 100%;
&__wrapper {
background: var(--secondary);
margin: 1.8em 1em 0;
margin: var(--space-6) var(--space-4) 0;
.mobile-view & {
padding: 1em;
@@ -909,6 +909,7 @@ en:
cancel_streaming: "Stop reply"
default_pm_prefix: "[Untitled AI bot PM]"
shortcut_title: "Start a PM with an AI bot"
shortcut_link: "AI bot"
exit: "exit AI bot"
share: "Copy AI conversation"
conversation_shared: "Conversation copied"
@@ -121,7 +121,8 @@ en:
ai_bot_allowed_groups: "When the GPT Bot has access to the PM, it will reply to members of these groups."
ai_bot_debugging_allowed_groups: "Allow these groups to see a debug button on posts which displays the raw AI request and response"
ai_bot_public_sharing_allowed_groups: "Allow these groups to share AI personal messages with the public via a unique publicly available link. Note: if your site requires login, shares will also require login."
ai_bot_add_to_header: "Display a button in the header to start a PM with a AI Bot"
ai_bot_add_to_header: "Display a button in the header to start a PM with an AI Bot"
ai_bot_add_to_community_section: "Display a link in the sidebar community section to start a PM with an AI Bot"
ai_bot_github_access_token: "GitHub access token for use with GitHub AI tools (required for search support)"
ai_discover_enabled: "Enable the discovery search feature"
+4
View File
@@ -389,6 +389,10 @@ discourse_ai:
refresh: true
area: "ai-features/bot"
ai_bot_add_to_header:
default: false
client: true
area: "ai-features/bot"
ai_bot_add_to_community_section:
default: true
client: true
area: "ai-features/bot"
@@ -9,6 +9,7 @@ RSpec.describe "AI chat channel summarization", type: :system do
before do
enable_current_plugin
SiteSetting.ai_bot_enabled = true
SiteSetting.ai_bot_add_to_header = true
toggle_enabled_bots(bots: [gpt_4, gpt_3_5_turbo])
SiteSetting.ai_bot_allowed_groups = group.id.to_s
sign_in(user)
@@ -137,293 +137,302 @@ RSpec.describe "AI Bot - Homepage", type: :system do
end
end
it "allows uploading files to a new conversation" do
ai_pm_homepage.visit
expect(ai_pm_homepage).to have_homepage
context "when `ai_bot_enable_dedicated_ux` is enabled" do
before { SiteSetting.ai_bot_add_to_header = true }
file_path_1 = file_from_fixtures("logo.png", "images").path
file_path_2 = file_from_fixtures("logo.jpg", "images").path
attach_file("ai-bot-file-uploader", [file_path_1, file_path_2], make_visible: true)
expect(page).to have_css(".ai-bot-upload", count: 2)
ai_pm_homepage.input.fill_in(with: "Here are two image attachments")
expect(page).to have_no_css(".ai-bot-upload--in-progress")
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 "shows an error when trying to submit while uploads are in progress" 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
ai_pm_homepage.input.fill_in(with: "Some message to send to AI with uploads")
cdp.with_slow_upload do
attach_file("ai-bot-file-uploader", [file_path_1, file_path_2], make_visible: true)
expect(page).to have_css(".ai-bot-upload--in-progress", count: 2)
ai_pm_homepage.submit
expect(page).to have_content(
I18n.t("js.discourse_ai.ai_bot.conversations.uploads_in_progress"),
)
end
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("ai-bot-file-uploader", file_path, make_visible: true)
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("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("Today")
expect(sidebar).to have_section_link(pm.title)
end
it "allows navigating to a specific LLM and persona" do
# url encode name
persona_name = CGI.escape(persona.name)
llm_name = CGI.escape(claude_2_dup.display_name)
visit "/discourse-ai/ai-bot/conversations?persona=#{persona_name}&llm=#{llm_name}"
ai_pm_homepage.persona_selector.expand # not needed, but helps to see what the list has
expect(ai_pm_homepage.persona_selector).to have_selected_name(persona.name)
expect(ai_pm_homepage.llm_selector).to have_selected_name(claude_2_dup.display_name)
end
it "removes persona from selector when allow_personal_messages is disabled" do
begin
persona.update!(allow_personal_messages: false)
it "allows uploading files to a new conversation" do
ai_pm_homepage.visit
ai_pm_homepage.persona_selector.expand
expect(ai_pm_homepage.persona_selector).to have_no_option_name(persona.name)
ensure
persona.update!(allow_personal_messages: true)
end
end
it "includes persona in selector when allow_personal_messages is enabled" do
# default is true
ai_pm_homepage.visit
ai_pm_homepage.persona_selector.expand
expect(ai_pm_homepage.persona_selector).to have_option_name(persona.name)
end
it "shows empty state when no PMs exist" do
pm.destroy!
visit "/"
header.click_bot_button
expect(page).to have_css(".sidebar-section .ai-bot-sidebar-empty-state", visible: true)
end
it "doesn't show empty state when a PM exists" do
visit "/"
header.click_bot_button
expect(page).to have_no_css(".sidebar-section .ai-bot-sidebar-empty-state")
end
it "displays last_7_days label in the sidebar" do
pm.update!(last_posted_at: 5.days.ago)
visit "/"
header.click_bot_button
expect(ai_pm_homepage).to have_homepage
expect(sidebar).to have_section("Last 7 days")
end
it "displays last_30_days label in the sidebar" do
pm.update!(last_posted_at: 28.days.ago)
visit "/"
header.click_bot_button
expect(ai_pm_homepage).to have_homepage
expect(sidebar).to have_section("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("2024-3")
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 "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
file_path_1 = file_from_fixtures("logo.png", "images").path
file_path_2 = file_from_fixtures("logo.jpg", "images").path
attach_file("ai-bot-file-uploader", [file_path_1, file_path_2], make_visible: true)
expect(page).to have_css(".ai-bot-upload", count: 2)
ai_pm_homepage.input.fill_in(with: "Here are two image attachments")
expect(page).to have_no_css(".ai-bot-upload--in-progress")
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 "still renders the sidebar" do
it "shows an error when trying to submit while uploads are in progress" 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
ai_pm_homepage.input.fill_in(with: "Some message to send to AI with uploads")
cdp.with_slow_upload do
attach_file("ai-bot-file-uploader", [file_path_1, file_path_2], make_visible: true)
expect(page).to have_css(".ai-bot-upload--in-progress", count: 2)
ai_pm_homepage.submit
expect(page).to have_content(
I18n.t("js.discourse_ai.ai_bot.conversations.uploads_in_progress"),
)
end
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("ai-bot-file-uploader", file_path, make_visible: true)
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
expect(header_dropdown).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("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("Today")
expect(sidebar).to have_section_link(pm.title)
end
it "allows navigating to a specific LLM and persona" do
# url encode name
persona_name = CGI.escape(persona.name)
llm_name = CGI.escape(claude_2_dup.display_name)
visit "/discourse-ai/ai-bot/conversations?persona=#{persona_name}&llm=#{llm_name}"
ai_pm_homepage.persona_selector.expand # not needed, but helps to see what the list has
expect(ai_pm_homepage.persona_selector).to have_selected_name(persona.name)
expect(ai_pm_homepage.llm_selector).to have_selected_name(claude_2_dup.display_name)
end
it "removes persona from selector when allow_personal_messages is disabled" do
begin
persona.update!(allow_personal_messages: false)
ai_pm_homepage.visit
ai_pm_homepage.persona_selector.expand
expect(ai_pm_homepage.persona_selector).to have_no_option_name(persona.name)
ensure
persona.update!(allow_personal_messages: true)
end
end
it "includes persona in selector when allow_personal_messages is enabled" do
# default is true
ai_pm_homepage.visit
ai_pm_homepage.persona_selector.expand
expect(ai_pm_homepage.persona_selector).to have_option_name(persona.name)
end
it "shows empty state when no PMs exist" do
pm.destroy!
visit "/"
header.click_bot_button
expect(page).to have_css(".sidebar-section .ai-bot-sidebar-empty-state", visible: true)
end
it "doesn't show empty state when a PM exists" do
visit "/"
header.click_bot_button
expect(page).to have_no_css(".sidebar-section .ai-bot-sidebar-empty-state")
end
it "displays last_7_days label in the sidebar" do
pm.update!(last_posted_at: 5.days.ago)
visit "/"
header.click_bot_button
expect(ai_pm_homepage).to have_homepage
expect(sidebar).to have_section("Last 7 days")
end
it "displays last_30_days label in the sidebar" do
pm.update!(last_posted_at: 28.days.ago)
visit "/"
header.click_bot_button
expect(ai_pm_homepage).to have_homepage
expect(sidebar).to have_section("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("2024-3")
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 "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 "renders back to forum link when header button is disabled" do
SiteSetting.ai_bot_add_to_header = false
ai_pm_homepage.visit
expect(ai_pm_homepage).to have_sidebar_back_link
end
context "with hamburger menu" do
before do
SiteSetting.navigation_menu = "header dropdown"
SiteSetting.ai_bot_add_to_header = true
end
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
@@ -7,6 +7,7 @@ RSpec.describe "AI personas", type: :system do
before do
enable_current_plugin
SiteSetting.ai_bot_enabled = true
SiteSetting.ai_bot_add_to_header = true
toggle_enabled_bots(bots: [gpt_4])
sign_in(admin)
end
@@ -0,0 +1,145 @@
import { visit } from "@ember/test-helpers";
import { test } from "qunit";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
acceptance("AI Bot - Sidebar community link", function (needs) {
needs.user({
ai_enabled_chat_bots: [
{
id: 1,
model_name: "gpt-4",
is_persona: false,
},
],
});
needs.settings({
discourse_ai_enabled: true,
ai_bot_enabled: true,
ai_bot_add_to_community_section: true,
});
test("displays AI bot link in community section when enabled", async function (assert) {
await visit("/");
assert
.dom(".sidebar-section-link[data-link-name='ai-bot']")
.exists("AI bot link is displayed in the sidebar");
assert
.dom(".sidebar-section-link[data-link-name='ai-bot'] .d-icon-robot")
.exists("AI bot link has robot icon");
assert
.dom(".sidebar-section-link[data-link-name='ai-bot']")
.hasText("AI bot", "AI bot link has correct text");
});
});
acceptance("AI Bot - Sidebar community link - disabled", function (needs) {
needs.user({
ai_enabled_chat_bots: [
{
id: 1,
model_name: "gpt-4",
is_persona: false,
},
],
});
needs.settings({
discourse_ai_enabled: true,
ai_bot_enabled: true,
ai_bot_add_to_community_section: false,
});
test("does not display AI bot link when setting is disabled", async function (assert) {
await visit("/");
assert
.dom(".sidebar-section-link[data-link-name='ai-bot']")
.doesNotExist("AI bot link is not displayed when setting is disabled");
});
});
acceptance("AI Bot - Sidebar community link - no bots", function (needs) {
needs.user({
ai_enabled_chat_bots: [],
});
needs.settings({
discourse_ai_enabled: true,
ai_bot_enabled: true,
ai_bot_add_to_community_section: true,
});
test("does not display AI bot link when no bots are available", async function (assert) {
await visit("/");
assert
.dom(".sidebar-section-link[data-link-name='ai-bot']")
.doesNotExist("AI bot link is not displayed when no bots are available");
});
});
acceptance(
"AI Bot - Sidebar community link - persona without default LLM",
function (needs) {
needs.user({
ai_enabled_chat_bots: [
{
id: 1,
model_name: "custom-persona",
is_persona: true,
has_default_llm: false,
},
],
});
needs.settings({
discourse_ai_enabled: true,
ai_bot_enabled: true,
ai_bot_add_to_community_section: true,
});
test("does not display AI bot link when persona has no default LLM", async function (assert) {
await visit("/");
assert
.dom(".sidebar-section-link[data-link-name='ai-bot']")
.doesNotExist(
"AI bot link is not displayed when persona lacks default LLM"
);
});
}
);
acceptance(
"AI Bot - Sidebar community link - persona with default LLM",
function (needs) {
needs.user({
ai_enabled_chat_bots: [
{
id: 1,
model_name: "custom-persona",
is_persona: true,
has_default_llm: true,
},
],
});
needs.settings({
discourse_ai_enabled: true,
ai_bot_enabled: true,
ai_bot_add_to_community_section: true,
});
test("displays AI bot link when persona has default LLM", async function (assert) {
await visit("/");
assert
.dom(".sidebar-section-link[data-link-name='ai-bot']")
.exists("AI bot link is displayed when persona has default LLM");
});
}
);