diff --git a/app/javascript/controllers/clicker_controller.js b/app/javascript/controllers/clicker_controller.js index 6d35719425..70a7575a88 100644 --- a/app/javascript/controllers/clicker_controller.js +++ b/app/javascript/controllers/clicker_controller.js @@ -3,8 +3,12 @@ import { nextFrame } from "helpers/timing_helpers"; export default class extends Controller { static targets = [ "clickable" ] + static outlets = [ "auto-save" ] async click() { + if (this.hasAutoSaveOutlet) { + await this.autoSaveOutlet.submit() + } await nextFrame() this.#clickable.click() } diff --git a/app/views/cards/container/footer/_create.html.erb b/app/views/cards/container/footer/_create.html.erb index 4e5a83e205..fae4e47ce4 100644 --- a/app/views/cards/container/footer/_create.html.erb +++ b/app/views/cards/container/footer/_create.html.erb @@ -3,13 +3,13 @@ <%= button_to card_publish_path(card), name: "creation_type", value: "add", class: "btn", title: "Create card (#{ hotkey_label(["ctrl", "enter"]) })", form: { data: { controller: "form bridge--form" } }, - data: { form_target: "submit", bridge__form_target: "submit", controller: "clicker", action: "keydown.ctrl+enter@document->clicker#click keydown.meta+enter@document->clicker#click" } do %> + data: { form_target: "submit", bridge__form_target: "submit", controller: "clicker", clicker_auto_save_outlet: "#card_form", action: "keydown.ctrl+enter@document->clicker#click keydown.meta+enter@document->clicker#click" } do %> Create card <% end %> <%= button_to card_publish_path(card), method: :post, class: "btn btn--reversed", name: "creation_type", value: "add_another", title: "Create and add another (#{ hotkey_label(["ctrl", "shift", "enter"]) })", form: { data: { controller: "form" } }, - data: { form_target: "submit", controller: "clicker", action: "keydown.ctrl+shift+enter@document->clicker#click keydown.meta+shift+enter@document->clicker#click" } do %> + data: { form_target: "submit", controller: "clicker", clicker_auto_save_outlet: "#card_form", action: "keydown.ctrl+shift+enter@document->clicker#click keydown.meta+shift+enter@document->clicker#click" } do %> Create and add another <% end %> diff --git a/test/system/card_creation_race_test.rb b/test/system/card_creation_race_test.rb new file mode 100644 index 0000000000..5dc5784955 --- /dev/null +++ b/test/system/card_creation_race_test.rb @@ -0,0 +1,47 @@ +require "application_system_test_case" + +class CardCreationRaceTest < ApplicationSystemTestCase + # Reproduces the race documented in #2778: when the user types a title in a + # new draft and immediately presses Cmd/Ctrl+Enter, the publish request can + # reach the server before the auto-save PATCH commits the title. The publish + # callback then writes "Untitled", which the late PATCH rewrites — generating + # a phantom `card_title_changed` event and a system comment as a side effect. + setup do + CardsController.class_eval do + alias_method :_update_without_test_delay, :update + def update + sleep 0.5 + _update_without_test_delay + end + end + end + + teardown do + CardsController.class_eval do + alias_method :update, :_update_without_test_delay + remove_method :_update_without_test_delay + end + end + + test "Cmd+Enter on a new draft preserves the typed title without phantom events or system comments" do + sign_in_as(users(:david)) + + visit board_url(boards(:writebook)) + click_on "Add a card" + + title_field = find("textarea[name='card[title]']") + title_field.send_keys "Race fix verified" + title_field.send_keys [ :control, :enter ] + + assert_current_path board_path(boards(:writebook)) + assert_text "Race fix verified" # wait for late PATCH to commit before assertions + + card = Card.where(creator: users(:david)).order(:created_at).last + assert_equal "Race fix verified", card.reload.title + assert card.published?, "card should be published" + refute Event.exists?(action: "card_title_changed", eventable: card), + "publish should not generate a phantom card_title_changed event" + refute card.comments.any? { |c| c.body.to_plain_text.include?("changed the title") }, + "publish should not generate a phantom 'changed the title' system comment" + end +end