discourse/spec/system/page_objects/components/composer.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

267 lines
6.7 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
module PageObjects
module Components
class Composer < PageObjects::Components::Base
COMPOSER_ID = "#reply-control"
AUTOCOMPLETE_MENU = ".autocomplete.ac-emoji"
def opened?
page.has_css?("#{COMPOSER_ID}.open")
end
def closed?
page.has_css?("#{COMPOSER_ID}.closed", visible: :all)
end
def open_composer_actions
find(".composer-action-title .btn").click
self
end
def click_toolbar_button(button_class)
find(".d-editor-button-bar button.#{button_class}").click
self
end
def fill_title(title)
find("#{COMPOSER_ID} #reply-title").fill_in(with: title)
self
end
def fill_content(content)
composer_input.fill_in(with: content)
self
end
def append_content(content)
current_content = composer_input.value
composer_input.set(current_content + content)
self
end
def fill_form_template_field(field, content)
form_template_field(field).fill_in(with: content)
self
end
def type_content(content)
composer_input.send_keys(content)
self
end
def clear_content
fill_content("")
end
def has_content?(content)
composer_input.value == content
end
def has_popup_content?(content)
composer_popup.has_content?(content)
end
def select_action(action)
find(action(action)).click
self
end
def create
find("#{COMPOSER_ID} .btn-primary").click
end
def action(action_title)
".composer-action-title .select-kit-collection li[title='#{action_title}']"
end
def button_label
find("#{COMPOSER_ID} .btn-primary .d-button-label")
end
def emoji_picker
find("#{COMPOSER_ID} .emoji-picker")
end
def emoji_autocomplete
find(AUTOCOMPLETE_MENU)
end
def category_chooser
Components::SelectKit.new(".category-chooser")
end
def switch_category(category_name)
category_chooser.expand
category_chooser.select_row_by_name(category_name)
end
def preview
find("#{COMPOSER_ID} .d-editor-preview-wrapper")
end
def has_discard_draft_modal?
page.has_css?(".discard-draft-modal")
end
def has_emoji_autocomplete?
has_css?(AUTOCOMPLETE_MENU)
end
def has_no_emoji_autocomplete?
has_no_css?(AUTOCOMPLETE_MENU)
end
EMOJI_SUGGESTION_SELECTOR = "#{AUTOCOMPLETE_MENU} .emoji-shortname"
def has_emoji_suggestion?(emoji)
has_css?(EMOJI_SUGGESTION_SELECTOR, text: emoji)
end
def has_no_emoji_suggestion?(emoji)
has_no_css?(EMOJI_SUGGESTION_SELECTOR, text: emoji)
end
def has_emoji_preview?(emoji)
page.has_css?(emoji_preview_selector(emoji))
end
def has_no_emoji_preview?(emoji)
page.has_no_css?(emoji_preview_selector(emoji))
end
COMPOSER_INPUT_SELECTOR = "#{COMPOSER_ID} .d-editor-input"
def has_no_composer_input?
page.has_no_css?(COMPOSER_INPUT_SELECTOR)
end
def has_composer_input?
page.has_css?(COMPOSER_INPUT_SELECTOR)
end
def has_composer_preview?
page.has_css?("#{COMPOSER_ID} .d-editor-preview-wrapper")
end
def has_no_composer_preview?
page.has_no_css?("#{COMPOSER_ID} .d-editor-preview-wrapper")
end
def has_composer_preview_toggle?
page.has_css?("#{COMPOSER_ID} .toggle-preview")
end
def has_no_composer_preview_toggle?
page.has_no_css?("#{COMPOSER_ID} .toggle-preview")
end
def has_form_template?
page.has_css?(".form-template-form__wrapper")
end
def has_form_template_field?(field)
page.has_css?(".form-template-field[data-field-type='#{field}']")
end
def has_form_template_field_required_indicator?(field)
page.has_css?(
".form-template-field[data-field-type='#{field}'] .form-template-field__required-indicator",
)
end
FORM_TEMPLATE_CHOOSER_SELECTOR = ".composer-select-form-template"
def has_no_form_template_chooser?
page.has_no_css?(FORM_TEMPLATE_CHOOSER_SELECTOR)
end
def has_form_template_chooser?
page.has_css?(FORM_TEMPLATE_CHOOSER_SELECTOR)
end
def has_form_template_field_error?(error)
page.has_css?(".form-template-field__error", text: error, visible: :all)
end
def has_form_template_field_label?(label)
page.has_css?(".form-template-field__label", text: label)
end
def has_form_template_field_description?(description)
page.has_css?(".form-template-field__description", text: description)
end
def has_post_error?(error)
page.has_css?(".popup-tip", text: error, visible: all)
end
def has_no_post_error?(error)
page.has_no_css?(".popup-tip", text: error, visible: all)
end
def composer_input
find("#{COMPOSER_ID} .d-editor .d-editor-input")
end
def composer_popup
find("#{COMPOSER_ID} .composer-popup")
end
def form_template_field(field)
find(".form-template-field[data-field-type='#{field}']")
end
def move_cursor_after(text)
execute_script(<<~JS, text)
const text = arguments[0];
const composer = document.querySelector("#{COMPOSER_ID} .d-editor-input");
const index = composer.value.indexOf(text);
const position = index + text.length;
composer.focus();
composer.setSelectionRange(position, position);
JS
end
def select_all
execute_script(<<~JS, text)
const composer = document.querySelector("#{COMPOSER_ID} .d-editor-input");
composer.focus();
composer.setSelectionRange(0, composer.value.length);
JS
end
FIX: Video thumbnail uploads interfering with subsequent uploads (#23216) Short answer -- the problem is the video thumbnail generator & uploader code added a couple of months back in f144c64e139e41f176ea2ec3433a468fa49b955f. It was implemented as another Mixin which overrides `this._uppyInstance` when uploading the video thumbnail after the initial upload is complete, which means the composer's `this._uppyInstance` value is overridden, and it loses all of its preprocessors & upload code. This is generally a problem with the Mixin based architecture that I used for the Uppy code, which we need to remove at some point and refacotr. The most ideal thing to do here would be to convert this video thumbnail code into an Uppy [postprocessor](https://uppy.io/docs/uppy/#addpostprocessorfn) plugin, which runs on each upload after they are complete. I started looking into this, and the main hurdle here is adding support to tracking the progress of postprocessors to [ExtendableUploader](https://github.com/discourse/discourse/blob/cf42466dea75f1df97d580d2a5e6c221358137a8/app/assets/javascripts/discourse/app/mixins/extendable-uploader.js) so that is out of scope at this time. The fix here makes it so the ComposerVideoThumbnailUppy code is no longer a Mixin, but acts more like a normal class, a pattern which we have used in chat. I also clean up a lot of the thumbnail uploader code and remove some unnecessary things. Attempted to add a system spec, but video streaming does not work in Chrome for Testing at this time, and it is needed for the onloadedmetadata event.
2023-08-24 12:04:26 +08:00
def submit
find("#{COMPOSER_ID} .save-or-cancel .create").click
end
def close
find("#{COMPOSER_ID} .save-or-cancel .cancel").click
end
FIX: Video thumbnail uploads interfering with subsequent uploads (#23216) Short answer -- the problem is the video thumbnail generator & uploader code added a couple of months back in f144c64e139e41f176ea2ec3433a468fa49b955f. It was implemented as another Mixin which overrides `this._uppyInstance` when uploading the video thumbnail after the initial upload is complete, which means the composer's `this._uppyInstance` value is overridden, and it loses all of its preprocessors & upload code. This is generally a problem with the Mixin based architecture that I used for the Uppy code, which we need to remove at some point and refacotr. The most ideal thing to do here would be to convert this video thumbnail code into an Uppy [postprocessor](https://uppy.io/docs/uppy/#addpostprocessorfn) plugin, which runs on each upload after they are complete. I started looking into this, and the main hurdle here is adding support to tracking the progress of postprocessors to [ExtendableUploader](https://github.com/discourse/discourse/blob/cf42466dea75f1df97d580d2a5e6c221358137a8/app/assets/javascripts/discourse/app/mixins/extendable-uploader.js) so that is out of scope at this time. The fix here makes it so the ComposerVideoThumbnailUppy code is no longer a Mixin, but acts more like a normal class, a pattern which we have used in chat. I also clean up a lot of the thumbnail uploader code and remove some unnecessary things. Attempted to add a system spec, but video streaming does not work in Chrome for Testing at this time, and it is needed for the onloadedmetadata event.
2023-08-24 12:04:26 +08:00
def has_no_in_progress_uploads?
find("#{COMPOSER_ID}").has_no_css?("#file-uploading")
end
def has_in_progress_uploads?
find("#{COMPOSER_ID}").has_css?("#file-uploading")
end
def select_pm_user(username)
select_kit = PageObjects::Components::SelectKit.new("#private-message-users")
select_kit.expand
select_kit.search(username)
select_kit.select_row_by_value(username)
select_kit.collapse
end
private
def emoji_preview_selector(emoji)
".d-editor-preview .emoji[title=':#{emoji}:']"
end
end
end
end