mirror of
https://github.com/discourse/discourse.git
synced 2026-03-03 23:54:20 +08:00
Following PR #36711 which enabled the refreshed review UI for all users, this commit removes the old feature flag infrastructure and cleans up legacy code that is no longer needed. Backend Changes: - Removed ReviewableActionLog model and its spec entirely - Removed CalculateFinalStatusFromLogs service and spec - Removed site settings: force_old_reviewable_ui, reviewable_old_moderator_actions - Simplified ReviewableActionBuilder — stripped out legacy action-building methods (build_user_actions_bundle, build_post_actions_bundle, build_new_separated_actions) - Cleaned up reviewable models (ReviewableFlaggedPost, ReviewablePost, ReviewableQueuedPost, ReviewableUser, Chat::ReviewableMessage, ReviewablePostVotingComment) — removed unused/legacy action definitions - Removed legacy specs for action builder, action logs, flagged post actions, post actions, user actions, and status-from-logs - Updated system tests and page objects to reflect the new UI structure Frontend Changes: - Deleted legacy components: reviewable-item.gjs, reviewable-user.gjs, review-index-legacy.gjs - Renamed reviewable-refresh/ → reviewable/ — moved all sub-components (created-by, flagged-post, item, post, queued-post, topic-link, user, etc.) out of the refresh directory into the canonical reviewable/ namespace - Simplified reviewable/item.gjs — removed feature flag conditionals and legacy code paths - Cleaned up review/index.gjs and review/show.gjs templates — removed branching between old/new UI - Updated plugin components (chat, AI, post-voting) to import from reviewable/ instead of reviewable-refresh/ - Removed acceptance tests (review-test.js) replaced by system tests - Renamed and updated integration tests from reviewable-refresh/* to reviewable/* Plan for next PRs: - Move plugins from Pages::RefreshedReview to Pages::Review - Move plugins to import from `reviewable/` and not `refreshed-reviewable/` - Move reviewable-user.js import in plugin to use `reviewable/user.js` - Remove unused settings like `reviewable_old_moderator_actions` from plugins - Delete `Pages::RefreshedReview` - Delete `reviewable-refresh/` directory - Delete `reviewable-user.js` component - Delete `reviewable_old_moderator_actions` site setting Plugins PRs: - https://github.com/discourse/discourse-akismet/pull/203 - https://github.com/discourse/discourse-antivirus/pull/98 - https://github.com/discourse/discourse-category-experts/pull/223
312 lines
11 KiB
Ruby
312 lines
11 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
describe "Reviewables", type: :system do
|
|
let(:review_page) { PageObjects::Pages::Review.new }
|
|
fab!(:admin)
|
|
fab!(:theme)
|
|
fab!(:long_post, :post_with_very_long_raw_content)
|
|
fab!(:post)
|
|
let(:composer) { PageObjects::Components::Composer.new }
|
|
let(:moderator) { Fabricate(:moderator) }
|
|
let(:toasts) { PageObjects::Components::Toasts.new }
|
|
|
|
before { sign_in(admin) }
|
|
|
|
describe "when there is a flagged post reviewable with a short post" do
|
|
fab!(:short_reviewable) { Fabricate(:reviewable_flagged_post, target: post) }
|
|
|
|
it "should not show a button to expand/collapse the post content" do
|
|
visit("/review")
|
|
expect(review_page).to have_no_post_body_collapsed
|
|
expect(review_page).to have_no_post_body_toggle
|
|
end
|
|
|
|
describe "reviewable actions" do
|
|
it "should have agree_and_edit action" do
|
|
visit("/review")
|
|
select_kit =
|
|
PageObjects::Components::SelectKit.new(".dropdown-select-box.post-agree-and-hide")
|
|
select_kit.expand
|
|
|
|
expect(select_kit).to have_option_value("post-agree_and_edit")
|
|
end
|
|
|
|
it "agree_and_edit should open the composer" do
|
|
visit("/review")
|
|
select_kit =
|
|
PageObjects::Components::SelectKit.new(".dropdown-select-box.post-agree-and-hide")
|
|
select_kit.expand
|
|
|
|
find("[data-value='post-agree_and_edit']").click
|
|
|
|
expect(composer).to be_opened
|
|
expect(composer.composer_input.value).to eq(post.raw)
|
|
expect(toasts).to have_success(I18n.t("reviewables.actions.agree_and_edit.complete"))
|
|
end
|
|
|
|
it "should open a modal when suspending a user" do
|
|
visit("/review")
|
|
|
|
select_kit =
|
|
PageObjects::Components::SelectKit.new(".dropdown-select-box.post-agree-and-hide")
|
|
select_kit.expand
|
|
|
|
select_kit.select_row_by_value("post-agree_and_suspend")
|
|
|
|
expect(review_page).to have_css(
|
|
"#discourse-modal-title",
|
|
text: I18n.t("js.flagging.take_action_options.suspend.title"),
|
|
)
|
|
end
|
|
|
|
it "should show a toast when disagreeing with a flag flag" do
|
|
visit("/review")
|
|
|
|
select_kit = PageObjects::Components::SelectKit.new(".dropdown-select-box.post-disagree")
|
|
select_kit.expand
|
|
select_kit.select_row_by_value("post-disagree")
|
|
|
|
expect(toasts).to have_success(I18n.t("reviewables.actions.disagree.complete"))
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "when there is a queued post reviewable with a short post" do
|
|
fab!(:short_queued_reviewable, :reviewable_queued_post)
|
|
|
|
it "should not show a button to expand/collapse the post content" do
|
|
visit("/review")
|
|
expect(review_page).to have_no_post_body_collapsed
|
|
expect(review_page).to have_no_post_body_toggle
|
|
end
|
|
|
|
it "should apply correct button classes to actions" do
|
|
visit("/review")
|
|
|
|
expect(page).to have_css(".approve-post.btn-success")
|
|
expect(page).to have_css(".reject-post .btn-danger")
|
|
|
|
expect(page).to have_no_css(".approve-post.btn-default")
|
|
expect(page).to have_no_css(".reject-post .btn-default")
|
|
end
|
|
end
|
|
|
|
describe "when there is a reviewable user" do
|
|
fab!(:user)
|
|
let(:rejection_reason_modal) { PageObjects::Modals::RejectReasonReviewable.new }
|
|
let(:scrub_user_modal) { PageObjects::Modals::ScrubRejectedUser.new }
|
|
|
|
before do
|
|
SiteSetting.must_approve_users = true
|
|
Jobs.run_immediately!
|
|
user.update!(approved: false)
|
|
user.activate
|
|
end
|
|
|
|
it "Rejecting user sends rejection email and updates reviewable with rejection reason" do
|
|
rejection_reason = "user is spamming"
|
|
reviewable = ReviewableUser.find_by_target_id(user.id)
|
|
# cache it for later assertion instead of querying UserHistory
|
|
user_email = user.email
|
|
|
|
review_page.visit_reviewable(reviewable)
|
|
review_page.select_bundled_action(reviewable, "user-delete_user")
|
|
rejection_reason_modal.fill_in_rejection_reason(rejection_reason)
|
|
rejection_reason_modal.select_send_rejection_email_checkbox
|
|
rejection_reason_modal.delete_user
|
|
|
|
expect(review_page).to have_reviewable_with_rejected_status(reviewable)
|
|
|
|
mail = ActionMailer::Base.deliveries.first
|
|
expect(mail.to).to eq([user_email])
|
|
expect(mail.subject).to match(/You've been rejected on Discourse/)
|
|
expect(mail.body.raw_source).to include rejection_reason
|
|
end
|
|
|
|
it "Allows scrubbing user data after rejection" do
|
|
rejection_reason = "user is spamming"
|
|
scrubbing_reason = "a spammer who knows how to make GDPR requests"
|
|
reviewable = ReviewableUser.find_by_target_id(user.id)
|
|
|
|
review_page.visit_reviewable(reviewable)
|
|
review_page.select_bundled_action(reviewable, "user-delete_user")
|
|
rejection_reason_modal.fill_in_rejection_reason(rejection_reason)
|
|
rejection_reason_modal.delete_user
|
|
|
|
expect(review_page).to have_reviewable_with_rejected_status(reviewable)
|
|
|
|
review_page.click_scrub_user_button
|
|
|
|
expect(scrub_user_modal.scrub_button).to be_disabled
|
|
scrub_user_modal.fill_in_scrub_reason(scrubbing_reason)
|
|
expect(scrub_user_modal.scrub_button).not_to be_disabled
|
|
scrub_user_modal.scrub_button.click
|
|
|
|
expect(review_page).to have_reviewable_with_scrubbed_by(reviewable, admin.username)
|
|
expect(review_page).to have_reviewable_with_scrubbed_reason(reviewable, scrubbing_reason)
|
|
expect(review_page).to have_reviewable_with_scrubbed_at(
|
|
reviewable,
|
|
reviewable.payload["scrubbed_at"],
|
|
)
|
|
end
|
|
end
|
|
|
|
context "when performing a review action from the show route" do
|
|
fab!(:contact_group, :group)
|
|
fab!(:contact_user, :user)
|
|
|
|
before do
|
|
SiteSetting.site_contact_group_name = contact_group.name
|
|
SiteSetting.site_contact_username = contact_user.username
|
|
end
|
|
|
|
context "with a ReviewableQueuedPost" do
|
|
fab!(:queued_post_reviewable, :reviewable_queued_post)
|
|
|
|
it "delete_user does not delete reviewable" do
|
|
review_page.visit_reviewable(queued_post_reviewable)
|
|
|
|
expect(queued_post_reviewable).to be_pending
|
|
expect(queued_post_reviewable.target_created_by).to be_present
|
|
expect(review_page).to have_reviewable_with_pending_status(queued_post_reviewable)
|
|
|
|
review_page.select_bundled_action(queued_post_reviewable, "delete_user")
|
|
|
|
expect(review_page).to have_no_error_dialog_visible
|
|
expect(review_page).to have_reviewable_with_rejected_status(queued_post_reviewable)
|
|
expect(review_page).to have_no_reviewable_action_dropdown
|
|
expect(queued_post_reviewable.reload).to be_rejected
|
|
expect(queued_post_reviewable.target_created_by).to be_nil
|
|
end
|
|
|
|
it "allows revising and rejecting to send a PM to the user" do
|
|
revise_modal = PageObjects::Modals::Base.new
|
|
|
|
review_page.visit_reviewable(queued_post_reviewable)
|
|
|
|
expect(queued_post_reviewable).to be_pending
|
|
expect(queued_post_reviewable.target_created_by).to be_present
|
|
|
|
review_page.select_bundled_action(queued_post_reviewable, "revise_and_reject_post")
|
|
|
|
expect(revise_modal).to be_open
|
|
|
|
reason_dropdown =
|
|
PageObjects::Components::SelectKit.new(".revise-and-reject-reviewable__reason")
|
|
reason_dropdown.select_row_by_value(SiteSetting.reviewable_revision_reasons_map.first)
|
|
find(".revise-and-reject-reviewable__feedback").fill_in(with: "This is a test")
|
|
revise_modal.click_primary_button
|
|
|
|
expect(review_page).to have_reviewable_with_rejected_status(queued_post_reviewable)
|
|
expect(queued_post_reviewable.reload).to be_rejected
|
|
|
|
topic = Topic.where(archetype: Archetype.private_message).last
|
|
expect(topic.topic_allowed_users.pluck(:user_id)).to include(contact_user.id)
|
|
expect(topic.topic_allowed_groups.pluck(:group_id)).to include(contact_group.id)
|
|
expect(topic.title).to eq(
|
|
I18n.t(
|
|
"system_messages.reviewable_queued_post_revise_and_reject.subject_template",
|
|
topic_title: queued_post_reviewable.topic.title,
|
|
),
|
|
)
|
|
end
|
|
|
|
it "allows selecting a custom reason for revise and reject" do
|
|
revise_modal = PageObjects::Modals::Base.new
|
|
|
|
review_page.visit_reviewable(queued_post_reviewable)
|
|
|
|
expect(queued_post_reviewable).to be_pending
|
|
expect(queued_post_reviewable.target_created_by).to be_present
|
|
|
|
review_page.select_bundled_action(queued_post_reviewable, "revise_and_reject_post")
|
|
expect(revise_modal).to be_open
|
|
|
|
reason_dropdown =
|
|
PageObjects::Components::SelectKit.new(".revise-and-reject-reviewable__reason")
|
|
reason_dropdown.select_row_by_value("other_reason")
|
|
find(".revise-and-reject-reviewable__custom-reason").fill_in(with: "I felt like it")
|
|
find(".revise-and-reject-reviewable__feedback").fill_in(with: "This is a test")
|
|
revise_modal.click_primary_button
|
|
|
|
expect(review_page).to have_reviewable_with_rejected_status(queued_post_reviewable)
|
|
end
|
|
|
|
context "with reviewable claiming enabled" do
|
|
before { SiteSetting.reviewable_claiming = "required" }
|
|
|
|
it "properly claims and unclaims the reviewable" do
|
|
review_page.visit_reviewable(queued_post_reviewable)
|
|
|
|
expect(review_page).to have_no_reviewable_action_dropdown
|
|
|
|
review_page.click_claim_reviewable
|
|
|
|
expect(review_page).to have_reviewable_action_dropdown
|
|
|
|
review_page.click_unclaim_reviewable
|
|
|
|
expect(review_page).to have_no_reviewable_action_dropdown
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "when there is an unknown plugin reviewable" do
|
|
fab!(:reviewable) { Fabricate(:reviewable_flagged_post, target: long_post) }
|
|
fab!(:reviewable2, :reviewable)
|
|
|
|
before do
|
|
reviewable.update_columns(type: "UnknownPlugin", type_source: "some-plugin")
|
|
reviewable2.update_columns(type: "UnknownSource", type_source: "unknown")
|
|
end
|
|
|
|
it "informs admin and allows to delete them" do
|
|
visit("/review")
|
|
expect(review_page).to have_information_about_unknown_reviewables_visible
|
|
expect(review_page).to have_listing_for_unknown_reviewables_plugin(
|
|
reviewable.type,
|
|
reviewable.type_source,
|
|
)
|
|
expect(review_page).to have_listing_for_unknown_reviewables_unknown_source(reviewable2.type)
|
|
review_page.click_ignore_all_unknown_reviewables
|
|
expect(review_page).to have_no_information_about_unknown_reviewables_visible
|
|
end
|
|
|
|
it "does not inform moderator about them" do
|
|
sign_in(moderator)
|
|
|
|
visit("/review")
|
|
expect(review_page).to have_no_information_about_unknown_reviewables_visible
|
|
end
|
|
end
|
|
|
|
describe "custom community moderator guide topic" do
|
|
fab!(:group)
|
|
fab!(:topic) { Fabricate(:topic, title: "Moderator guide") }
|
|
fab!(:post) { Fabricate(:post, topic: topic) }
|
|
fab!(:reviewable, :reviewable_queued_post)
|
|
|
|
before { group.add(admin) }
|
|
|
|
it "displays the custom guide topic link when configured" do
|
|
SiteSetting.moderator_guide_topic = topic.id
|
|
|
|
review_page.visit_reviewable(reviewable)
|
|
expect(review_page).to have_css(
|
|
"a.review-resources__link",
|
|
text: I18n.t("js.review.help.community_moderation_guide"),
|
|
)
|
|
end
|
|
|
|
it "does not display anything when no custom guide topic configured" do
|
|
SiteSetting.moderator_guide_topic = ""
|
|
|
|
review_page.visit_reviewable(reviewable)
|
|
expect(review_page).to have_no_css(
|
|
"a.review-resources__link",
|
|
text: I18n.t("js.review.help.community_moderation_guide"),
|
|
)
|
|
end
|
|
end
|
|
end
|