# frozen_string_literal: true RSpec.describe "AI image caption", type: :system, js: true do fab!(:user) { Fabricate(:admin, refresh_auto_groups: true) } fab!(:non_member_group) { Fabricate(:group) } let(:user_preferences_ai_page) { PageObjects::Pages::UserPreferencesAi.new } let(:topic_page) { PageObjects::Pages::Topic.new } fab!(:topic) fab!(:post) { Fabricate(:post, topic: topic) } let(:composer) { PageObjects::Components::Composer.new } let(:popup) { PageObjects::Components::AiCaptionPopup.new } let(:dialog) { PageObjects::Components::Dialog.new } let(:file_path) { file_from_fixtures("logo.jpg", "images").path } let(:caption) do "The image shows a stylized speech bubble icon with a multicolored border on a black background." end let(:caption_with_attrs) do "#{caption} (#{I18n.t("discourse_ai.ai_helper.image_caption.attribution")})" end before do Group.find_by(id: Group::AUTO_GROUPS[:admins]).add(user) assign_fake_provider_to(:ai_helper_model) assign_fake_provider_to(:ai_helper_image_caption_model) SiteSetting.ai_helper_enabled = true SiteSetting.ai_helper_enabled_features = "image_caption" sign_in(user) end shared_examples "shows no image caption button" do it "should not show an image caption button" do visit("/latest") page.find("#create-topic").click attach_file([file_path]) { composer.click_toolbar_button("upload") } wait_for { composer.has_no_in_progress_uploads? } expect(popup).to have_no_generate_caption_button end end context "when not a member of ai helper group" do before { SiteSetting.composer_ai_helper_allowed_groups = non_member_group.id.to_s } include_examples "shows no image caption button" end context "when image caption feature is disabled" do before { SiteSetting.ai_helper_enabled_features = "" } include_examples "shows no image caption button" end context "when triggering caption with AI on desktop" do it "should show an image caption in an input field" do DiscourseAi::Completions::Llm.with_prepared_responses([caption]) do visit("/latest") page.find("#create-topic").click attach_file([file_path]) { composer.click_toolbar_button("upload") } popup.click_generate_caption expect(popup.has_caption_popup_value?(caption_with_attrs)).to eq(true) popup.save_caption wait_for { page.find(".image-wrapper img")["alt"] == caption_with_attrs } expect(page.find(".image-wrapper img")["alt"]).to eq(caption_with_attrs) end end it "should allow you to cancel a caption request" do DiscourseAi::Completions::Llm.with_prepared_responses([caption]) do visit("/latest") page.find("#create-topic").click attach_file([file_path]) { composer.click_toolbar_button("upload") } popup.click_generate_caption popup.cancel_caption expect(popup).to have_no_disabled_generate_button end end end context "when triggering caption with AI on mobile", mobile: true do it "should show update the image alt text with the caption" do DiscourseAi::Completions::Llm.with_prepared_responses([caption]) do visit("/latest") page.find("#create-topic").click attach_file([file_path]) { page.find(".mobile-file-upload").click } page.find(".mobile-preview").click popup.click_generate_caption wait_for { page.find(".image-wrapper img")["alt"] == caption_with_attrs } expect(page.find(".image-wrapper img")["alt"]).to eq(caption_with_attrs) end end end describe "automatic image captioning" do context "when ai helper is disabled" do before { SiteSetting.ai_helper_enabled = false } it "should not have the setting present in the user preferences page" do user_preferences_ai_page.visit(user) expect(user_preferences_ai_page).to have_no_ai_preference("pref-auto-image-caption") end end context "when toggling the setting from the user preferences page" do before { user.user_option.update!(auto_image_caption: false) } it "should update the preference to enabled" do user_preferences_ai_page.visit(user) user_preferences_ai_page.toggle_setting("pref-auto-image-caption") user_preferences_ai_page.save_changes wait_for(timeout: 5) { user.reload.user_option.auto_image_caption } expect(user.reload.user_option.auto_image_caption).to eq(true) end end context "when the user preference is disabled" do before { user.user_option.update!(auto_image_caption: false) } it "should show a prompt when submitting a post with captionable images uploaded" do visit("/latest") page.find("#create-topic").click attach_file([file_path]) { composer.click_toolbar_button("upload") } wait_for { composer.has_no_in_progress_uploads? } composer.fill_title("I love using Discourse! It is my favorite forum software") composer.create expect(dialog).to be_open end it "should not show a prompt when submitting a post with no captionable images uploaded" do original_file_path = Rails.root.join("spec/fixtures/images/logo.jpg") temp_file_path = Rails.root.join("spec/fixtures/images/An image of Discourse logo.jpg") FileUtils.cp(original_file_path, temp_file_path) visit("/latest") page.find("#create-topic").click attach_file([temp_file_path]) { composer.click_toolbar_button("upload") } wait_for { composer.has_no_in_progress_uploads? } composer.fill_title("I love using Discourse! It is my favorite forum software") composer.create expect(dialog).to be_closed end it "should auto caption the existing images and update the preference when dialog is accepted" do DiscourseAi::Completions::Llm.with_prepared_responses([caption]) do visit("/latest") page.find("#create-topic").click attach_file([file_path]) { composer.click_toolbar_button("upload") } wait_for { composer.has_no_in_progress_uploads? } composer.fill_title("I love using Discourse! It is my favorite forum software") composer.create dialog.click_yes wait_for(timeout: 100) { page.find("#post_1 .cooked img")["alt"] == caption_with_attrs } expect(page.find("#post_1 .cooked img")["alt"]).to eq(caption_with_attrs) end end end context "when the user preference is enabled" do before { user.user_option.update!(auto_image_caption: true) } skip "TODO: Fix auto_image_caption user option not present in testing environment?" do it "should auto caption the image after uploading" do DiscourseAi::Completions::Llm.with_prepared_responses([caption]) do visit("/latest") page.find("#create-topic").click attach_file([Rails.root.join("spec/fixtures/images/logo.jpg")]) do composer.click_toolbar_button("upload") end wait_for { composer.has_no_in_progress_uploads? } wait_for { page.find(".image-wrapper img")["alt"] == caption_with_attrs } expect(page.find(".image-wrapper img")["alt"]).to eq(caption_with_attrs) end end end end context "when a post has no uploads" do before { user.user_option.update!(auto_image_caption: true) } it "should create the topic without triggering auto caption" do title = "I love using Discourse! It is my favorite forum software" visit("/latest") page.find("#create-topic").click composer.fill_title(title) composer.fill_content( "Culpa labore velit cupidatat commodo magna esse et minim consequat veniam dolore eiusmod. Labore eu elit in nulla ipsum elit consectetur sunt consectetur enim. Cillum ex ex velit eiusmod labore ullamco ut ad. Dolore dolor commodo nisi fugiat esse quis anim officia quis. Pariatur cillum pariatur irure cupidatat nostrud ullamco labore id aliqua ut nostrud. Eu adipisicing ut laboris.", ) composer.create expect(dialog).to be_closed expect(topic_page).to have_topic_title(title) end it "should create a reply to a post without triggering auto caption" do visit("/t/-/#{topic.id}") topic_page.click_footer_reply composer.fill_content( "Culpa labore velit cupidatat commodo magna esse et minim consequat veniam dolore eiusmod. Labore eu elit in nulla ipsum elit consectetur sunt consectetur enim. Cillum ex ex velit eiusmod labore ullamco ut ad. Dolore dolor commodo nisi fugiat esse quis anim officia quis. Pariatur cillum pariatur irure cupidatat nostrud ullamco labore id aliqua ut nostrud. Eu adipisicing ut laboris.", ) composer.create expect(dialog).to be_closed expect(topic_page).to have_post_number(post.post_number) end end end end