mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-23 15:48:43 -05:00
Invite Users step
This commit is contained in:
parent
35b767f6af
commit
ef84981e38
19 changed files with 373 additions and 62 deletions
|
@ -0,0 +1,16 @@
|
||||||
|
import computed from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNames: ['invite-list-user'],
|
||||||
|
|
||||||
|
@computed('user.role')
|
||||||
|
roleName(role) {
|
||||||
|
return this.get('roles').findProperty('id', role).label;
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
removeUser(user) {
|
||||||
|
this.sendAction('removeUser', user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
62
app/assets/javascripts/wizard/components/invite-list.js.es6
Normal file
62
app/assets/javascripts/wizard/components/invite-list.js.es6
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNames: ['invite-list'],
|
||||||
|
users: null,
|
||||||
|
inviteEmail: '',
|
||||||
|
inviteRole: '',
|
||||||
|
invalid: false,
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._super();
|
||||||
|
this.set('users', []);
|
||||||
|
|
||||||
|
this.set('roles', [
|
||||||
|
{id: 'moderator', label: I18n.t('wizard.invites.roles.moderator') },
|
||||||
|
{id: 'regular', label: I18n.t('wizard.invites.roles.regular') },
|
||||||
|
]);
|
||||||
|
|
||||||
|
this.updateField();
|
||||||
|
},
|
||||||
|
|
||||||
|
keyPress(e) {
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.send('addUser');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateField() {
|
||||||
|
this.set('field.value', JSON.stringify(this.get('users')));
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
addUser() {
|
||||||
|
const user = {
|
||||||
|
email: this.get('inviteEmail') || '',
|
||||||
|
role: this.get('inviteRole')
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!/(.+)@(.+){2,}\.(.+){2,}/.test(user.email)) {
|
||||||
|
return this.set('invalid', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const users = this.get('users');
|
||||||
|
if (users.findProperty('email', user.email)) {
|
||||||
|
return this.set('invalid', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set('invalid', false);
|
||||||
|
|
||||||
|
users.pushObject(user);
|
||||||
|
this.updateField();
|
||||||
|
|
||||||
|
this.set('inviteEmail', '');
|
||||||
|
Ember.run.scheduleOnce('afterRender', () => this.$('.invite-email').focus());
|
||||||
|
},
|
||||||
|
|
||||||
|
removeUser(user) {
|
||||||
|
this.get('users').removeObject(user);
|
||||||
|
this.updateField();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,6 @@
|
||||||
|
<span class='email'>{{user.email}}</span>
|
||||||
|
<span class='role'>{{roleName}}</span>
|
||||||
|
|
||||||
|
<button class="wizard-btn small danger remove-user" {{action "removeUser" user}}>
|
||||||
|
{{fa-icon "times"}}
|
||||||
|
</button>
|
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
{{#if users}}
|
||||||
|
<div class="users-list">
|
||||||
|
{{#each users as |user|}}
|
||||||
|
{{invite-list-user user=user roles=roles removeUser="removeUser"}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="new-user">
|
||||||
|
<div class="text-field {{if invalid 'invalid'}}">
|
||||||
|
{{input class="invite-email" value=inviteEmail placeholder="user@example.com"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{combo-box value=inviteRole content=roles nameProperty="label" width="200px"}}
|
||||||
|
|
||||||
|
<button class="wizard-btn small add-user" {{action "addUser"}}>
|
||||||
|
{{fa-icon "plus"}}{{i18n "wizard.invites.add_user"}}
|
||||||
|
</button>
|
||||||
|
</div>
|
|
@ -1 +1,6 @@
|
||||||
{{combo-box class=fieldClass value=field.value content=field.choices nameProperty="label" width="400px"}}
|
{{combo-box elementId=field.id
|
||||||
|
class=fieldClass
|
||||||
|
value=field.value
|
||||||
|
content=field.choices
|
||||||
|
nameProperty="label"
|
||||||
|
width="400px"}}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{{input value=field.value class=fieldClass placeholder=field.placeholder}}
|
{{input elementId=field.id value=field.value class=fieldClass placeholder=field.placeholder}}
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
<label>
|
<label for={{field.id}}>
|
||||||
<span class='label-value'>{{field.label}}</span>
|
<span class='label-value'>{{field.label}}</span>
|
||||||
|
|
||||||
{{#if field.description}}
|
{{#if field.description}}
|
||||||
<div class='field-description'>{{{field.description}}}</div>
|
<div class='field-description'>{{{field.description}}}</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class='input-area'>
|
|
||||||
{{component inputComponentName field=field step=step fieldClass=fieldClass}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if field.errorDescription}}
|
|
||||||
<div class='field-error-description'>{{field.errorDescription}}</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<div class='input-area'>
|
||||||
|
{{component inputComponentName field=field step=step fieldClass=fieldClass}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if field.errorDescription}}
|
||||||
|
<div class='field-error-description'>{{field.errorDescription}}</div>
|
||||||
|
{{/if}}
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showNextButton}}
|
{{#if showNextButton}}
|
||||||
<button class='wizard-btn next' {{action "nextStep"}} disabled={{saving}}>
|
<button class='wizard-btn next primary' {{action "nextStep"}} disabled={{saving}}>
|
||||||
{{i18n "wizard.next"}}
|
{{i18n "wizard.next"}}
|
||||||
{{fa-icon "chevron-right"}}
|
{{fa-icon "chevron-right"}}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -1,4 +1,15 @@
|
||||||
module("Acceptance: wizard");
|
import startApp from 'wizard/test/helpers/start-app';
|
||||||
|
|
||||||
|
var wizard;
|
||||||
|
module("Acceptance: wizard", {
|
||||||
|
beforeEach() {
|
||||||
|
wizard = startApp();
|
||||||
|
},
|
||||||
|
|
||||||
|
teardown() {
|
||||||
|
Ember.run(wizard, 'destroy');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test("Wizard starts", assert => {
|
test("Wizard starts", assert => {
|
||||||
visit("/");
|
visit("/");
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { componentTest } from 'wizard/test/helpers/component-test';
|
||||||
|
|
||||||
|
moduleForComponent('invite-list', { integration: true });
|
||||||
|
|
||||||
|
componentTest('can add users', {
|
||||||
|
template: `{{invite-list field=field}}`,
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
this.set('field', {});
|
||||||
|
},
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
assert.ok(this.$('.users-list .invite-list-user').length === 0, 'no users at first');
|
||||||
|
assert.ok(this.$('.new-user .invalid').length === 0, 'not invalid at first');
|
||||||
|
|
||||||
|
const firstVal = JSON.parse(this.get('field.value'));
|
||||||
|
assert.equal(firstVal.length, 0, 'empty JSON at first');
|
||||||
|
|
||||||
|
click('.add-user');
|
||||||
|
andThen(() => {
|
||||||
|
assert.ok(this.$('.users-list .invite-list-user').length === 0, "doesn't add a blank user");
|
||||||
|
assert.ok(this.$('.new-user .invalid').length === 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
fillIn('.invite-email', 'eviltrout@example.com');
|
||||||
|
click('.add-user');
|
||||||
|
|
||||||
|
andThen(() => {
|
||||||
|
assert.ok(this.$('.users-list .invite-list-user').length === 1, 'adds the user');
|
||||||
|
assert.ok(this.$('.new-user .invalid').length === 0);
|
||||||
|
|
||||||
|
const val = JSON.parse(this.get('field.value'));
|
||||||
|
assert.equal(val.length, 1);
|
||||||
|
assert.equal(val[0].email, 'eviltrout@example.com', 'adds the email to the JSON');
|
||||||
|
assert.ok(val[0].role.length, 'adds the role to the JSON');
|
||||||
|
});
|
||||||
|
|
||||||
|
fillIn('.invite-email', 'eviltrout@example.com');
|
||||||
|
click('.add-user');
|
||||||
|
|
||||||
|
andThen(() => {
|
||||||
|
assert.ok(this.$('.users-list .invite-list-user').length === 1, "can't add the same user twice");
|
||||||
|
assert.ok(this.$('.new-user .invalid').length === 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
fillIn('.invite-email', 'not-an-email');
|
||||||
|
click('.add-user');
|
||||||
|
|
||||||
|
andThen(() => {
|
||||||
|
assert.ok(this.$('.users-list .invite-list-user').length === 1, "won't add an invalid email");
|
||||||
|
assert.ok(this.$('.new-user .invalid').length === 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
click('.invite-list .invite-list-user:eq(0) .remove-user');
|
||||||
|
andThen(() => {
|
||||||
|
assert.ok(this.$('.users-list .invite-list-user').length === 0, 'removed the user');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,16 @@
|
||||||
|
import initializer from 'wizard/initializers/load-helpers';
|
||||||
|
|
||||||
|
export function componentTest(name, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
|
||||||
|
test(name, function(assert) {
|
||||||
|
initializer.initialize();
|
||||||
|
|
||||||
|
if (opts.setup) {
|
||||||
|
opts.setup.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
andThen(() => this.render(opts.template));
|
||||||
|
andThen(() => opts.test.call(this, assert));
|
||||||
|
});
|
||||||
|
}
|
19
app/assets/javascripts/wizard/test/helpers/start-app.js.es6
Normal file
19
app/assets/javascripts/wizard/test/helpers/start-app.js.es6
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import Wizard from 'wizard/wizard';
|
||||||
|
import initializer from 'wizard/initializers/load-helpers';
|
||||||
|
|
||||||
|
let app;
|
||||||
|
let started = false;
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
Ember.run(() => app = Wizard.create({ rootElement: '#ember-testing' }));
|
||||||
|
|
||||||
|
if (!started) {
|
||||||
|
initializer.initialize();
|
||||||
|
app.start();
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
app.setupForTesting();
|
||||||
|
app.injectTestHelpers();
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
|
@ -12,8 +12,10 @@
|
||||||
//= require wizard-application
|
//= require wizard-application
|
||||||
//= require wizard-vendor
|
//= require wizard-vendor
|
||||||
//= require helpers/assertions
|
//= require helpers/assertions
|
||||||
|
//= require_tree ./helpers
|
||||||
//= require_tree ./acceptance
|
//= require_tree ./acceptance
|
||||||
//= require_tree ./models
|
//= require_tree ./models
|
||||||
|
//= require_tree ./components
|
||||||
//= require locales/en
|
//= require locales/en
|
||||||
//= require fake_xml_http_request
|
//= require fake_xml_http_request
|
||||||
//= require route-recognizer
|
//= require route-recognizer
|
||||||
|
@ -42,13 +44,10 @@ QUnit.testDone(function() {
|
||||||
server.shutdown();
|
server.shutdown();
|
||||||
});
|
});
|
||||||
|
|
||||||
var wizard = require('wizard/wizard').default.create({
|
require('wizard/test/helpers/start-app').default();
|
||||||
rootElement: '#ember-testing'
|
|
||||||
});
|
|
||||||
|
|
||||||
wizard.setupForTesting();
|
var buildResolver = require('discourse-common/resolver').buildResolver;
|
||||||
wizard.injectTestHelpers();
|
window.setResolver(buildResolver('wizard').create());
|
||||||
wizard.start();
|
|
||||||
|
|
||||||
Object.keys(requirejs.entries).forEach(function(entry) {
|
Object.keys(requirejs.entries).forEach(function(entry) {
|
||||||
if ((/\-test/).test(entry)) {
|
if ((/\-test/).test(entry)) {
|
||||||
|
|
|
@ -84,6 +84,11 @@ body.wizard {
|
||||||
color: #333;
|
color: #333;
|
||||||
box-shadow: 0 1px 4px rgba(0, 0, 0, .4);
|
box-shadow: 0 1px 4px rgba(0, 0, 0, .4);
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
|
@ -110,6 +115,42 @@ body.wizard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wizard-btn.primary {
|
||||||
|
background-color: #6699ff;
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 0, 0, .6);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #80B3FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #4D80E6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: #000167;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard-btn.danger {
|
||||||
|
background-color: #E60000;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #CC0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #B30000;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: #990000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.wizard-btn-upload {
|
.wizard-btn-upload {
|
||||||
clear: both;
|
clear: both;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -125,22 +166,6 @@ body.wizard {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.wizard-btn.next {
|
.wizard-btn.next {
|
||||||
background-color: #6699ff;
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 1px 4px rgba(0, 0, 0, .6);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: #80B3FF;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background-color: #4D80E6;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
background-color: #000167;
|
|
||||||
}
|
|
||||||
|
|
||||||
min-width: 70px;
|
min-width: 70px;
|
||||||
|
|
||||||
i.fa-chevron-right {
|
i.fa-chevron-right {
|
||||||
|
@ -240,29 +265,29 @@ body.wizard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.text-field {
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
font-size: 1.2em;
|
|
||||||
padding: 6px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
transition: border-color .5s;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.invalid {
|
|
||||||
input {
|
|
||||||
padding: 3px;
|
|
||||||
border: 4px solid red;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
margin-bottom: 2em;
|
margin-bottom: 2em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-field {
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 1.2em;
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
transition: border-color .5s;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.invalid {
|
||||||
|
input {
|
||||||
|
padding: 3px;
|
||||||
|
border: 4px solid red;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.radio-field-choice {
|
.radio-field-choice {
|
||||||
margin-bottom: 1.5em;
|
margin-bottom: 1.5em;
|
||||||
|
|
||||||
|
@ -280,3 +305,42 @@ body.wizard {
|
||||||
color: #777;
|
color: #777;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.invite-list {
|
||||||
|
.users-list {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
.invite-list-user {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
.email {
|
||||||
|
width: 330px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-user {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
.invite-email {
|
||||||
|
width: 350px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button.add-user {
|
||||||
|
.fa {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3234,3 +3234,11 @@ en:
|
||||||
upload: "Upload"
|
upload: "Upload"
|
||||||
uploading: "Uploading..."
|
uploading: "Uploading..."
|
||||||
|
|
||||||
|
invites:
|
||||||
|
add_user: "add"
|
||||||
|
roles:
|
||||||
|
admin: "Admin"
|
||||||
|
moderator: "Moderator"
|
||||||
|
regular: "Regular User"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3311,6 +3311,10 @@ en:
|
||||||
label: "Large Icon"
|
label: "Large Icon"
|
||||||
description: "Icon used for Apple touch devices. Recommended size is 144px by 144px."
|
description: "Icon used for Apple touch devices. Recommended size is 144px by 144px."
|
||||||
|
|
||||||
|
invites:
|
||||||
|
title: "Invite Staff"
|
||||||
|
description: "We recommend you invite some staff members to help you get things started."
|
||||||
|
|
||||||
finished:
|
finished:
|
||||||
title: "Your Discourse Forum is Ready!"
|
title: "Your Discourse Forum is Ready!"
|
||||||
description: |
|
description: |
|
||||||
|
|
|
@ -129,6 +129,18 @@ class Wizard
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@wizard.append_step('invites') do |step|
|
||||||
|
step.add_field(id: 'invite_list', type: 'component')
|
||||||
|
|
||||||
|
step.on_update do |updater|
|
||||||
|
users = JSON.parse(updater.fields[:invite_list])
|
||||||
|
|
||||||
|
users.each do |u|
|
||||||
|
Invite.create_invite_by_email(u['email'], @wizard.user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
DiscourseEvent.trigger(:build_wizard, @wizard)
|
DiscourseEvent.trigger(:build_wizard, @wizard)
|
||||||
|
|
||||||
@wizard.append_step('finished')
|
@wizard.append_step('finished')
|
||||||
|
|
|
@ -141,7 +141,6 @@ describe Wizard::StepUpdater do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "logos step" do
|
context "logos step" do
|
||||||
|
|
||||||
it "updates the fields correctly" do
|
it "updates the fields correctly" do
|
||||||
updater = wizard.create_updater('logos',
|
updater = wizard.create_updater('logos',
|
||||||
logo_url: '/uploads/logo.png',
|
logo_url: '/uploads/logo.png',
|
||||||
|
@ -158,5 +157,22 @@ describe Wizard::StepUpdater do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "invites step" do
|
||||||
|
|
||||||
|
let(:invites) {
|
||||||
|
return [{ email: 'regular@example.com', role: 'regular'},
|
||||||
|
{ email: 'moderator@example.com', role: 'moderator'}]
|
||||||
|
}
|
||||||
|
|
||||||
|
it "updates the fields correctly" do
|
||||||
|
updater = wizard.create_updater('invites', invite_list: invites.to_json)
|
||||||
|
updater.update
|
||||||
|
|
||||||
|
expect(updater).to be_success
|
||||||
|
|
||||||
|
expect(Invite.where(email: 'regular@example.com')).to be_present
|
||||||
|
expect(Invite.where(email: 'moderator@example.com')).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,12 +18,6 @@ describe ExtraLocalesController do
|
||||||
get :show, bundle: '-invalid..character!!'
|
get :show, bundle: '-invalid..character!!'
|
||||||
expect(response).to_not be_success
|
expect(response).to_not be_success
|
||||||
end
|
end
|
||||||
|
|
||||||
it "works with a valid bundle" do
|
|
||||||
get :show, bundle: 'admin'
|
|
||||||
expect(response).to be_success
|
|
||||||
expect(response.body).to be_present
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue