Heavy Confirmation Flow for DisplayAd-Destroy (#14862)

* wham, bam, done!

* refactor and remove switch statement

* add guards to handleRecord function

* make cypress spec more robust
This commit is contained in:
Arit Amana 2021-10-04 08:36:43 -04:00 committed by GitHub
parent 79a537b62d
commit 9d1eaee84e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 182 additions and 54 deletions

View file

@ -51,11 +51,9 @@ module Admin
@display_ad = DisplayAd.find(params[:id])
if @display_ad.destroy
flash[:success] = "Display Ad has been deleted!"
redirect_to admin_display_ads_path
render json: { message: "Display Ad has been deleted!" }, status: :ok
else
flash[:danger] = "Something went wrong with deleting the Display Ad."
render :edit
render json: { error: "Something went wrong with deleting the Display Ad." }, status: :unprocessable_entity
end
end

View file

@ -13,20 +13,38 @@ window.addEventListener('load', () => {
}
});
const nonRedirectEndpoints = [
'/admin/content_manager/badge_achievements',
'/admin/customization/display_ads',
];
const redirectEndpoints = ['/admin/advanced/broadcasts'];
export default class ConfirmationModalController extends ModalController {
static targets = ['itemId', 'username', 'endpoint'];
removeRecordAsync({ id, outcome }) {
document.querySelector(`[data-row-id="${id}"]`).remove();
displaySnackbar(outcome.message);
}
redirectAfterDestroy({ endpoint, outcome }) {
localStorage.setItem('outcome', outcome.message);
window.location.replace(`${endpoint}?redirected`);
}
handleRecord({ endpoint, id, outcome }) {
switch (endpoint) {
case '/admin/content_manager/badge_achievements':
document.querySelector(`[data-row-id="${id}"]`).remove();
displaySnackbar(outcome.message);
break;
case '/admin/advanced/broadcasts':
localStorage.setItem('outcome', outcome.message);
window.location.replace(`${endpoint}?redirected`);
break;
if (nonRedirectEndpoints.includes(endpoint)) {
this.removeRecordAsync({ id, outcome });
return;
}
if (redirectEndpoints.includes(endpoint)) {
this.redirectAfterDestroy({ endpoint, outcome });
return;
}
displayErrorAlert('Something went wrong.');
}
async sendToEndpoint({ itemId, endpoint }) {

View file

@ -1,41 +1,56 @@
<nav class="flex mb-4" aria-label="Display Ads navigation">
<%= form_tag(admin_display_ads_path, method: "get") do %>
<%= text_field_tag(:search, params[:search], aria: { label: "Search" }, class: "crayons-header--search-input crayons-textfield", placeholder: "Search by Org name") %>
<% end %>
<div class="ml-auto">
<div class="justify-content-end">
<%= link_to "Make A New Display Ad", new_admin_display_ad_path, class: "crayons-btn" %>
</div>
</div>
</nav>
<%= paginate @display_ads %>
<table class="crayons-table" width="100%">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Organization</th>
<th scope="col">Placement Area</th>
<th scope="col">Published</th>
<th scope="col">Approved</th>
<th scope="col">Success Rate</th>
</tr>
</thead>
<tbody class="crayons-card">
<% @display_ads.each do |display_ad| %>
<tr>
<td><%= link_to display_ad.id, edit_admin_display_ad_path(display_ad) %></td>
<td><%= link_to display_ad.organization.name, admin_organization_path(display_ad.organization) %></td>
<td><%= display_ad.human_readable_placement_area %></td>
<td><%= display_ad.published %></td>
<td><%= display_ad.approved %></td>
<td><%= display_ad.success_rate %></td>
<td><%= link_to "Edit", edit_admin_display_ad_path(display_ad), class: "crayons-btn" %></td>
<td><%= link_to "Destroy", admin_display_ad_path(display_ad), class: "crayons-btn crayons-btn--danger", method: :delete, data: { confirm: "Are you sure?" } %></td>
</tr>
<div
data-controller="confirmation-modal"
data-confirmation-modal-root-selector-value="#confirmation-modal-root"
data-confirmation-modal-content-selector-value="#confirmation-modal"
data-confirmation-modal-title-value="Confirm changes"
data-confirmation-modal-size-value="m">
<nav class="flex mb-4" aria-label="Display Ads navigation">
<%= form_tag(admin_display_ads_path, method: "get") do %>
<%= text_field_tag(:search, params[:search], aria: { label: "Search" }, class: "crayons-header--search-input crayons-textfield", placeholder: "Search by Org name") %>
<% end %>
</tbody>
</table>
<div class="ml-auto">
<div class="justify-content-end">
<%= link_to "Make A New Display Ad", new_admin_display_ad_path, class: "crayons-btn" %>
</div>
</div>
</nav>
<%= paginate @display_ads %>
<%= paginate @display_ads %>
<table class="crayons-table" width="100%">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Organization</th>
<th scope="col">Placement Area</th>
<th scope="col">Published</th>
<th scope="col">Approved</th>
<th scope="col">Success Rate</th>
</tr>
</thead>
<tbody class="crayons-card">
<% @display_ads.each do |display_ad| %>
<tr data-row-id="<%= display_ad.id %>">
<td><%= link_to display_ad.id, edit_admin_display_ad_path(display_ad) %></td>
<td><%= link_to display_ad.organization.name, admin_organization_path(display_ad.organization) %></td>
<td><%= display_ad.human_readable_placement_area %></td>
<td><%= display_ad.published %></td>
<td><%= display_ad.approved %></td>
<td><%= display_ad.success_rate %></td>
<td><%= link_to "Edit", edit_admin_display_ad_path(display_ad), class: "crayons-btn" %></td>
<td>
<button
class="crayons-btn crayons-btn--danger"
data-item-id="<%= display_ad.id %>"
data-endpoint="/admin/customization/display_ads"
data-username="<%= current_user.username %>"
data-action="click->confirmation-modal#openModal">Destroy</button>
</td>
</tr>
<% end %>
</tbody>
</table>
<%= render partial: "admin/shared/destroy_confirmation_modal" %>
<%= paginate @display_ads %>
</div>

View file

@ -0,0 +1,87 @@
describe('Display Ads', () => {
beforeEach(() => {
cy.testSetup();
cy.fixture('users/adminUser.json').as('user');
cy.get('@user').then((user) => {
cy.loginAndVisit(user, '/admin/customization/display_ads');
cy.findByRole('table').within(() => {
cy.findByRole('button', { name: 'Destroy' }).click();
});
});
});
describe('delete a display ad', () => {
it('should display confirmation modal', () => {
cy.findByRole('dialog').contains('Confirm changes').should('be.visible');
});
it('should display warning text if confirmation text does not match', () => {
cy.findByRole('dialog').within(() => {
cy.get('input').type('Text that does not match.');
cy.findByRole('button', { name: 'Confirm changes' }).click();
cy.get('.crayons-notice')
.contains('The confirmation text did not match.')
.should('be.visible');
cy.findByRole('button', { name: 'Close' }).click();
});
cy.findByRole('table').within(() => {
cy.findByRole('button', { name: 'Destroy' }).should('be.visible');
});
});
it('should remove display ad if confirmation text matches', () => {
cy.get('@user').then((user) => {
cy.findByRole('dialog').within(() => {
cy.get('input').type(
`My username is @${user.username} and this action is 100% safe and appropriate.`,
);
cy.findByRole('button', { name: 'Confirm changes' }).click();
});
cy.findByTestId('snackbar').within(() => {
cy.findByRole('alert').should(
'have.text',
'Display Ad has been deleted!',
);
});
cy.findByRole('table').within(() => {
cy.findByRole('button', { name: 'Destroy' }).should('not.exist');
});
});
});
it('generates error message when remove action fails', () => {
cy.intercept('DELETE', '/admin/customization/display_ads/**', {
statusCode: 422,
body: {
error: 'Something went wrong with deleting the Display Ad.',
},
});
cy.get('@user').then((user) => {
cy.findByRole('dialog').within(() => {
cy.get('input').type(
`My username is @${user.username} and this action is 100% safe and appropriate.`,
);
cy.findByRole('button', { name: 'Confirm changes' }).click();
});
cy.findByTestId('alertzone').within(() => {
cy.findByRole('alert')
.contains('Something went wrong with deleting the Display Ad.')
.should('be.visible');
});
cy.findByRole('table').within(() => {
cy.findByRole('button', { name: 'Destroy' }).should('be.visible');
});
});
});
});
});

View file

@ -77,7 +77,6 @@ RSpec.describe "/admin/customization/display_ads", type: :request do
expect do
delete admin_display_ad_path(display_ad.id)
end.to change { DisplayAd.all.count }.by(-1)
expect(response.body).to redirect_to admin_display_ads_path
end
end
end
@ -121,7 +120,6 @@ RSpec.describe "/admin/customization/display_ads", type: :request do
expect do
delete admin_display_ad_path(display_ad.id)
end.to change { DisplayAd.all.count }.by(-1)
expect(response.body).to redirect_to admin_display_ads_path
end
end
end

View file

@ -622,3 +622,15 @@ seeder.create_if_none(Broadcast) do
active: true,
)
end
##############################################################################
seeder.create_if_none(DisplayAd) do
DisplayAd.create!(
organization_id: 1,
body_markdown: "<h1>This is an add</h1>",
placement_area: "sidebar_left",
published: true,
approved: true,
)
end