Remove "New Admin Member"-Related Code (#16475)

* Removes code behind new_admin_members feature flag

* Removes components/admin/users/tools/* and the tools components

* Removes unused /admin/users/tools/* controllers, comments, and routes

* Removes New Member View-related E2E and RSpec specs

* Remove admin_users_tools.rb frin spec/support/shared_examples/

* Removes remaining component-related specs

* Removes the view_component gem and test helper

* Resolve merge conflicts in Admin::UsersController

* Removes the view_component gem, as it is no longer used

* Removes view_component from Gemfile.lock
This commit is contained in:
Julianna Tetreault 2022-02-10 06:42:56 -07:00 committed by GitHub
parent e6244b7f7e
commit 89706527b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 2 additions and 1889 deletions

View file

@ -107,7 +107,6 @@ gem "twitter", "~> 7.0" # A Ruby interface to the Twitter API
gem "uglifier", "~> 4.2" # Uglifier minifies JavaScript files
gem "validate_url", "~> 1.0" # Library for validating urls in Rails
gem "vault", "~> 0.16" # Used to store secrets
gem "view_component", "~> 2.48" # View components for Rails
gem "warning", "~> 1.2" # Adds custom processing for warnings, including the ability to ignore specific warning messages
gem "wcag_color_contrast", "~> 0.1" # Detect contrast of colors to determine readability and a11y.
gem "webpacker", "~> 5.4.3" # Use webpack to manage app-like JavaScript modules in Rails

View file

@ -1042,7 +1042,6 @@ DEPENDENCIES
validate_url (~> 1.0)
vault (~> 0.16)
vcr (~> 6.0)
view_component (~> 2.48)
warning (~> 1.2)
wcag_color_contrast (~> 0.1)
web-console (~> 4.2)

View file

@ -1,134 +0,0 @@
<section
class="crayons-card p-3 js-component"
data-controller="users--tools--ajax"
data-action="ajax:success@document->users--tools--ajax#success ajax:error@document->users--tools--ajax#error">
<%= render Admin::Users::Tools::HeaderComponent.new(title: "Credits", user: @user) %>
<%# Add user credits %>
<%= form_with url: admin_user_tools_credits_path(@user),
method: :post,
remote: true,
class: "my-3",
id: :credits_add do |f| %>
<fieldset>
<legend class="fw-medium mb-3">Add credits</legend>
<div class="fs-base fw-medium mb-3"><%= pluralize(@user_unspent_credits_count, "available user credit") %></div>
<div class="crayons-field form-group w-50">
<%= f.number_field "credits[count]",
id: nil,
in: 1...10_000,
required: true,
class: "crayons-textfield",
"aria-label": "Number of credits to add" %>
<%= f.text_field "credits[note]",
id: nil,
placeholder: "Why are you adding these credits?",
required: true,
class: "crayons-textfield",
"aria-label": "Reason why you want to add credits" %>
</div>
<%= f.submit "Add", class: "crayons-btn", data: { disable_with: "Add" } %>
</fieldset>
<% end -%>
<%# Remove user credits %>
<% if @user_unspent_credits_count.positive? %>
<%= form_with url: admin_user_tools_credits_path(@user),
method: :delete,
remote: true,
class: "my-3",
id: :credits_remove do |f| %>
<fieldset>
<legend class="fw-medium mb-3">Remove credits</legend>
<div class="crayons-field form-group w-50">
<%= f.number_field "credits[count]",
id: nil,
in: 1..@user_unspent_credits_count,
required: true,
class: "crayons-textfield mr-1",
"aria-label": "Number of credits to remove" %>
<%= f.text_field "credits[note]",
id: nil,
placeholder: "Why are you removing these credits?",
required: true,
class: "crayons-textfield mr-1",
"aria-label": "Reason why you want to remove credits" %>
</div>
<%= f.submit "Remove", class: "crayons-btn crayons-btn--danger", data: { disable_with: "Remove" } %>
</fieldset>
<% end -%>
<% end %>
<%# Add and remove organizations credits %>
<% if @organizations.any? %>
<%= form_with url: admin_user_tools_credits_path(@user),
method: :post,
remote: true,
class: "my-3",
id: :org_credits_add do |f| %>
<fieldset>
<legend class="fw-medium mb-3">Add credits to organizations</legend>
<div class="crayons-field form-group w-50">
<%= f.number_field "credits[count]",
id: nil,
in: 1...10_000,
required: true,
class: "crayons-textfield mr-1",
"aria-label": "Number of credits to add" %>
<%= f.text_field "credits[note]",
id: nil,
placeholder: "Why are you adding these credits?",
required: true,
class: "crayons-textfield mr-1",
"aria-label": "Reason why you want to add credits" %>
<%= select_tag "credits[organization_id]", orgs_with_credits(@organizations),
id: "select_add_org_credits",
class: "crayons-select",
"aria-label": "Select organization receiving the credits" %>
</div>
<%= f.submit "Add", class: "crayons-btn", data: { disable_with: "Add" } %>
</fieldset>
<% end -%>
<%= form_with url: admin_user_tools_credits_path(@user),
method: :delete,
remote: true,
class: "my-3",
id: :org_credits_remove do |f| %>
<fieldset>
<legend class="fw-medium mb-3">Remove credits from organizations</legend>
<div class="crayons-field form-group w-50">
<%= f.number_field "credits[count]",
id: nil,
in: 1..10_000,
required: true,
class: "crayons-textfield mr-1",
"aria-label": "Number of credits to remove" %>
<%= f.text_field "credits[note]",
id: nil,
placeholder: "Why are you removing these credits?",
required: true,
class: "crayons-textfield mr-1",
"aria-label": "Reason why you want to remove credits" %>
<%= select_tag "credits[organization_id]", orgs_with_credits(@organizations),
id: "select_remove_org_credits",
class: "crayons-select",
"aria-label": "Select organization whose credits are going to be withdrawn" %>
</div>
<%= f.submit "Remove", class: "crayons-btn crayons-btn--danger", data: { disable_with: "Remove" } %>
</fieldset>
<% end -%>
<% end %>
</section>

View file

@ -1,15 +0,0 @@
module Admin
module Users
module Tools
class CreditsComponent < ViewComponent::Base
delegate :orgs_with_credits, to: :helpers
def initialize(user:)
@user = user
@user_unspent_credits_count = user.unspent_credits_count
@organizations = user.organizations.order(:name)
end
end
end
end
end

View file

@ -1,67 +0,0 @@
<section
class="crayons-card p-3 js-component"
data-controller="users--tools--ajax"
data-action="ajax:success@document->users--tools--ajax#success ajax:error@document->users--tools--ajax#error">
<%= render Admin::Users::Tools::HeaderComponent.new(title: "Emails", user: @user) %>
<%# Email verification #%>
<article class="my-3 flex justify-between items-center">
<span>
<strong>To: <%= @user.name %></strong>
<span class="color-base-60">(<%= @user.email %><% if @verified %> · Verified on <local-time datetime="<%= @verification_date.iso8601 %>"><%= @verification_date.strftime("%b %d, %Y") %></local-time><% end -%>)</span>
</span>
<%= button_to "Verify Email Ownership", verify_email_ownership_admin_user_path(@user),
remote: true,
class: "btn btn-secondary btn-sm" %>
</article>
<%# Send email to user #%>
<article class="my-3">
<%= form_with url: send_email_admin_user_path(@user) do |f| %>
<div class="crayons-field form-group">
<%= f.label :email_subject, "Subject", class: "crayons-field__label" %>
<%= f.text_field :email_subject,
required: true,
placeholder: "What do you want to say in the subject line?",
class: "crayons-textfield" %>
</div>
<div class="crayons-field form-group">
<%= f.label :email_body, "Body", class: "crayons-field__label" %>
<%= f.text_area :email_body,
required: true,
placeholder: "What do you want to say?",
class: "crayons-textfield" %>
</div>
<%= f.submit "Send Email",
class: "crayons-btn",
data: { disable_with: "Sending..." } %>
<% end -%>
</article>
<%# Emails history #%>
<% if @messages.any? %>
<article class="mt-5">
<details>
<summary class="fw-medium">Emails history (<%= @messages.size %>)</summary>
<ul class="list-none">
<% @messages.each_with_index do |email, index| %>
<li
class="py-3 pl-3 <%= index == @messages.size - 1 ? "border-b-0" : "border-b-1" %> border-l-0 border-t-0 border-r-0 border-solid border-base-50">
<a href="<%= admin_user_email_message_path(@user, email) %>"
class="text-none color-base-60"
rel="noopener noreferer"
target="_blank">
<%= email.subject %> <em> - <%= email.sent_at&.strftime("%b %e '%y") %></em>
</a>
</li>
<% end %>
</ul>
</details>
</article>
<% end %>
</section>

View file

@ -1,16 +0,0 @@
module Admin
module Users
module Tools
class EmailsComponent < ViewComponent::Base
delegate :send_email_admin_user_path, to: :helpers
def initialize(user:)
@user = user
@verification_date = EmailAuthorization.last_verification_date(user)
@verified = @verification_date.present?
@messages = user.email_messages.order(sent_at: :desc).limit(50)
end
end
end
end
end

View file

@ -1,9 +0,0 @@
<%# Used to announce to the screen reader client side which section the user is in %>
<div id="section-title" class="hidden"><%= @title %> section</div>
<h3>
<%= link_to "← Tools", admin_user_tools_path(@user),
remote: true,
data: { action: "ajax:success@document->user#replacePartial" },
class: "crayons-title fs-l backlink" %>
</h3>

View file

@ -1,12 +0,0 @@
module Admin
module Users
module Tools
class HeaderComponent < ViewComponent::Base
def initialize(title:, user:)
@title = title
@user = user
end
end
end
end
end

View file

@ -1,47 +0,0 @@
<section
class="crayons-card p-3 js-component"
data-controller="users--tools--ajax"
data-action="ajax:success@document->users--tools--ajax#success ajax:error@document->users--tools--ajax#error">
<%= render Admin::Users::Tools::HeaderComponent.new(title: "Notes", user: @user) %>
<%# Create a new note %>
<%= form_with model: Note.new,
url: admin_user_tools_notes_path(@user),
method: :post,
remote: true,
class: "mt-2" do |f| %>
<div class="crayons-field form-group">
<%= f.label :content, "Add a new note", class: "crayons-field__label" %>
<%= f.text_area :content, required: true, class: "crayons-textfield" %>
</div>
<%= f.submit "Create Note", class: "crayons-btn", data: { disable_with: "Creating..." } %>
<% end -%>
<%# Notes history #%>
<% if @notes.any? %>
<article class="mt-5">
<details>
<summary class="fw-medium">Recent Notes (<%= @notes.size %>)</summary>
<ul class="list-none">
<% @notes.each_with_index do |note, index| %>
<li class="py-3 pl-3 <%= index == @notes.size - 1 ? "border-b-0" : "border-b-1" %> border-l-0 border-t-0 border-r-0 border-solid border-base-50">
<local-time datetime="<%= note.created_at.iso8601 %>">
<%= note.created_at.strftime("%d %B %Y %H:%M %Z") %>
</local-time>
by
<%= note.author&.username %>
<% if note.reason.present? %>
<strong><%= note.reason %></strong>:
<% end -%>
<%= note.content %>
</li>
<% end %>
</ul>
</details>
</article>
<% end %>
</section>

View file

@ -1,12 +0,0 @@
module Admin
module Users
module Tools
class NotesComponent < ViewComponent::Base
def initialize(user:)
@user = user
@notes = user.notes.includes(:author).order(created_at: :desc).limit(10)
end
end
end
end
end

View file

@ -1,84 +0,0 @@
<section
class="crayons-card p-3 js-component"
data-controller="users--tools--ajax"
data-action="ajax:success@document->users--tools--ajax#success ajax:error@document->users--tools--ajax#error">
<%= render Admin::Users::Tools::HeaderComponent.new(title: "Organizations", user: @user) %>
<%# Add new org membership %>
<%= form_with model: [:admin, OrganizationMembership.new],
remote: true,
class: "my-3",
id: "add-membership" do |f| %>
<%= f.hidden_field :user_id, value: @user.id %>
<fieldset>
<legend class="fw-medium mb-3">Add <%= @user.name %> to a new organization</legend>
<div class="crayons-field form-group w-50">
<%= f.label :organization_id, "Organization ID", class: "crayons-field__label" %>
<%= f.number_field :organization_id, class: "crayons-textfield" %>
</div>
<div class="crayons-field form-group w-50">
<%= f.label :type_of_user, "Membership Level", class: "crayons-field__label", id: "membership-add" %>
<%= select_tag "organization_membership[type_of_user]",
options_for_select(@memberships_for_select, "Member"),
id: nil,
class: "crayons-select",
"aria-label": "Membership level" %>
</div>
<%= f.submit "Add",
class: "crayons-btn",
data: { disable_with: "Adding..." } %>
</fieldset>
<% end -%>
<%# Manage existing memberships %>
<% if @memberships.present? %>
<article class="mt-5">
<details>
<summary class="fw-medium">Manage memberships</summary>
<ul class="list-none">
<% @memberships.each do |membership, index| %>
<li class="py-3 pl-3 <%= index == @memberships.size - 1 ? "border-b-0" : "border-b-1" %> border-l-0 border-t-0 border-r-0 border-solid border-base-50">
<div class="flex justify-between">
<a href="<%= membership.organization.path %>" target="_blank" rel="noopener" class="inline-block">
<h4><%= membership.organization.name %></h4>
</a>
<%= button_to "Remove from organization",
admin_organization_membership_path(membership),
method: :delete,
remote: true,
class: "crayons-btn crayons-btn--danger",
data: { confirm: "Are you sure?" } %>
</div>
<%= form_with model: [:admin, membership],
method: :patch,
remote: true,
id: "update-permissions-#{index}" do |f| %>
<div class="crayons-field form-group w-50">
<%= f.label :type_of_user, "Membership Level", class: "crayons-field__label", id: "update-permissions-label-#{index}" %>
<%= select_tag "organization_membership[type_of_user]",
options_for_select(@memberships_for_select, membership.type_of_user),
id: nil,
class: "crayons-select",
"aria-label": "Membership level for #{membership.organization.name}" %>
</div>
<%= f.submit "Update Permissions",
class: "crayons-btn",
data: { disable_with: "Updating..." } %>
<% end %>
</li>
<% end %>
</ul>
</details>
</article>
<% end %>
</section>

View file

@ -1,21 +0,0 @@
module Admin
module Users
module Tools
class OrganizationsComponent < ViewComponent::Base
MEMBERSHIPS_FOR_SELECT = [
["Member", :member],
["Admin", :admin],
].freeze
def initialize(user:)
@user = user
@memberships_for_select = MEMBERSHIPS_FOR_SELECT
@memberships = @user.organization_memberships
.joins(:organization)
.order("organizations.name" => :asc)
.includes(:organization)
end
end
end
end
end

View file

@ -1,27 +0,0 @@
<section
class="crayons-card p-3 js-component"
data-controller="users--tools--ajax"
data-action="ajax:success@document->users--tools--ajax#success ajax:error@document->users--tools--ajax#error">
<%= render Admin::Users::Tools::HeaderComponent.new(title: "Reactions", user: @user) %>
<% if @reactions.any? %>
<article>
<ul class="list-none">
<% @reactions.each_with_index do |reaction, index| %>
<li
class="py-3 pl-3 <%= index == @reactions.size - 1 ? "border-b-0" : "border-b-1" %> border-l-0 border-t-0 border-r-0 border-solid border-base-50">
<a href="<%= reaction.reactable.path %>"
class="text-none color-base-60 flex justify-between"
rel="noopener noreferer"
target="_blank">
<span>🧐 <%= t("views.reactions.category.#{reaction.category}") %></span>
<span><strong><%= reaction.reactable_type %></strong><%= reaction.reactable_type == "User" ? "" : ": #{reaction.reactable.title}" %></span>
<span><local-time datetime="<%= reaction.created_at.iso8601 %>"><%= reaction.created_at.strftime("%b %d, %Y") %></local-time></span>
</a>
</li>
<% end %>
</ul>
</article>
<% end %>
</section>

View file

@ -1,23 +0,0 @@
module Admin
module Users
module Tools
class ReactionsComponent < ViewComponent::Base
def initialize(user:)
@user = user
@reactions = reactions
end
private
attr_reader :user
def reactions
Reaction.related_negative_reactions_for_user(user)
.includes(:reactable)
.order(created_at: :desc)
.limit(15)
end
end
end
end
end

View file

@ -1,27 +0,0 @@
<section
class="crayons-card p-3 js-component"
data-controller="users--tools--ajax"
data-action="ajax:success@document->users--tools--ajax#success ajax:error@document->users--tools--ajax#error">
<%= render Admin::Users::Tools::HeaderComponent.new(title: "Reports", user: @user) %>
<% if @reports.any? %>
<article>
<ul class="list-none">
<% @reports.each_with_index do |report, index| %>
<li
class="py-3 pl-3 <%= index == @reports.size - 1 ? "border-b-0" : "border-b-1" %> border-l-0 border-t-0 border-r-0 border-solid border-base-50">
<a href="<%= admin_report_path(report) %>"
class="text-none color-base-60 flex justify-between"
rel="noopener noreferer"
target="_blank">
<span><%= report.category.capitalize %> (<%= report.status %>)</span>
<span><strong><%= report.user_types(@user.id).to_sentence %></strong>: <%= report.message %></span>
<span><local-time datetime="<%= report.created_at.iso8601 %>"><%= report.created_at.strftime("%b %d, %Y") %></local-time></span>
</a>
</li>
<% end %>
</ul>
</article>
<% end %>
</section>

View file

@ -1,12 +0,0 @@
module Admin
module Users
module Tools
class ReportsComponent < ViewComponent::Base
def initialize(user:)
@user = user
@reports = FeedbackMessage.all_user_reports(user).order(created_at: :desc).limit(15)
end
end
end
end
end

View file

@ -1,72 +0,0 @@
<%# This is where the component loaded from the server side will insert itself %>
<div data-user-target="replace" role="region"></div>
<section class="crayons-card p-3 js-component tools-component" data-user-target="toolsComponent">
<%# Used to announce to the screen reader client side which section the user is in %>
<div id="section-title" class="hidden">Tools section</div>
<h3 class="crayons-subtitle-2">Tools</h3>
<div class="crayons-card__body grid grid-cols-1 l:grid-cols-2 grid-rows-3 gap-3">
<%= link_to admin_user_tools_emails_path(@user), remote: true,
data: { action: "ajax:success->user#replacePartial" },
class: "crayons-card box js-action" do %>
<h4 class="crayons-subtitle-3 mb-4">Emails</h4>
<span class="color-base-70">
<%= pluralize(@emails.total, "past email") %>
<% if @emails.verified %> - Verified<% end -%>
</span>
<% end %>
<%= link_to admin_user_tools_notes_path(@user), remote: true,
data: { action: "ajax:success->user#replacePartial" },
class: "crayons-card box js-action" do %>
<h4 class="crayons-subtitle-3 mb-4">Notes</h4>
<span class="color-base-70">
<%= pluralize(@notes.total, "note") %>
</span>
<% end %>
<%= link_to admin_user_tools_credits_path(@user), remote: true,
data: { action: "ajax:success->user#replacePartial" },
class: "crayons-card box js-action" do %>
<h4 class="crayons-subtitle-3 mb-4">Credits</h4>
<span class="color-base-70">
<%= pluralize(@credits.total, "credit") %>
</span>
<% end %>
<%= link_to admin_user_tools_organizations_path(@user), remote: true,
data: { action: "ajax:success->user#replacePartial" },
class: "crayons-card box js-action" do %>
<h4 class="crayons-subtitle-3 mb-4">Organizations</h4>
<span class="color-base-70">
<%= pluralize(@organizations.total, "organization") %>
</span>
<% end %>
<%= link_to admin_user_tools_reports_path(@user), remote: true,
data: { action: "ajax:success->user#replacePartial" },
class: "crayons-card box js-action" do %>
<h4 class="crayons-subtitle-3 mb-4">Reports</h4>
<span class="color-base-70">
<%= pluralize(@reports.total, "report") %>
</span>
<% end %>
<%= link_to admin_user_tools_reactions_path(@user), remote: true,
data: { action: "ajax:success->user#replacePartial" },
class: "crayons-card box js-action" do %>
<h4 class="crayons-subtitle-3 mb-4">Reactions</h4>
<span class="color-base-70">
<%= pluralize(@reactions.total, "reaction") %>
</span>
<% end %>
</div>
</section>

View file

@ -1,60 +0,0 @@
module Admin
module Users
class ToolsComponent < ViewComponent::Base
DATA = Struct.new(:total, :verified, keyword_init: true)
def initialize(user:)
@user = user
@emails = emails
@notes = notes
@credits = credits
@organizations = organizations
@reports = reports
@reactions = reactions
end
private
attr_reader :user
def emails
DATA.new(
total: [user.email_messages.count, 50].min, # we only display 50 emails at most
verified: EmailAuthorization.last_verification_date(user).present?,
)
end
def notes
DATA.new(
total: [user.notes.count, 10].min, # we only display 10 notes at most
)
end
def credits
DATA.new(
total: user.unspent_credits_count,
)
end
def organizations
DATA.new(
total: user.organizations.count,
)
end
def reports
DATA.new(
# we only display 15 reports at most
total: [FeedbackMessage.all_user_reports(user).count, 15].min,
)
end
def reactions
DATA.new(
# we only display 15 reactions at most
total: [Reaction.related_negative_reactions_for_user(user).count, 15].min,
)
end
end
end
end

View file

@ -1,87 +0,0 @@
module Admin
module Users
module Tools
class CreditsController < Admin::ApplicationController
layout false
def show
user = ::User.find(params[:user_id])
render CreditsComponent.new(user: user), content_type: "text/html"
end
def create
user = ::User.find(params[:user_id])
respond_to do |format|
format.js do
ActiveRecord::Base.transaction do
if credits_params[:organization_id].present?
Credits::Manage.call(
user,
add_org_credits: credits_params[:count],
organization_id: credits_params[:organization_id],
)
else
Credits::Manage.call(user, add_credits: credits_params[:count])
end
create_note(user)
end
message = "Added #{credits_params[:count]} #{'credit'.pluralize(credits_params[:count].to_i)}!"
render json: { result: message }, content_type: "application/json", status: :created
rescue ActiveRecord::RecordInvalid => e
render json: { error: e.message }, content_type: "application/json", status: :unprocessable_entity
end
end
end
def destroy
user = ::User.find(params[:user_id])
respond_to do |format|
format.js do
ActiveRecord::Base.transaction do
if credits_params[:organization_id].present?
Credits::Manage.call(
user,
remove_org_credits: credits_params[:count],
organization_id: credits_params[:organization_id],
)
else
Credits::Manage.call(user, remove_credits: credits_params[:count])
end
create_note(user)
end
message = "Removed #{credits_params[:count]} #{'credit'.pluralize(credits_params[:count].to_i)}!"
render json: { result: message }, content_type: "application/json", status: :ok
rescue ActiveRecord::RecordInvalid => e
render json: { error: e.message }, content_type: "application/json", status: :unprocessable_entity
end
end
end
private
def authorization_resource
User
end
def credits_params
params.require(:credits).permit(:count, :note, :organization_id)
end
def create_note(user)
user.notes.create!(
content: credits_params[:note],
author: current_user,
reason: :misc_note,
)
end
end
end
end
end

View file

@ -1,21 +0,0 @@
module Admin
module Users
module Tools
class EmailsController < Admin::ApplicationController
layout false
def show
user = ::User.find(params[:user_id])
render_component(EmailsComponent, user: user)
end
private
def authorization_resource
User
end
end
end
end
end

View file

@ -1,42 +0,0 @@
module Admin
module Users
module Tools
class NotesController < Admin::ApplicationController
layout false
def show
user = ::User.find(params[:user_id])
render_component(NotesComponent, user: user)
end
def create
user = ::User.find(params[:user_id])
note = user.notes.build(note_params.merge(author: current_user, reason: :misc_note))
respond_to do |format|
format.js do
if note.save
render json: { result: "Note created!" }, content_type: "application/json", status: :created
else
render json: { error: note.errors_as_sentence },
content_type: "application/json",
status: :unprocessable_entity
end
end
end
end
private
def authorization_resource
User
end
def note_params
params.require(:note).permit(:content)
end
end
end
end
end

View file

@ -1,21 +0,0 @@
module Admin
module Users
module Tools
class OrganizationsController < Admin::ApplicationController
layout false
def show
user = ::User.find(params[:user_id])
render_component(OrganizationsComponent, user: user)
end
private
def authorization_resource
User
end
end
end
end
end

View file

@ -1,21 +0,0 @@
module Admin
module Users
module Tools
class ReactionsController < Admin::ApplicationController
layout false
def show
user = ::User.find(params[:user_id])
render_component(ReactionsComponent, user: user)
end
private
def authorization_resource
User
end
end
end
end
end

View file

@ -1,21 +0,0 @@
module Admin
module Users
module Tools
class ReportsController < Admin::ApplicationController
layout false
def show
user = ::User.find(params[:user_id])
render_component(ReportsComponent, user: user)
end
private
def authorization_resource
User
end
end
end
end
end

View file

@ -1,19 +0,0 @@
module Admin
module Users
class ToolsController < Admin::ApplicationController
layout false
def show
user = ::User.find(params[:user_id])
render_component(ToolsComponent, user: user)
end
private
def authorization_resource
User
end
end
end
end

View file

@ -36,23 +36,16 @@ module Admin
def show
@user = User.find(params[:id])
if FeatureFlag.enabled?(:new_admin_members, current_user)
render "admin/users/new/show"
elsif FeatureFlag.enabled?(:admin_member_view)
if FeatureFlag.enabled?(:admin_member_view)
set_feedback_messages
set_related_reactions
set_user_details
else
set_user_details
end
set_user_details
end
def update
@user = User.find(params[:id])
# TODO: [@rhymes] in the new Admin Member view this logic has been moved
# to Admin::Users::Tools::CreditsController and Admin::Users::Tools::NotesController#create.
# It can eventually be removed when we transition away from the old Admin UI
Credits::Manage.call(@user, credit_params)
add_note if user_params[:new_note]

View file

@ -214,12 +214,6 @@ class ApplicationController < ActionController::Base
Settings::General.admin_action_taken_at = Time.current # Used as cache key
end
# To ensure that components are sent back as HTML, we wrap their rendering in
# this helper method
def render_component(component_class, *args, **kwargs)
render component_class.new(*args, **kwargs), content_type: "text/html"
end
private
def configure_permitted_parameters

View file

@ -44,19 +44,6 @@ namespace :admin do
end
resources :users, only: %i[index show edit update destroy] do
scope module: "users" do
resource :tools, only: :show
namespace :tools do
resource :credits, only: %i[show create destroy]
resource :emails, only: :show
resource :notes, only: %i[show create]
resource :organizations, only: %i[show]
resource :reports, only: %i[show]
resource :reactions, only: %i[show]
end
end
resources :email_messages, only: :show
member do

View file

@ -1,267 +0,0 @@
describe('Tools Section', () => {
beforeEach(() => {
cy.testSetup();
cy.fixture('users/adminUser.json').as('user');
cy.get('@user').then((user) => {
cy.loginAndVisit(user, '/admin/users');
});
});
describe('Show section', () => {
it('shows the boxes', () => {
cy.get('@user').then(({ username }) => {
cy.visitAndWaitForUserSideEffects('/admin/users');
cy.findByRole('link', { name: username }).click();
cy.findByRole('main').within(() => {
cy.findByRole('link', { name: /Emails/ }).should('be.visible');
cy.findByRole('link', { name: /Notes/ }).should('be.visible');
cy.findByRole('link', { name: /Credits/ }).should('be.visible');
cy.findByRole('link', { name: /Organizations/ }).should('be.visible');
cy.findByRole('link', { name: /Reports/ }).should('be.visible');
cy.findByRole('link', { name: /Reactions/ }).should('be.visible');
});
});
});
});
describe('Emails', () => {
it('Verifies the email', () => {
cy.get('@user').then(({ username }) => {
cy.visitAndWaitForUserSideEffects('/admin/users');
cy.findByRole('link', { name: username }).click();
cy.findByRole('link', { name: /Emails/ }).click();
cy.findByRole('button', { name: 'Verify Email Ownership' }).as(
'verifyEmailOwnership',
);
cy.get('@verifyEmailOwnership').within((button) => {
button.click();
});
cy.findByTestId('snackbar').should(
'have.text',
'Verification email sent!',
);
});
});
it('Sends an email to the user and checks its presence in the history', () => {
cy.get('@user').then(({ username }) => {
cy.visitAndWaitForUserSideEffects('/admin/users');
cy.findByRole('link', { name: username }).click();
cy.findByRole('link', { name: /Emails/ }).click();
// Send email
cy.findByRole('textbox', { name: 'Subject' }).type('Hello!');
cy.findByRole('textbox', { name: 'Body' }).type('This is an email');
cy.findByRole('button', { name: 'Send Email' }).click();
// Check message coming from the server
cy.findByTestId('snackbar').should('have.text', 'Email sent!');
// Go back to check its presence in the history
cy.visitAndWaitForUserSideEffects('/admin/users');
cy.findByRole('link', { name: username }).click();
cy.findByRole('link', { name: /Emails/ }).click();
cy.findAllByText(/Emails history/)
.first()
.within((details) => {
details.click(); // open the details
});
// Check the email is present in the details
cy.findByRole('link', { name: /Hello!/ }).should('exist');
});
});
});
describe('Notes', () => {
it('Creates a note and checks its presence in the history', () => {
cy.get('@user').then(({ username }) => {
cy.visitAndWaitForUserSideEffects('/admin/users');
cy.findByRole('link', { name: username }).click();
cy.findByRole('link', { name: /Notes/ }).click();
cy.findByRole('textbox', { name: 'Add a new note' }).type(
'Hello, this is a note about them',
);
cy.findByRole('button', { name: 'Create Note' }).click();
cy.findByTestId('snackbar').should('have.text', 'Note created!');
// Go back to check its presence in the history
cy.visitAndWaitForUserSideEffects('/admin/users');
cy.findByRole('link', { name: username }).click();
cy.findByRole('link', { name: /Notes/ }).click();
cy.findAllByText(/Recent Notes/)
.first()
.within((details) => {
details.click(); // open the details
});
cy.findAllByText(/a note about them/).should('exist');
});
});
});
describe('Credits', () => {
it('Add credits for the user', () => {
cy.get('@user').then(({ username }) => {
cy.visitAndWaitForUserSideEffects('/admin/users');
cy.findByRole('link', { name: username }).click();
cy.findByRole('link', { name: /Credits/ }).click();
cy.findByRole('group', { name: 'Add credits' }).within(() => {
cy.findByRole('spinbutton', { name: /Number/i })
.type(1)
.trigger('change');
cy.findByRole('textbox', { name: /Reason/i }).type(
'Increasing credits',
);
cy.findAllByRole('button', { name: 'Add' }).first().click();
});
cy.findByTestId('snackbar').should('have.text', 'Added 1 credit!');
});
});
it('Removes credits for the user', () => {
cy.get('@user').then(({ username }) => {
cy.visitAndWaitForUserSideEffects('/admin/users');
cy.findByRole('link', { name: username }).click();
cy.findByRole('link', { name: /Credits/ }).click();
cy.findByRole('group', { name: 'Remove credits' }).within(() => {
cy.findByRole('spinbutton', { name: /Number/i })
.type(1)
.trigger('change');
cy.findByRole('textbox', { name: /Reason/i }).type(
'Decreasing credits',
);
cy.findAllByRole('button', { name: 'Remove' }).first().click();
});
cy.findByTestId('snackbar').should('have.text', 'Removed 1 credit!');
});
});
it('Add credits to the organization', () => {
cy.get('@user').then(({ username }) => {
cy.visitAndWaitForUserSideEffects('/admin/users');
cy.findByRole('link', { name: username }).click();
cy.findByRole('link', { name: /Credits/ }).click();
cy.findByRole('group', { name: 'Add credits to organizations' }).within(
() => {
cy.findByRole('spinbutton', { name: /Number/i })
.type(1)
.trigger('change');
cy.findByRole('textbox', { name: /Reason/i }).type(
'Increasing credits',
);
cy.findAllByRole('button', { name: 'Add' }).first().click();
},
);
cy.findByTestId('snackbar').should('have.text', 'Added 1 credit!');
});
});
it('Removes credits from the organization', () => {
cy.get('@user').then(({ username }) => {
cy.visitAndWaitForUserSideEffects('/admin/users');
cy.findByRole('link', { name: username }).click();
cy.findByRole('link', { name: /Credits/ }).click();
cy.findByRole('group', {
name: 'Remove credits from organizations',
}).within(() => {
cy.findByRole('spinbutton', { name: /Number/i })
.type(1)
.trigger('change');
cy.findByRole('textbox', { name: /Reason/i }).type(
'Decreasing credits',
);
cy.findAllByRole('button', { name: 'Remove' }).first().click();
});
cy.findByTestId('snackbar').should('have.text', 'Removed 1 credit!');
});
});
});
describe('Organizations', () => {
it('Changes a user membership level within an organization', () => {
cy.get('@user').then(({ username }) => {
cy.visitAndWaitForUserSideEffects('/admin/users');
cy.findByRole('link', { name: username }).click();
cy.findByRole('link', { name: /Organizations/ }).click();
cy.findAllByText(/Manage memberships/)
.first()
.within((details) => {
details.click();
});
// Change role
cy.findAllByRole('combobox', { name: /Membership level/i })
.last()
.select('Member');
cy.findAllByRole('button', { name: 'Update Permissions' })
.first()
.click();
cy.findByTestId('snackbar').contains(
/User was successfully updated to member/i,
);
});
});
it('Removes a user from an organization', () => {
cy.get('@user').then(({ username }) => {
cy.visitAndWaitForUserSideEffects('/admin/users');
cy.findByRole('link', { name: username }).click();
cy.findByRole('link', { name: /Organizations/ }).click();
cy.findAllByText(/Manage memberships/)
.first()
.within((details) => {
details.click();
});
cy.findAllByRole('button', { name: /Remove from organization/i })
.first()
.click();
cy.findByTestId('snackbar').contains(/User was successfully removed/i);
});
});
});
describe('Reports', () => {
it('Shows a report', () => {
cy.get('@user').then(({ username }) => {
cy.visitAndWaitForUserSideEffects('/admin/users');
cy.findByRole('link', { name: username }).click();
cy.findByRole('link', { name: /Reports/ }).click();
cy.findByRole('link', { name: /a bug/ }).should('be.visible');
});
});
});
describe('Reactions', () => {
it('Shows a reaction', () => {
cy.get('@user').then(({ username }) => {
cy.visitAndWaitForUserSideEffects('/admin/users');
cy.findByRole('link', { name: username }).click();
cy.findByRole('link', { name: /Reactions/ }).click();
cy.findByRole('link', { name: /Flag\s+user/i }).should('be.visible');
});
});
});
});

View file

@ -1,61 +0,0 @@
require "rails_helper"
RSpec.describe Admin::Users::Tools::CreditsComponent, type: :component do
let(:user) { create(:user) }
it "renders the header", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("h3", text: "← Tools")
expect(rendered_component).to have_link(href: admin_user_tools_path(user))
end
it "renders the section title for the screen reader", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("div", id: "section-title", class: "hidden")
end
describe "Add user credits" do
it "renders the section", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text("available user credit".pluralize(user.unspent_credits_count))
expect(rendered_component).to have_css("form[action='#{admin_user_tools_credits_path(user)}']")
end
end
describe "Remove credits" do
it "does not render the section by default" do
render_inline(described_class.new(user: user))
expect(rendered_component).not_to have_text("Remove credits")
end
it "renders the section if the user has unspent credits" do
Credit.add_to(user, 1)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("form[action='#{admin_user_tools_credits_path(user)}']")
end
end
describe "Organization credits" do
it "does not render the section by default", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).not_to have_text("Add credits to organizations")
expect(rendered_component).not_to have_text("Remove credits from organizations")
end
it "renders the section if the user belongs to an organization", :aggregate_failures do
create(:organization_membership, user: user)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css('form[id="org_credits_add"]')
expect(rendered_component).to have_css('form[id="org_credits_remove"]')
end
end
end

View file

@ -1,60 +0,0 @@
require "rails_helper"
RSpec.describe Admin::Users::Tools::EmailsComponent, type: :component do
let(:user) { create(:user) }
it "renders the header", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("h3", text: "← Tools")
expect(rendered_component).to have_link(href: admin_user_tools_path(user))
end
it "renders the section title for the screen reader", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("div", id: "section-title", class: "hidden")
end
describe "Verification" do
it "renders the section", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text("To: #{user.name}")
expect(rendered_component).to have_text(user.email)
expect(rendered_component).not_to have_text("Verified on")
expect(rendered_component).to have_css("form[action='#{verify_email_ownership_admin_user_path(user)}']")
end
it "renders the section with verification info" do
allow(EmailAuthorization).to receive(:last_verification_date).with(user).and_return(1.day.ago)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text("Verified on")
end
end
describe "Send Email" do
it "renders the section" do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("form[action='#{send_email_admin_user_path(user)}']")
end
end
describe "Email history" do
it "does not render the section by default" do
render_inline(described_class.new(user: user))
expect(rendered_component).not_to have_text("Emails history")
end
it "renders the section if the user has email messages", :aggregate_failures do
email = create(:email_message, user: user)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text("Emails history")
expect(rendered_component).to have_link(href: admin_user_email_message_path(user, email), visible: :hidden)
end
end
end

View file

@ -1,46 +0,0 @@
require "rails_helper"
RSpec.describe Admin::Users::Tools::NotesComponent, type: :component do
let(:user) { create(:user) }
it "renders the header", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("h3", text: "← Tools")
expect(rendered_component).to have_link(href: admin_user_tools_path(user))
end
it "renders the section title for the screen reader", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("div", id: "section-title", class: "hidden")
end
describe "Create note" do
it "renders the section", :aggregate_failures do
render_inline(described_class.new(user: user))
selector = "form[action='#{admin_user_tools_notes_path(user)}'][method='post'][data-remote='true']"
expect(rendered_component).to have_css(selector)
end
end
describe "Notes history" do
it "does not render the section by default" do
render_inline(described_class.new(user: user))
expect(rendered_component).not_to have_text("Recent Notes")
end
it "renders the section if the user has notes", :aggregate_failures do
note = create(:note, noteable: user, author: create(:user))
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text("Recent Notes")
expect(rendered_component).to have_text(note.created_at.strftime("%d %B %Y %H:%M %Z"))
expect(rendered_component).to have_text(note.author.username)
expect(rendered_component).to have_text(note.reason)
expect(rendered_component).to have_text(note.content)
end
end
end

View file

@ -1,44 +0,0 @@
require "rails_helper"
RSpec.describe Admin::Users::Tools::OrganizationsComponent, type: :component do
let(:user) { create(:user) }
it "renders the header", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("h3", text: "← Tools")
expect(rendered_component).to have_link(href: admin_user_tools_path(user))
end
it "renders the section title for the screen reader", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("div", id: "section-title", class: "hidden")
end
describe "Add new org membership" do
it "renders the section", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text("Add #{user.name} to a new organization")
expect(rendered_component).to have_css("form[action='#{admin_organization_memberships_path}']")
end
end
describe "Manage memberships" do
it "does not render the section by default" do
render_inline(described_class.new(user: user))
expect(rendered_component).not_to have_text("Manage memberships")
end
it "renders the section if the user belongs to an organization", :aggregate_failures do
membership = create(:organization_membership, user: user)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text(membership.organization.name)
expect(rendered_component).to have_css("input[value='delete']", visible: :hidden)
end
end
end

View file

@ -1,58 +0,0 @@
require "rails_helper"
RSpec.describe Admin::Users::Tools::ReactionsComponent, type: :component do
let(:user) { create(:user) }
let(:moderator) { create(:user, :trusted) }
it "renders the header", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("h3", text: "← Tools")
expect(rendered_component).to have_link(href: admin_user_tools_path(user))
end
it "renders the section title for the screen reader", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("div", id: "section-title", class: "hidden")
end
describe "View reactions" do
it "does not render the section by default" do
render_inline(described_class.new(user: user))
expect(rendered_component).not_to have_css("article")
end
it "renders the section if the user receives a vomit on one of their articles", :aggregate_failures do
article = create(:article, user: user)
reaction = create(:vomit_reaction, user: moderator, reactable: article)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text(I18n.t("views.reactions.category.vomit"))
expect(rendered_component).to have_text(reaction.reactable_type)
expect(rendered_component).to have_text(reaction.reactable.title)
end
it "renders the section if the user receives a vomit on one of their comments", :aggregate_failures do
comment = create(:comment, user: user)
reaction = create(:vomit_reaction, user: moderator, reactable: comment)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text(I18n.t("views.reactions.category.vomit"))
expect(rendered_component).to have_text(reaction.reactable_type)
expect(rendered_component).to have_text(reaction.reactable.title)
end
it "renders the section if the user left vomit reactions", :aggregate_failures do
reaction = create(:vomit_reaction, user: moderator, reactable: user)
render_inline(described_class.new(user: moderator))
expect(rendered_component).to have_text(I18n.t("views.reactions.category.vomit"))
expect(rendered_component).to have_text(reaction.reactable_type)
end
end
end

View file

@ -1,56 +0,0 @@
require "rails_helper"
RSpec.describe Admin::Users::Tools::ReportsComponent, type: :component do
let(:user) { create(:user) }
it "renders the header", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("h3", text: "← Tools")
expect(rendered_component).to have_link(href: admin_user_tools_path(user))
end
it "renders the section title for the screen reader", :aggregate_failures do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("div", id: "section-title", class: "hidden")
end
describe "View reports" do
it "does not render the section by default" do
render_inline(described_class.new(user: user))
expect(rendered_component).not_to have_css("article")
end
it "renders the section if the user leaves a report", :aggregate_failures do
report = create(:feedback_message, reporter: user)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text(report.category.capitalize)
expect(rendered_component).to have_text(report.status)
expect(rendered_component).to have_text(report.message)
end
it "renders the section if the user is affected by a report", :aggregate_failures do
report = create(:feedback_message, affected: user)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text(report.category.capitalize)
expect(rendered_component).to have_text(report.status)
expect(rendered_component).to have_text(report.message)
end
it "renders the section if the user is reported", :aggregate_failures do
report = create(:feedback_message, offender: user)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text(report.category.capitalize)
expect(rendered_component).to have_text(report.status)
expect(rendered_component).to have_text(report.message)
end
end
end

View file

@ -1,116 +0,0 @@
require "rails_helper"
RSpec.describe Admin::Users::ToolsComponent, type: :component do
let(:user) { create(:user) }
describe "Emails" do
it "renders the header" do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("h4", text: "Emails")
end
it "renders the emails count" do
create(:email_message, user: user)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text("1 past email")
end
it "does not render the Verified text by default" do
render_inline(described_class.new(user: user))
expect(rendered_component).not_to have_text("Verified")
end
it "render the Verified text when verified is true" do
allow(EmailAuthorization).to receive(:last_verification_date).with(user).and_return(Time.current)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text("Verified")
end
end
describe "Notes" do
it "renders the header" do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("h4", text: "Notes")
end
it "renders the notes count" do
create(:note, noteable: user)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text("1 note")
end
end
describe "Credits" do
it "renders the header" do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("h4", text: "Credits")
end
it "renders the credits count" do
Credit.add_to(user, 1)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text("1 credit")
end
end
describe "Organizations" do
it "renders the header" do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("h4", text: "Organizations")
end
it "renders the credits count" do
create(:organization_membership, user: user)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text("1 organization")
end
end
describe "Reports" do
it "renders the header" do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("h4", text: "Reports")
end
it "renders the credits count" do
create(:feedback_message, reporter: user)
render_inline(described_class.new(user: user))
expect(rendered_component).to have_text("1 report")
end
end
describe "Reactions" do
it "renders the header" do
render_inline(described_class.new(user: user))
expect(rendered_component).to have_css("h4", text: "Reactions")
end
it "renders the credits count" do
moderator = create(:user, :trusted)
create(:vomit_reaction, user: moderator, reactable: user)
render_inline(described_class.new(user: moderator))
expect(rendered_component).to have_text("1 reaction")
end
end
end

View file

@ -1,155 +0,0 @@
require "rails_helper"
RSpec.describe "/admin/users/:user_id/tools/credits", type: :request do
include_examples "Admin::Users::Tools::ShowAction", :admin_user_tools_credits_path,
Admin::Users::Tools::CreditsComponent
describe "#create" do
let(:user) { create(:user) }
it "returns not found for non existing users" do
expect { post admin_user_tools_credits_path(9999) }.to raise_error(ActiveRecord::RecordNotFound)
end
it "returns :unprocessable_entity if a param is invalid", :aggregate_failures do
expect do
post admin_user_tools_credits_path(user), params: {
credits: {
count: :a
}
}, xhr: true
end.to not_change(user.credits, :count)
.and not_change(user.notes, :count)
expect(response).to have_http_status(:unprocessable_entity)
expect(response.parsed_body["error"]).to be_present
end
it "adds credits to the user and a note", :aggregate_failures do
expect do
post admin_user_tools_credits_path(user), params: {
credits: {
count: 1,
note: "Test"
}
}, xhr: true
end.to change(user.credits, :count).by(1)
.and change(user.notes, :count).by(1)
expect(response).to have_http_status(:created)
end
it "adds credits to the user's organization and a note", :aggregate_failures do
membership = create(:organization_membership, user: user)
expect do
post admin_user_tools_credits_path(user), params: {
credits: {
count: 1,
note: "Test",
organization_id: membership.organization_id
}
}, xhr: true
end.to change(membership.organization.credits, :count).by(1)
.and change(user.notes, :count).by(1)
.and not_change(user.credits, :count)
expect(response).to have_http_status(:created)
end
it "returns a JSON result", :aggregate_failures do
post admin_user_tools_credits_path(user), params: {
credits: {
count: 1,
note: "Test"
}
}, xhr: true
expect(response.media_type).to eq("application/json")
expect(response.parsed_body["result"]).to be_present
end
end
describe "#destroy" do
let(:user) { create(:user) }
it "returns not found for non existing users" do
expect { delete admin_user_tools_credits_path(9999) }.to raise_error(ActiveRecord::RecordNotFound)
end
it "returns :unprocessable_entity if a param is invalid", :aggregate_failures do
expect do
delete admin_user_tools_credits_path(user), params: {
credits: {
count: :a
}
}, xhr: true
end.to not_change(user.credits, :count)
.and not_change(user.notes, :count)
expect(response).to have_http_status(:unprocessable_entity)
expect(response.parsed_body["error"]).to be_present
end
it "does not remove credits if none are available", :aggregate_failures do
expect do
delete admin_user_tools_credits_path(user), params: {
credits: {
count: 1,
note: "Test"
}
}, xhr: true
end.to change(user.credits, :count).by(0)
.and change(user.notes, :count).by(1)
expect(response).to have_http_status(:ok)
end
it "removes credits from the user and adds a note", :aggregate_failures do
Credit.add_to(user, 1)
expect do
delete admin_user_tools_credits_path(user), params: {
credits: {
count: 1,
note: "Test"
}
}, xhr: true
end.to change(user.credits, :count).by(-1)
.and change(user.notes, :count).by(1)
expect(response).to have_http_status(:ok)
end
it "remove credits from the user's organization and adds a note", :aggregate_failures do
organization = create(:organization_membership, user: user).organization
Credit.add_to(organization, 1)
expect do
delete admin_user_tools_credits_path(user), params: {
credits: {
count: 1,
note: "Test",
organization_id: organization.id
}
}, xhr: true
end.to change(organization.credits, :count).by(-1)
.and change(user.notes, :count).by(1)
.and not_change(user.credits, :count)
expect(response).to have_http_status(:ok)
end
it "returns a JSON result", :aggregate_failures do
delete admin_user_tools_credits_path(user), params: {
credits: {
count: 1,
note: "Test"
}
}, xhr: true
expect(response.media_type).to eq("application/json")
expect(response.parsed_body["result"]).to be_present
end
end
end

View file

@ -1,6 +0,0 @@
require "rails_helper"
RSpec.describe "/admin/users/:user_id/tools/emails", type: :request do
include_examples "Admin::Users::Tools::ShowAction", :admin_user_tools_emails_path,
Admin::Users::Tools::EmailsComponent
end

View file

@ -1,38 +0,0 @@
require "rails_helper"
RSpec.describe "/admin/users/:user_id/tools/notes", type: :request do
include_examples "Admin::Users::Tools::ShowAction", :admin_user_tools_notes_path,
Admin::Users::Tools::NotesComponent
describe "#create" do
let(:user) { create(:user) }
it "returns not found for non existing users" do
expect { post admin_user_tools_notes_path(9999) }.to raise_error(ActiveRecord::RecordNotFound)
end
it "returns :unprocessable_entity if a param is invalid", :aggregate_failures do
expect do
post admin_user_tools_notes_path(user), params: { note: { content: nil } }, xhr: true
end.to not_change(user.notes, :count)
expect(response).to have_http_status(:unprocessable_entity)
expect(response.parsed_body["error"]).to be_present
end
it "adds a note to the user", :aggregate_failures do
expect do
post admin_user_tools_notes_path(user), params: { note: { content: "Test" } }, xhr: true
end.to change(user.notes, :count).by(1)
expect(response).to have_http_status(:created)
end
it "returns a JSON result", :aggregate_failures do
post admin_user_tools_notes_path(user), params: { note: { content: "Test" } }, xhr: true
expect(response.media_type).to eq("application/json")
expect(response.parsed_body["result"]).to be_present
end
end
end

View file

@ -1,6 +0,0 @@
require "rails_helper"
RSpec.describe "/admin/users/:user_id/tools/organizations", type: :request do
include_examples "Admin::Users::Tools::ShowAction", :admin_user_tools_organizations_path,
Admin::Users::Tools::OrganizationsComponent
end

View file

@ -1,6 +0,0 @@
require "rails_helper"
RSpec.describe "/admin/users/:user_id/tools/organizations", type: :request do
include_examples "Admin::Users::Tools::ShowAction", :admin_user_tools_reactions_path,
Admin::Users::Tools::ReactionsComponent
end

View file

@ -1,6 +0,0 @@
require "rails_helper"
RSpec.describe "/admin/users/:user_id/tools/organizations", type: :request do
include_examples "Admin::Users::Tools::ShowAction", :admin_user_tools_reports_path,
Admin::Users::Tools::ReportsComponent
end

View file

@ -1,6 +0,0 @@
require "rails_helper"
require "view_component/test_helpers"
RSpec.describe "/admin/users/:user_id/tools", type: :request do
include_examples "Admin::Users::Tools::ShowAction", :admin_user_tools_path, Admin::Users::ToolsComponent
end

View file

@ -26,14 +26,6 @@ RSpec.describe "/admin/users", type: :request do
expect(response.body).not_to include("Go back to All members")
end
it "renders the new admin page if the feature flag is enabled" do
FeatureFlag.enable(:new_admin_members, admin)
get admin_user_path(user)
expect(response.body).to include("Go back to All members")
end
context "when a user is unregistered" do
it "renders a message stating that the user isn't registered" do
user.update_columns(registered: false)

View file

@ -1,8 +0,0 @@
require "capybara/rspec"
require "view_component/test_helpers"
RSpec.configure do |config|
config.include Capybara::RSpecMatchers, type: :component
config.include ViewComponent::TestHelpers, type: :component
config.include Rails.application.routes.url_helpers, type: :component
end

View file

@ -56,9 +56,6 @@ seeder.create_if_doesnt_exist(User, "email", "admin@forem.local") do
user.add_role(:super_admin)
user.add_role(:single_resource_admin, Config)
user.add_role(:trusted)
# Enable new Admin Members
FeatureFlag.enable(:new_admin_members, user)
end
admin_user = User.find_by(email: "admin@forem.local")

View file

@ -1,39 +0,0 @@
require "view_component/test_helpers"
RSpec.shared_examples "Admin::Users::Tools::ShowAction" do |path_helper, component|
include ViewComponent::TestHelpers
let(:admin) { create(:user, :super_admin) }
before do
sign_in(admin)
end
describe "#show" do
let(:user) { create(:user) }
it "returns not found for non existing users" do
expect { get public_send(path_helper, 9999) }.to raise_error(ActiveRecord::RecordNotFound)
end
it "renders successfully" do
get public_send(path_helper, user)
expect(response).to have_http_status(:ok)
end
it "returns HTML" do
get public_send(path_helper, user)
expect(response.media_type).to eq("text/html")
end
it "renders the EmailsComponent" do
get public_send(path_helper, user)
render_inline(component.new(user: user))
expect(response.body).to eq(rendered_component)
end
end
end