mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-09-23 02:49:03 +03:00
Add User Role Dropdown
Closes #3402, Closes #3428 ------------------- ### Components - Added GhostSelectComponent to handle async select creation (h/t @rwjblue) - Added GhostRolesSelector (extends GhostSelect) for displaying user role options - Created StoreInjector for surgically inserting the store into things that normally wouldn't have them. ### Users Settings - InviteNewUserModal now uses GhostRolesSelector & defaults to Author - The role dropdown for user settings has permissions set per 3402 ### User Model - Added `role` property as an interface to getting and setting `roles` - Refactored anything that set `roles` to set `role` - isAdmin, isAuthor, isOwner and isEditor are all keyed off of `role` now ### Tests - Added functional tests for Settings.Users - updated settings.users and settings.users.user screens - fix spacing on screens ### Server Fixtures - Fixed owner fixture's roles
This commit is contained in:
parent
80cbef8cdb
commit
21abed7f9a
@ -748,9 +748,9 @@ var path = require('path'),
|
|||||||
//
|
//
|
||||||
// You can use the `--target` argument to run any individual test file, or the admin or frontend tests:
|
// You can use the `--target` argument to run any individual test file, or the admin or frontend tests:
|
||||||
//
|
//
|
||||||
// `grunt test-functional --target=admin/editor_test.js` - run just the editor tests
|
// `grunt test-functional --target=client/editor_test.js` - run just the editor tests
|
||||||
//
|
//
|
||||||
// `grunt test-functional --target=admin/` - run all of the tests in the admin directory
|
// `grunt test-functional --target=client/` - run all of the tests in the client directory
|
||||||
//
|
//
|
||||||
// Functional tests are run with [phantom.js](http://phantomjs.org/) and defined using the testing api from
|
// Functional tests are run with [phantom.js](http://phantomjs.org/) and defined using the testing api from
|
||||||
// [casper.js](http://docs.casperjs.org/en/latest/testing.html).
|
// [casper.js](http://docs.casperjs.org/en/latest/testing.html).
|
||||||
|
13
core/client/components/gh-role-selector.js
Normal file
13
core/client/components/gh-role-selector.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import GhostSelect from 'ghost/components/gh-select';
|
||||||
|
|
||||||
|
var RolesSelector = GhostSelect.extend({
|
||||||
|
roles: Ember.computed.alias('options'),
|
||||||
|
options: Ember.computed(function () {
|
||||||
|
var rolesPromise = this.store.find('role', { permissions: 'assign' });
|
||||||
|
|
||||||
|
return Ember.ArrayProxy.extend(Ember.PromiseProxyMixin)
|
||||||
|
.create({promise: rolesPromise});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export default RolesSelector;
|
69
core/client/components/gh-select.js
Normal file
69
core/client/components/gh-select.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
//GhostSelect is a solution to Ember.Select being evil and worthless.
|
||||||
|
// (Namely, this solves problems with async data in Ember.Select)
|
||||||
|
//Inspired by (that is, totally ripped off from) this JSBin
|
||||||
|
//http://emberjs.jsbin.com/rwjblue/40/edit
|
||||||
|
|
||||||
|
//Usage:
|
||||||
|
//Extend this component and create a template for your component.
|
||||||
|
//Your component must define the `options` property.
|
||||||
|
//Optionally use `initialValue` to set the object
|
||||||
|
// you want to have selected to start with.
|
||||||
|
//Both options and initalValue are promise safe.
|
||||||
|
//Set onChange in your template to be the name
|
||||||
|
// of the action you want called in your
|
||||||
|
//For an example, see gh-roles-selector
|
||||||
|
|
||||||
|
var GhostSelect = Ember.Component.extend({
|
||||||
|
tagName: 'span',
|
||||||
|
classNames: ['gh-select'],
|
||||||
|
|
||||||
|
options: null,
|
||||||
|
initialValue: null,
|
||||||
|
|
||||||
|
resolvedOptions: null,
|
||||||
|
resolvedInitialValue: null,
|
||||||
|
|
||||||
|
//Convert promises to their values
|
||||||
|
init: function () {
|
||||||
|
var self = this;
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
|
||||||
|
Ember.RSVP.hash({
|
||||||
|
resolvedOptions: this.get('options'),
|
||||||
|
resolvedInitialValue: this.get('initialValue')
|
||||||
|
}).then(function (resolvedHash) {
|
||||||
|
self.setProperties(resolvedHash);
|
||||||
|
|
||||||
|
//Run after render to ensure the <option>s have rendered
|
||||||
|
Ember.run.schedule('afterRender', function () {
|
||||||
|
self.setInitialValue();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setInitialValue: function () {
|
||||||
|
var initialValue = this.get('resolvedInitialValue'),
|
||||||
|
options = this.get('resolvedOptions'),
|
||||||
|
initialValueIndex = options.indexOf(initialValue);
|
||||||
|
if (initialValueIndex > -1) {
|
||||||
|
this.$('option:eq(' + initialValueIndex + ')').prop('selected', true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//Called by DOM events, weee!
|
||||||
|
change: function () {
|
||||||
|
this._changeSelection();
|
||||||
|
},
|
||||||
|
//Send value to specified action
|
||||||
|
_changeSelection: function () {
|
||||||
|
var value = this._selectedValue();
|
||||||
|
Ember.set(this, 'value', value);
|
||||||
|
this.sendAction('onChange', value);
|
||||||
|
},
|
||||||
|
_selectedValue: function () {
|
||||||
|
var selectedIndex = this.$('select')[0].selectedIndex;
|
||||||
|
|
||||||
|
return this.get('options').objectAt(selectedIndex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default GhostSelect;
|
@ -1,5 +1,15 @@
|
|||||||
var InviteNewUserController = Ember.Controller.extend({
|
var InviteNewUserController = Ember.Controller.extend({
|
||||||
|
//Used to set the initial value for the dropdown
|
||||||
|
authorRole: Ember.computed(function () {
|
||||||
|
var self = this;
|
||||||
|
return this.store.find('role').then(function (roles) {
|
||||||
|
var authorRole = roles.findBy('name', 'Author');
|
||||||
|
//Initialize role as well.
|
||||||
|
self.set('role', authorRole);
|
||||||
|
return authorRole;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
confirm: {
|
confirm: {
|
||||||
accept: {
|
accept: {
|
||||||
text: 'send invitation now'
|
text: 'send invitation now'
|
||||||
@ -8,45 +18,23 @@ var InviteNewUserController = Ember.Controller.extend({
|
|||||||
buttonClass: 'hidden'
|
buttonClass: 'hidden'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
roles: Ember.computed(function () {
|
|
||||||
var roles = {},
|
|
||||||
self = this;
|
|
||||||
|
|
||||||
roles.promise = this.store.find('role', { permissions: 'assign' }).then(function (roles) {
|
|
||||||
return roles.rejectBy('name', 'Owner').sortBy('id');
|
|
||||||
}).then(function (roles) {
|
|
||||||
// After the promise containing the roles has been resolved and the array
|
|
||||||
// has been sorted, explicitly set the selectedRole for the Ember.Select.
|
|
||||||
// The explicit set is needed because the data-select-text attribute is
|
|
||||||
// not being set until a change is made in the dropdown list.
|
|
||||||
// This is only required with Ember.Select when it is bound to async data.
|
|
||||||
self.set('selectedRole', roles.get('firstObject'));
|
|
||||||
|
|
||||||
return roles;
|
|
||||||
});
|
|
||||||
|
|
||||||
return Ember.ArrayProxy.extend(Ember.PromiseProxyMixin).create(roles);
|
|
||||||
}),
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
setRole: function (role) {
|
||||||
|
this.set('role', role);
|
||||||
|
},
|
||||||
confirmAccept: function () {
|
confirmAccept: function () {
|
||||||
var email = this.get('email'),
|
var email = this.get('email'),
|
||||||
role_id = this.get('role'),
|
role = this.get('role'),
|
||||||
self = this,
|
self = this,
|
||||||
newUser,
|
newUser;
|
||||||
role;
|
|
||||||
|
|
||||||
newUser = self.store.createRecord('user', {
|
newUser = self.store.createRecord('user', {
|
||||||
email: email,
|
email: email,
|
||||||
status: 'invited'
|
status: 'invited',
|
||||||
|
role: role
|
||||||
});
|
});
|
||||||
|
|
||||||
// no need to make an API request, the store will already have this role
|
|
||||||
role = self.store.getById('role', role_id);
|
|
||||||
|
|
||||||
newUser.get('roles').pushObject(role);
|
|
||||||
|
|
||||||
newUser.save().then(function () {
|
newUser.save().then(function () {
|
||||||
var notificationText = 'Invitation sent! (' + email + ')';
|
var notificationText = 'Invitation sent! (' + email + ')';
|
||||||
|
|
||||||
|
@ -36,7 +36,6 @@ var SettingsUserController = Ember.ObjectController.extend({
|
|||||||
var lastLogin = this.get('user.last_login');
|
var lastLogin = this.get('user.last_login');
|
||||||
|
|
||||||
return lastLogin ? lastLogin.fromNow() : '';
|
return lastLogin ? lastLogin.fromNow() : '';
|
||||||
|
|
||||||
}.property('user.last_login'),
|
}.property('user.last_login'),
|
||||||
|
|
||||||
created_at: function () {
|
created_at: function () {
|
||||||
@ -44,12 +43,11 @@ var SettingsUserController = Ember.ObjectController.extend({
|
|||||||
|
|
||||||
return createdAt ? createdAt.fromNow() : '';
|
return createdAt ? createdAt.fromNow() : '';
|
||||||
}.property('user.created_at'),
|
}.property('user.created_at'),
|
||||||
|
|
||||||
isAuthor: function () {
|
|
||||||
return this.get('user.isAuthor');
|
|
||||||
}.property('user.isAuthor'),
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
changeRole: function (newRole) {
|
||||||
|
this.set('model.role', newRole);
|
||||||
|
},
|
||||||
revoke: function () {
|
revoke: function () {
|
||||||
var self = this,
|
var self = this,
|
||||||
email = this.get('email');
|
email = this.get('email');
|
||||||
|
10
core/client/initializers/store-injector.js
Normal file
10
core/client/initializers/store-injector.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
//Used to surgically insert the store into things that wouldn't normally have them.
|
||||||
|
var StoreInjector = {
|
||||||
|
name: 'store-injector',
|
||||||
|
after: 'store',
|
||||||
|
initialize: function (container, application) {
|
||||||
|
application.inject('component:gh-role-selector', 'store', 'store:main');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StoreInjector;
|
@ -26,18 +26,22 @@ var User = DS.Model.extend(NProgressSaveMixin, SelectiveSaveMixin, ValidationEng
|
|||||||
updated_by: DS.attr('number'),
|
updated_by: DS.attr('number'),
|
||||||
roles: DS.hasMany('role', { embedded: 'always' }),
|
roles: DS.hasMany('role', { embedded: 'always' }),
|
||||||
|
|
||||||
|
role: Ember.computed('roles', function (name, value) {
|
||||||
// TODO: Once client-side permissions are in place,
|
if (arguments.length > 1) {
|
||||||
// remove the hard role check.
|
//Only one role per user, so remove any old data.
|
||||||
isAuthor: Ember.computed('roles', function () {
|
this.get('roles').clear();
|
||||||
return this.get('roles').objectAt(0).get('name').toLowerCase() === 'author';
|
this.get('roles').pushObject(value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return this.get('roles.firstObject');
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// TODO: Once client-side permissions are in place,
|
// TODO: Once client-side permissions are in place,
|
||||||
// remove the hard role check.
|
// remove the hard role check.
|
||||||
isEditor: Ember.computed('roles', function () {
|
isAuthor: Ember.computed.equal('role.name', 'Author'),
|
||||||
return this.get('roles').objectAt(0).get('name').toLowerCase() === 'editor';
|
isEditor: Ember.computed.equal('role.name', 'Editor'),
|
||||||
}),
|
isAdmin: Ember.computed.equal('role.name', 'Administrator'),
|
||||||
|
isOwner: Ember.computed.equal('role.name', 'Owner'),
|
||||||
|
|
||||||
saveNewPassword: function () {
|
saveNewPassword: function () {
|
||||||
var url = this.get('ghostPaths.url').api('users', 'password');
|
var url = this.get('ghostPaths.url').api('users', 'password');
|
||||||
|
5
core/client/templates/components/gh-role-selector.hbs
Normal file
5
core/client/templates/components/gh-role-selector.hbs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<select {{bind-attr id=selectId name=selectName}}>
|
||||||
|
{{#each roles}}
|
||||||
|
<option {{bind-attr value=id}}>{{name}}</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
@ -10,16 +10,10 @@
|
|||||||
|
|
||||||
<div class="form-group for-select">
|
<div class="form-group for-select">
|
||||||
<label for="new-user-role">Role</label>
|
<label for="new-user-role">Role</label>
|
||||||
<span class="gh-select" {{bind-attr data-select-text=selectedRole.name}}>
|
{{gh-role-selector
|
||||||
{{view Ember.Select
|
initialValue=authorRole
|
||||||
content=roles
|
onChange="setRole"
|
||||||
id="new-user-role"
|
selectId="new-user-role"}}
|
||||||
optionValuePath="content.id"
|
|
||||||
optionLabelPath="content.name"
|
|
||||||
name="role"
|
|
||||||
value=role
|
|
||||||
selection=selectedRole}}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
@ -67,23 +67,16 @@
|
|||||||
{{input type="email" value=user.email id="user-email" placeholder="Email Address" autocapitalize="off" autocorrect="off" autocomplete="off"}}
|
{{input type="email" value=user.email id="user-email" placeholder="Email Address" autocapitalize="off" autocorrect="off" autocomplete="off"}}
|
||||||
<p>Used for notifications</p>
|
<p>Used for notifications</p>
|
||||||
</div>
|
</div>
|
||||||
|
{{#if view.rolesDropdownIsVisible}}
|
||||||
{{!-- The correct markup for select boxes. Needs changing to the correct data --}}
|
<div class="form-group">
|
||||||
{{!-- <div class="form-group">
|
|
||||||
<label for="user-role">Role</label>
|
<label for="user-role">Role</label>
|
||||||
<span class="gh-select">
|
{{gh-role-selector
|
||||||
{{view Ember.Select
|
initialValue=role
|
||||||
id="activeTheme"
|
onChange="changeRole"
|
||||||
name="general[activeTheme]"
|
selectId="user-role"}}
|
||||||
content=themes
|
|
||||||
optionValuePath="content.name"
|
|
||||||
optionLabelPath="content.label"
|
|
||||||
value=activeTheme
|
|
||||||
selection=selectedTheme}}
|
|
||||||
</span>
|
|
||||||
<p>What permissions should this user have?</p>
|
<p>What permissions should this user have?</p>
|
||||||
</div> --}}
|
</div>
|
||||||
|
{{/if}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="user-location">Location</label>
|
<label for="user-location">Location</label>
|
||||||
{{input type="text" value=user.location id="user-location"}}
|
{{input type="text" value=user.location id="user-location"}}
|
||||||
|
13
core/client/views/settings/users/user.js
Normal file
13
core/client/views/settings/users/user.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
var SettingsUserView = Ember.View.extend({
|
||||||
|
currentUser: Ember.computed.alias('controller.session.user'),
|
||||||
|
|
||||||
|
isNotOwnProfile: Ember.computed('controller.user.id', 'currentUser.id', function () {
|
||||||
|
return this.get('controller.user.id') !== this.get('currentUser.id');
|
||||||
|
}),
|
||||||
|
|
||||||
|
canAssignRoles: Ember.computed.or('currentUser.isAdmin', 'currentUser.isOwner'),
|
||||||
|
|
||||||
|
rolesDropdownIsVisible: Ember.computed.and('isNotOwnProfile', 'canAssignRoles')
|
||||||
|
});
|
||||||
|
|
||||||
|
export default SettingsUserView;
|
@ -55,62 +55,67 @@ var DEBUG = false, // TOGGLE THIS TO GET MORE SCREENSHOTS
|
|||||||
screens;
|
screens;
|
||||||
|
|
||||||
screens = {
|
screens = {
|
||||||
'root': {
|
'root': {
|
||||||
url: 'ghost/',
|
url: 'ghost/',
|
||||||
linkSelector: '#main-menu > li.content a',
|
linkSelector: '#main-menu > li.content a',
|
||||||
selector: '#main-menu .content.active'
|
selector: '#main-menu .content.active'
|
||||||
},
|
},
|
||||||
'content': {
|
'content': {
|
||||||
url: 'ghost/content/',
|
url: 'ghost/content/',
|
||||||
linkSelector: '#main-menu > li.content a',
|
linkSelector: '#main-menu > li.content a',
|
||||||
selector: '#main-menu .content.active'
|
selector: '#main-menu .content.active'
|
||||||
},
|
},
|
||||||
'editor': {
|
'editor': {
|
||||||
url: 'ghost/editor/',
|
url: 'ghost/editor/',
|
||||||
linkSelector: '#main-menu > li.editor a',
|
linkSelector: '#main-menu > li.editor a',
|
||||||
selector: '#entry-title'
|
selector: '#entry-title'
|
||||||
},
|
},
|
||||||
'settings': {
|
'settings': {
|
||||||
url: 'ghost/settings/',
|
url: 'ghost/settings/',
|
||||||
linkSelector: '#main-menu > li.settings a',
|
linkSelector: '#main-menu > li.settings a',
|
||||||
selector: '.settings-content'
|
selector: '.settings-content'
|
||||||
},
|
},
|
||||||
'settings.general': {
|
'settings.general': {
|
||||||
url: 'ghost/settings/general',
|
url: 'ghost/settings/general',
|
||||||
selector: '.settings-content .settings-general'
|
selector: '.settings-content .settings-general'
|
||||||
},
|
},
|
||||||
'settings.user': {
|
'settings.users': {
|
||||||
url: 'ghost/settings/user',
|
url: 'ghost/settings/users',
|
||||||
linkSelector: '#user-menu li.usermenu-profile a',
|
linkSelector: '.settings-menu li.users a',
|
||||||
selector: '.settings-content .settings-user'
|
selector: '.settings-content .settings-users'
|
||||||
},
|
},
|
||||||
'signin': {
|
'settings.users.user': {
|
||||||
url: 'ghost/signin/',
|
url: 'ghost/settings/users/test-user',
|
||||||
selector: '.button-save'
|
linkSelector: '#user-menu li.usermenu-profile a',
|
||||||
},
|
selector: '.settings-content .settings-user'
|
||||||
'signin-authenticated': {
|
},
|
||||||
url: 'ghost/signin/',
|
'signin': {
|
||||||
//signin with authenticated user redirects to posts
|
url: 'ghost/signin/',
|
||||||
selector: '#main-menu .content .active'
|
selector: '.button-save'
|
||||||
},
|
},
|
||||||
'signout': {
|
'signin-authenticated': {
|
||||||
url: 'ghost/signout/',
|
url: 'ghost/signin/',
|
||||||
|
//signin with authenticated user redirects to posts
|
||||||
|
selector: '#main-menu .content .active'
|
||||||
|
},
|
||||||
|
'signout': {
|
||||||
|
url: 'ghost/signout/',
|
||||||
linkSelector: '#usermenu li.usermenu-signout a',
|
linkSelector: '#usermenu li.usermenu-signout a',
|
||||||
// When no user exists we get redirected to setup which has button-add
|
// When no user exists we get redirected to setup which has button-add
|
||||||
selector: '.button-save, .button-add'
|
selector: '.button-save, .button-add'
|
||||||
},
|
},
|
||||||
'signup': {
|
'signup': {
|
||||||
url: 'ghost/signup/',
|
url: 'ghost/signup/',
|
||||||
selector: '.button-save'
|
selector: '.button-save'
|
||||||
},
|
},
|
||||||
'setup': {
|
'setup': {
|
||||||
url: 'ghost/setup/',
|
url: 'ghost/setup/',
|
||||||
selector: '.button-add'
|
selector: '.button-add'
|
||||||
},
|
},
|
||||||
'setup-authenticated': {
|
'setup-authenticated': {
|
||||||
url: 'ghost/setup/',
|
url: 'ghost/setup/',
|
||||||
selector: '#main-menu .content a.active'
|
selector: '#main-menu .content a.active'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
casper.writeContentToCodeMirror = function (content) {
|
casper.writeContentToCodeMirror = function (content) {
|
||||||
@ -487,4 +492,4 @@ CasperTest.Routines = (function () {
|
|||||||
togglePermalinks: _createRunner(togglePermalinks)
|
togglePermalinks: _createRunner(togglePermalinks)
|
||||||
};
|
};
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
@ -169,13 +169,54 @@ CasperTest.begin('General settings validation is correct', 7, function suite(tes
|
|||||||
}, casper.failOnTimeout(test, 'postsPerPage min error did not appear', 2000));
|
}, casper.failOnTimeout(test, 'postsPerPage min error did not appear', 2000));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
CasperTest.begin('Users screen is correct', 9, function suite(test) {
|
||||||
|
casper.thenOpenAndWaitForPageLoad('settings.general');
|
||||||
|
casper.thenTransitionAndWaitForScreenLoad('settings.users', function canTransition () {
|
||||||
|
test.assert(true, 'Can transition to users screen from settings.general');
|
||||||
|
test.assertUrlMatch(/ghost\/settings\/users\/$/, 'settings.users transitions to correct url');
|
||||||
|
});
|
||||||
|
casper.then(function usersScreenHasContent() {
|
||||||
|
test.assertSelectorHasText('.settings-users .object-list .object-list-title', 'Active users');
|
||||||
|
test.assertExists('.settings-users .object-list .object-list-item', 'Has an active user');
|
||||||
|
test.assertSelectorHasText('.settings-users .object-list-item .name', 'Test User');
|
||||||
|
test.assertExists('.settings-users .object-list-item .role-label.owner', 'First user has owner role displayed');
|
||||||
|
|
||||||
|
test.assertExists('.page-actions .button-add', 'Add user button is on page.');
|
||||||
|
});
|
||||||
|
casper.thenClick('.page-actions .button-add');
|
||||||
|
casper.waitForOpaque('.invite-new-user .modal-content', function then() {
|
||||||
|
test.assertEval(function testOwnerRoleNotAnOption() {
|
||||||
|
var options = document.querySelectorAll('.invite-new-user select#new-user-role option'),
|
||||||
|
i = 0;
|
||||||
|
for (; i < options.length; i++) {
|
||||||
|
if (options[i].text === "Owner") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}, '"Owner" is not a role option for new users');
|
||||||
|
});
|
||||||
|
//role options get loaded asynchronously; give them a chance to come in
|
||||||
|
casper.waitForSelector('.invite-new-user select#new-user-role option', function then() {
|
||||||
|
test.assertEval(function authorIsSelectedByDefault() {
|
||||||
|
var options = document.querySelectorAll('.invite-new-user select#new-user-role option'),
|
||||||
|
i = 0;
|
||||||
|
for (; i < options.length; i++) {
|
||||||
|
if (options[i].selected) {
|
||||||
|
return options[i].text === "Author"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}, 'The "Author" role is selected by default when adding a new user');
|
||||||
|
});
|
||||||
|
});
|
||||||
// ### User settings tests
|
// ### User settings tests
|
||||||
// Please uncomment and fix these as the functionality is implemented
|
// Please uncomment and fix these as the functionality is implemented
|
||||||
|
|
||||||
//CasperTest.begin('Can save settings', 6, function suite(test) {
|
//CasperTest.begin('Can save settings', 6, function suite(test) {
|
||||||
// casper.thenOpenAndWaitForPageLoad('settings.user', function testTitleAndUrl() {
|
// casper.thenOpenAndWaitForPageLoad('settings.users.user', function testTitleAndUrl() {
|
||||||
// test.assertTitle('Ghost Admin', 'Ghost admin has no title');
|
// test.assertTitle('Ghost Admin', 'Ghost Admin title is GhostAdmin');
|
||||||
// test.assertUrlMatch(/ghost\/settings\/user\/$/, 'Landed on the correct URL');
|
// test.assertUrlMatch(/ghost\/settings\/users\/test-user\/$/, 'settings.users.user has correct URL');
|
||||||
// });
|
// });
|
||||||
//
|
//
|
||||||
// function handleUserRequest(requestData) {
|
// function handleUserRequest(requestData) {
|
||||||
|
Loading…
Reference in New Issue
Block a user