Merge pull request #7541 from opf/feature/members_widget_on_dashboard
add members widget to dashboard [ci skip]pull/7544/head
commit
b66e4ae676
@ -0,0 +1,35 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
class Queries::Projects::Filters::IdFilter < Queries::Projects::Filters::ProjectFilter |
||||
def type |
||||
:integer |
||||
end |
||||
end |
@ -0,0 +1,41 @@ |
||||
// -- copyright
|
||||
// OpenProject is a project management system.
|
||||
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See doc/COPYRIGHT.rdoc for more details.
|
||||
// ++
|
||||
|
||||
import { |
||||
SimpleResourceCollection |
||||
} from 'core-app/modules/common/path-helper/apiv3/path-resources'; |
||||
|
||||
export class Apiv3GroupsPaths extends SimpleResourceCollection { |
||||
constructor(basePath:string) { |
||||
super(basePath, 'groups'); |
||||
} |
||||
|
||||
public toString():string { |
||||
throw 'Backend does not yet offer this end point'; |
||||
} |
||||
} |
@ -0,0 +1,39 @@ |
||||
// -- copyright
|
||||
// OpenProject is a project management system.
|
||||
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See doc/COPYRIGHT.rdoc for more details.
|
||||
// ++
|
||||
|
||||
import {SimpleResourceCollection} from 'core-app/modules/common/path-helper/apiv3/path-resources'; |
||||
|
||||
export class Apiv3MembershipsPaths extends SimpleResourceCollection { |
||||
constructor(basePath:string) { |
||||
super(basePath, 'memberships'); |
||||
} |
||||
|
||||
// /api/v3/memberships/available_projects
|
||||
public readonly available_projects = this.path + '/available_projects'; |
||||
|
||||
} |
@ -0,0 +1,64 @@ |
||||
<widget-header |
||||
[name]="widgetName" |
||||
[icon]="'group'" |
||||
(onRenamed)="renameWidget($event)"> |
||||
|
||||
<widget-menu |
||||
[resource]="resource"> |
||||
</widget-menu> |
||||
</widget-header> |
||||
|
||||
<div class="grid--widget-content"> |
||||
<no-results *ngIf="noMembers" |
||||
[title]="text.noResults"> |
||||
</no-results> |
||||
<div class="attributes-key-value" *ngFor="let usersByRole of usersByRole"> |
||||
<div class="attributes-key-value--key -minimal"> |
||||
{{usersByRole.role.name}}: |
||||
</div> |
||||
<div class="attributes-key-value--value-container"> |
||||
<div class="attributes-key-value--value"> |
||||
<span *ngFor="let principal of usersByRole.users; let last = last"> |
||||
|
||||
<ng-container *ngIf="isGroup(principal)"> |
||||
{{userName(principal)}} |
||||
</ng-container> |
||||
<ng-container *ngIf="!isGroup(principal)"> |
||||
<user-avatar [user]="principal" |
||||
data-class-list="avatar avatar-mini -spaced"> |
||||
</user-avatar> |
||||
<a [href]="userPath(principal)" |
||||
[textContent]="userName(principal)"> |
||||
</a> |
||||
</ng-container> |
||||
|
||||
<ng-container *ngIf="!last">, </ng-container> |
||||
</span> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div *ngIf="moreMembers" |
||||
class="members-widget--notification"> |
||||
{{moreMembersText}} |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="grid--widget-footer"> |
||||
<a *ngIf="membersAddable" |
||||
[href]="projectMembershipsPath + '?show_add_members=true'" |
||||
class="button -alt-highlight"> |
||||
<i class="button--icon icon-add" aria-hidden="true"></i> |
||||
<span class="button--text" |
||||
[textContent]="text.add"> |
||||
</span> |
||||
</a> |
||||
|
||||
<a *ngIf="!noMembers" |
||||
[href]="projectMembershipsPath" |
||||
class="button -highlight-inverted"> |
||||
<i class="button--icon icon-group" aria-hidden="true"></i> |
||||
<span class="button--text" |
||||
[textContent]="text.viewAll"> |
||||
</span> |
||||
</a> |
||||
</div> |
@ -0,0 +1,9 @@ |
||||
.members-widget--notification |
||||
font-style: italic |
||||
margin-top: 0.5rem |
||||
|
||||
.grid--widget-footer |
||||
margin-top: 0.5rem |
||||
|
||||
.button |
||||
margin-bottom: 0 |
@ -0,0 +1,139 @@ |
||||
import {AbstractWidgetComponent} from "core-app/modules/grids/widgets/abstract-widget.component"; |
||||
import {Component, OnInit, ChangeDetectorRef, Injector, ChangeDetectionStrategy} from '@angular/core'; |
||||
import {I18nService} from "core-app/modules/common/i18n/i18n.service"; |
||||
import {PathHelperService} from "core-app/modules/common/path-helper/path-helper.service"; |
||||
import {UserResource} from "core-app/modules/hal/resources/user-resource"; |
||||
import {CurrentProjectService} from "core-components/projects/current-project.service"; |
||||
import {DmListParameter} from "core-app/modules/hal/dm-services/dm.service.interface"; |
||||
import {MembershipResource} from "core-app/modules/hal/resources/membership-resource"; |
||||
import {MembershipDmService} from "core-app/modules/hal/dm-services/membership-dm.service"; |
||||
import {RoleResource} from "core-app/modules/hal/resources/role-resource"; |
||||
|
||||
const DISPLAYED_MEMBERS_LIMIT = 100; |
||||
|
||||
@Component({ |
||||
templateUrl: './members.component.html', |
||||
changeDetection: ChangeDetectionStrategy.OnPush, |
||||
styleUrls: ['./members.component.sass'] |
||||
}) |
||||
export class WidgetMembersComponent extends AbstractWidgetComponent implements OnInit { |
||||
public text = { |
||||
add: this.i18n.t('js.grid.widgets.members.add'), |
||||
noResults: this.i18n.t('js.grid.widgets.members.no_results'), |
||||
viewAll: this.i18n.t('js.grid.widgets.members.view_all_members'), |
||||
}; |
||||
|
||||
public totalMembers:number; |
||||
public entriesByRoles:{[roleId:string]:{role:RoleResource, users:UserResource[]}} = {}; |
||||
private entriesLoaded = false; |
||||
public membersAddable:boolean = false; |
||||
|
||||
constructor(readonly pathHelper:PathHelperService, |
||||
readonly i18n:I18nService, |
||||
protected readonly injector:Injector, |
||||
readonly membershipDm:MembershipDmService, |
||||
readonly currentProject:CurrentProjectService, |
||||
readonly cdr:ChangeDetectorRef) { |
||||
super(i18n, injector); |
||||
} |
||||
|
||||
ngOnInit() { |
||||
this.membershipDm |
||||
.list(this.listMembersParams) |
||||
.then(collection => { |
||||
this.partitionEntriesByRole(collection.elements); |
||||
this.sortUsersByName(); |
||||
this.totalMembers = collection.total; |
||||
|
||||
this.entriesLoaded = true; |
||||
this.cdr.detectChanges(); |
||||
}); |
||||
|
||||
this.membershipDm |
||||
.listAvailableProjects(this.listAvailableProjectsParams) |
||||
.then(collection => { |
||||
this.membersAddable = collection.total > 0; |
||||
}) |
||||
.catch(() => { |
||||
// nothing bad, the user is just not allowed to add members to the project
|
||||
|
||||
}); |
||||
} |
||||
|
||||
public userPath(user:UserResource) { |
||||
return this.pathHelper.userPath(user.id!); |
||||
} |
||||
|
||||
public userName(user:UserResource) { |
||||
return user.name; |
||||
} |
||||
|
||||
public get noMembers() { |
||||
return this.entriesLoaded && !Object.keys(this.entriesByRoles).length; |
||||
} |
||||
|
||||
public get moreMembers() { |
||||
return this.entriesLoaded && this.totalMembers > DISPLAYED_MEMBERS_LIMIT; |
||||
} |
||||
|
||||
public get moreMembersText() { |
||||
return I18n.t( |
||||
'js.grid.widgets.members.too_many', |
||||
{ count: DISPLAYED_MEMBERS_LIMIT, total: this.totalMembers } |
||||
); |
||||
} |
||||
|
||||
public get projectMembershipsPath() { |
||||
return this.pathHelper.projectMembershipsPath(this.currentProject.identifier!); |
||||
} |
||||
|
||||
public get usersByRole() { |
||||
return Object.values(this.entriesByRoles); |
||||
} |
||||
|
||||
public isGroup(principal:UserResource) { |
||||
return this.pathHelper.api.v3.groups.id(principal.id!).toString() === principal.href; |
||||
} |
||||
|
||||
private partitionEntriesByRole(memberships:MembershipResource[]) { |
||||
memberships.forEach(membership => { |
||||
membership.roles.forEach((role) => { |
||||
if (!this.entriesByRoles[role.id!]) { |
||||
this.entriesByRoles[role.id!] = { role: role, users: [] }; |
||||
} |
||||
|
||||
this.entriesByRoles[role.id!].users.push(membership.principal); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
private sortUsersByName() { |
||||
Object.values(this.entriesByRoles).forEach(entry => { |
||||
entry.users.sort((a, b) => { |
||||
return this.userName(a).localeCompare(this.userName(b)); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
private get listMembersParams() { |
||||
let params:DmListParameter = { sortBy: [['created_on', 'desc']], pageSize: DISPLAYED_MEMBERS_LIMIT }; |
||||
|
||||
if (this.currentProject.id) { |
||||
params['filters'] = [['project_id', '=', [this.currentProject.id]]]; |
||||
} |
||||
|
||||
return params; |
||||
} |
||||
|
||||
private get listAvailableProjectsParams() { |
||||
// It would make sense to set the pageSize but the backend for projects
|
||||
// returns an upaginated list which does not support that.
|
||||
let params:DmListParameter = {}; |
||||
|
||||
if (this.currentProject.id) { |
||||
params['filters'] = [['id', '=', [this.currentProject.id]]]; |
||||
} |
||||
|
||||
return params; |
||||
} |
||||
} |
@ -0,0 +1,53 @@ |
||||
//-- copyright
|
||||
// OpenProject is a project management system.
|
||||
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See doc/COPYRIGHT.rdoc for more details.
|
||||
//++
|
||||
|
||||
import {Injectable} from '@angular/core'; |
||||
import {AbstractDmService} from "core-app/modules/hal/dm-services/abstract-dm.service"; |
||||
import {MembershipResource} from "core-app/modules/hal/resources/membership-resource"; |
||||
import {DmListParameter} from "core-app/modules/hal/dm-services/dm.service.interface"; |
||||
import {CollectionResource} from "core-app/modules/hal/resources/collection-resource"; |
||||
import {ProjectResource} from "core-app/modules/hal/resources/project-resource"; |
||||
|
||||
@Injectable() |
||||
export class MembershipDmService extends AbstractDmService<MembershipResource> { |
||||
public listAvailableProjects(params:DmListParameter|null) { |
||||
return this.listRequest(this.availableProjectsUrl(), params) as Promise<CollectionResource<ProjectResource>>; |
||||
} |
||||
|
||||
protected listUrl() { |
||||
return this.pathHelper.api.v3.memberships.toString(); |
||||
} |
||||
|
||||
protected oneUrl(id:number|string) { |
||||
return this.pathHelper.api.v3.memberships.id(id).toString(); |
||||
} |
||||
|
||||
protected availableProjectsUrl() { |
||||
return this.pathHelper.api.v3.memberships.available_projects.toString(); |
||||
} |
||||
} |
@ -0,0 +1,49 @@ |
||||
//-- copyright
|
||||
// OpenProject is a project management system.
|
||||
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See doc/COPYRIGHT.rdoc for more details.
|
||||
//++
|
||||
|
||||
import {HalResource} from 'core-app/modules/hal/resources/hal-resource'; |
||||
import {UserResource} from "core-app/modules/hal/resources/user-resource"; |
||||
import {RoleResource} from "core-app/modules/hal/resources/role-resource"; |
||||
import {ProjectResource} from "core-app/modules/hal/resources/project-resource"; |
||||
|
||||
export interface MembershipResourceLinks { |
||||
update(payload:unknown):Promise<unknown>; |
||||
updateImmediately(payload:unknown):Promise<unknown>; |
||||
delete():Promise<unknown>; |
||||
} |
||||
|
||||
export interface MembershipResourceEmbedded { |
||||
principal:UserResource; |
||||
roles:RoleResource[]; |
||||
project:ProjectResource; |
||||
} |
||||
|
||||
export class MembershipResource extends HalResource { |
||||
} |
||||
|
||||
export interface MembershipResource extends MembershipResourceLinks, MembershipResourceEmbedded {} |
@ -0,0 +1,32 @@ |
||||
//-- copyright
|
||||
// OpenProject is a project management system.
|
||||
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See doc/COPYRIGHT.rdoc for more details.
|
||||
//++
|
||||
|
||||
import {HalResource} from 'core-app/modules/hal/resources/hal-resource'; |
||||
|
||||
export class RoleResource extends HalResource { |
||||
} |
@ -0,0 +1,148 @@ |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
require_relative '../support/pages/dashboard' |
||||
|
||||
describe 'Members widget on dashboard', type: :feature, js: true do |
||||
let!(:project) { FactoryBot.create :project } |
||||
let!(:other_project) { FactoryBot.create :project } |
||||
|
||||
let!(:manager_user) do |
||||
FactoryBot.create :user, lastname: "Manager", member_in_project: project, member_through_role: role |
||||
end |
||||
let!(:no_edit_member_user) do |
||||
FactoryBot.create :user, lastname: "No_Edit", member_in_project: project, member_through_role: no_edit_member_role |
||||
end |
||||
let!(:no_view_member_user) do |
||||
FactoryBot.create :user, lastname: "No_View", member_in_project: project, member_through_role: no_view_member_role |
||||
end |
||||
let!(:invisible_user) do |
||||
FactoryBot.create :user, lastname: "Invisible", member_in_project: other_project, member_through_role: role |
||||
end |
||||
|
||||
let(:no_view_member_role) do |
||||
FactoryBot.create(:role, |
||||
permissions: %i[manage_dashboards |
||||
view_dashboards]) |
||||
end |
||||
let(:no_edit_member_role) do |
||||
FactoryBot.create(:role, |
||||
permissions: %i[manage_dashboards |
||||
view_dashboards |
||||
view_members]) |
||||
end |
||||
let(:role) do |
||||
FactoryBot.create(:role, |
||||
permissions: %i[manage_dashboards |
||||
view_dashboards |
||||
manage_members |
||||
view_members]) |
||||
end |
||||
let(:dashboard) do |
||||
Pages::Dashboard.new(project) |
||||
end |
||||
|
||||
before do |
||||
login_as manager_user |
||||
|
||||
dashboard.visit! |
||||
end |
||||
|
||||
def expect_all_members_visible(area) |
||||
within area do |
||||
expect(page) |
||||
.to have_content role.name |
||||
expect(page) |
||||
.to have_content manager_user.name |
||||
expect(page) |
||||
.to have_content no_edit_member_role |
||||
expect(page) |
||||
.to have_content no_edit_member_user.name |
||||
expect(page) |
||||
.to have_content no_view_member_role |
||||
expect(page) |
||||
.to have_content no_view_member_user.name |
||||
end |
||||
end |
||||
|
||||
it 'can add the widget and see the members if the permissions suffice' do |
||||
# within top-right area, add an additional widget |
||||
dashboard.add_widget(1, 3, 'Members') |
||||
|
||||
members_area = Components::Grids::GridArea.new('.grid--area.-widgeted:nth-of-type(1)') |
||||
members_area.expect_to_span(1, 3, 4, 5) |
||||
|
||||
members_area.resize_to(7, 4) |
||||
|
||||
expect_all_members_visible(members_area.area) |
||||
|
||||
expect(page) |
||||
.not_to have_content invisible_user.name |
||||
|
||||
within members_area.area do |
||||
expect(page) |
||||
.to have_link('Member') |
||||
end |
||||
|
||||
# A user without edit permission will see the members but cannot add one |
||||
login_as no_edit_member_user |
||||
|
||||
visit root_path |
||||
dashboard.visit! |
||||
|
||||
expect_all_members_visible(members_area.area) |
||||
|
||||
within members_area.area do |
||||
expect(page) |
||||
.to have_no_link('Member') |
||||
end |
||||
|
||||
# A user without view permission will not see any members |
||||
login_as no_view_member_user |
||||
|
||||
visit root_path |
||||
|
||||
dashboard.visit! |
||||
|
||||
within members_area.area do |
||||
expect(page) |
||||
.to have_no_content manager_user.name |
||||
|
||||
expect(page) |
||||
.to have_content('No visible members') |
||||
|
||||
expect(page) |
||||
.to have_no_link('Member') |
||||
|
||||
expect(page) |
||||
.to have_no_link('View all members') |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue