FIX: editing post while replying (#29985)

BEFORE: if you click the "reply" button on a post and then decided that you want to "edit" the same post, clicking the "edit" button would do nothing. Clicking "edit" on another post works, but editing the same post would appear broken.

AFTER: if you click the "edit" button, it will properly load the content of the post you're trying to edit. No matter which one it is.

This was somewhat tricky to track down as the system specs seemed to contradict the qunit tests until I realized that the qunit tests were only testing the edit on the 1st post and the system specs were testing on replies.

I improved the qunit tests to test both editing OP and a reply and (hopefully) made the system specs a little bit clearer.

This is a follow up to bbe62d88d2.
This commit is contained in:
Régis Hanol 2024-12-02 16:06:36 +01:00 committed by GitHub
parent ab32623c07
commit 9bdd97db42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 70 additions and 51 deletions

View File

@ -848,12 +848,7 @@ export default class TopicController extends Controller.extend(
return false; return false;
} }
const composer = this.composer; const topic = this.model;
let topic = this.model;
const composerModel = composer.get("model");
let editingFirst =
composerModel &&
(post.get("firstPost") || composerModel.get("editingFirstPost"));
let editingSharedDraft = false; let editingSharedDraft = false;
let draftsCategoryId = this.get("site.shared_drafts_category_id"); let draftsCategoryId = this.get("site.shared_drafts_category_id");
@ -872,22 +867,14 @@ export default class TopicController extends Controller.extend(
opts.destinationCategoryId = topic.get("destination_category_id"); opts.destinationCategoryId = topic.get("destination_category_id");
} }
// Reopen the composer if we're editing the same post const { composer } = this;
const editingExisting = const composerModel = composer.get("model");
post.id === composerModel?.post?.id && const editingSamePost =
opts?.action === Composer.EDIT && opts.post.id === composerModel?.post?.id &&
composerModel?.draftKey === opts.draftKey; opts.action === composerModel?.action &&
if (editingExisting) { opts.draftKey === composerModel?.draftKey;
composer.unshrink();
return;
}
// Cancel and reopen the composer for the first post return editingSamePost ? composer.unshrink() : composer.open(opts);
if (editingFirst) {
composer.cancelComposer(opts).then(() => composer.open(opts));
} else {
composer.open(opts);
}
} }
@action @action

View File

@ -576,7 +576,7 @@ acceptance("Composer", function (needs) {
); );
}); });
test("Composer can toggle between edit and reply", async function (assert) { test("Composer can toggle between edit and reply on the OP", async function (assert) {
await visit("/t/this-is-a-test-topic/9"); await visit("/t/this-is-a-test-topic/9");
await click(".topic-post:nth-of-type(1) button.edit"); await click(".topic-post:nth-of-type(1) button.edit");
@ -586,8 +586,10 @@ acceptance("Composer", function (needs) {
/^This is the first post\./, /^This is the first post\./,
"populates the input with the post text" "populates the input with the post text"
); );
await click(".topic-post:nth-of-type(1) button.reply"); await click(".topic-post:nth-of-type(1) button.reply");
assert.dom(".d-editor-input").hasNoValue("clears the input"); assert.dom(".d-editor-input").hasNoValue("clears the composer input");
await click(".topic-post:nth-of-type(1) button.edit"); await click(".topic-post:nth-of-type(1) button.edit");
assert assert
.dom(".d-editor-input") .dom(".d-editor-input")
@ -597,6 +599,27 @@ acceptance("Composer", function (needs) {
); );
}); });
test("Composer can toggle between edit and reply on a reply", async function (assert) {
await visit("/t/this-is-a-test-topic/9");
await click(".topic-post:nth-of-type(2) button.edit");
assert
.dom(".d-editor-input")
.hasValue(
/^This is the second post\./,
"populates the input with the post text"
);
await click(".topic-post:nth-of-type(2) button.reply");
assert.dom(".d-editor-input").hasNoValue("clears the composer input");
await click(".topic-post:nth-of-type(2) button.edit");
assert.true(
query(".d-editor-input").value.startsWith("This is the second post."),
"populates the input with the post text"
);
});
test("Composer can toggle whispers when whisperer user", async function (assert) { test("Composer can toggle whispers when whisperer user", async function (assert) {
const menu = selectKit(".toolbar-popup-menu-options"); const menu = selectKit(".toolbar-popup-menu-options");
@ -802,6 +825,7 @@ acceptance("Composer", function (needs) {
i18n("post.cancel_composer.keep_editing"), i18n("post.cancel_composer.keep_editing"),
"has keep editing button" "has keep editing button"
); );
await click(".d-modal__footer button.save-draft"); await click(".d-modal__footer button.save-draft");
assert.dom(".d-editor-input").hasNoValue("clears the composer input"); assert.dom(".d-editor-input").hasNoValue("clears the composer input");
}); });

View File

@ -1,68 +1,72 @@
# frozen_string_literal: true # frozen_string_literal: true
describe "Composer - discard draft modal", type: :system do describe "Composer - discard draft modal", type: :system do
fab!(:current_user) { Fabricate(:admin) } fab!(:topic)
fab!(:topic_1) { Fabricate(:topic) } fab!(:admin)
let(:topic_page) { PageObjects::Pages::Topic.new } let(:topic_page) { PageObjects::Pages::Topic.new }
let(:discard_draft_modal) { PageObjects::Modals::DiscardDraft.new }
let(:composer) { PageObjects::Components::Composer.new } let(:composer) { PageObjects::Components::Composer.new }
let(:discard_draft_modal) { PageObjects::Modals::DiscardDraft.new }
before { sign_in(current_user) } before { sign_in(admin) }
context "when editing different post" do context "when editing different post" do
fab!(:post_1) { Fabricate(:post, topic: topic_1, user: current_user) } fab!(:post_1) { Fabricate(:post, topic:, user: admin) }
fab!(:post_2) { Fabricate(:post, topic: topic_1, user: current_user) } fab!(:post_2) { Fabricate(:post, topic:, user: admin) }
it "shows the discard modal" do it "shows the discard modal when there are changes in the composer" do
topic_page.visit_topic(post_1.topic) topic_page.visit_topic(post_1.topic)
topic_page.click_post_action_button(post_1, :edit) topic_page.click_post_action_button(post_1, :edit)
composer.fill_content("a b c d e f g") composer.fill_content("a b c d e f g")
composer.minimize composer.minimize
topic_page.click_post_action_button(post_2, :edit) topic_page.click_post_action_button(post_2, :edit)
expect(discard_draft_modal).to be_open expect(discard_draft_modal).to be_open
end end
end
context "when re-editing the first post" do it "doesn't show the discard modal when there are no changes in the composer" do
fab!(:post_1) { Fabricate(:post, topic: topic_1, user: current_user) }
it "doesnt show the discard modal" do
topic_page.visit_topic(post_1.topic) topic_page.visit_topic(post_1.topic)
topic_page.click_post_action_button(post_1, :edit) topic_page.click_post_action_button(post_1, :edit)
composer.fill_content("a b c d e f g")
composer.minimize composer.minimize
topic_page.click_post_action_button(post_1, :edit)
expect(discard_draft_modal).to be_closed topic_page.click_post_action_button(post_2, :edit)
expect(composer).to be_opened
topic_page.click_post_action_button(post_1, :edit)
expect(discard_draft_modal).to be_closed expect(discard_draft_modal).to be_closed
expect(composer).to be_opened expect(composer).to be_opened
end end
end end
context "when re-editing the second post" do context "when editing the same post" do
fab!(:post_1) { Fabricate(:post, topic: topic_1, user: current_user) } fab!(:post_1) { Fabricate(:post, topic:, user: admin) }
fab!(:post_2) { Fabricate(:post, topic: topic_1, user: current_user) }
it "doesnt show the discard modal" do it "doesnt show the discard modal even if there are changes in the composer" do
topic_page.visit_topic(post_1.topic) topic_page.visit_topic(post_1.topic)
topic_page.click_post_action_button(post_2, :edit) topic_page.click_post_action_button(post_1, :edit)
composer.fill_content("a b c d e f g") composer.fill_content("a b c d e f g")
composer.minimize composer.minimize
topic_page.click_post_action_button(post_2, :edit)
topic_page.click_post_action_button(post_1, :edit)
expect(discard_draft_modal).to be_closed expect(discard_draft_modal).to be_closed
expect(composer).to be_opened expect(composer).to be_opened
expect(composer).to have_content("a b c d e f g")
topic_page.click_post_action_button(post_2, :edit) composer.minimize
expect(composer).to be_minimized
topic_page.click_post_action_button(post_1, :edit)
expect(discard_draft_modal).to be_closed
expect(composer).to be_opened
end
it "doesnt show the discard modal when there are no changes in the composer" do
topic_page.visit_topic(post_1.topic)
topic_page.click_post_action_button(post_1, :edit)
composer.minimize
topic_page.click_post_action_button(post_1, :edit)
expect(discard_draft_modal).to be_closed expect(discard_draft_modal).to be_closed
expect(composer).to be_opened expect(composer).to be_opened

View File

@ -14,6 +14,10 @@ module PageObjects
page.has_css?("#{COMPOSER_ID}.closed", visible: :all) page.has_css?("#{COMPOSER_ID}.closed", visible: :all)
end end
def minimized?
page.has_css?("#{COMPOSER_ID}.draft")
end
def open_composer_actions def open_composer_actions
find(".composer-action-title .btn").click find(".composer-action-title .btn").click
self self