Skip to content

Commit 99bd854

Browse files
authored
Merge pull request #370 from developmentseed/enhance/paginate-team-view
Add sort, pagination and search to members list at team page
2 parents 0e62ca6 + 4b88035 commit 99bd854

File tree

15 files changed

+484
-180
lines changed

15 files changed

+484
-180
lines changed

cypress.config.js

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ const Organization = require('./src/models/organization')
55
const TeamInvitation = require('./src/models/team-invitation')
66
const { pick } = require('ramda')
77

8-
const user1 = {
9-
id: 1,
10-
}
11-
128
module.exports = defineConfig({
139
e2e: {
1410
baseUrl: 'http://127.0.0.1:3000/',
@@ -22,32 +18,15 @@ module.exports = defineConfig({
2218
await db.raw('TRUNCATE TABLE osm_users RESTART IDENTITY CASCADE')
2319
return null
2420
},
25-
'db:seed': async () => {
26-
// Add teams in series
27-
await Team.create(
28-
{
29-
name: 'Team 1',
30-
},
31-
user1.id
32-
)
33-
await Team.create(
34-
{
35-
name: 'Team 2',
36-
privacy: 'private',
37-
},
38-
user1.id
39-
)
40-
return null
41-
},
42-
'db:seed:teams': async ({ teams, moderatorId }) => {
21+
'db:seed:create-teams': async ({ teams, moderatorId }) => {
4322
let createdTeams = []
4423
for (let i = 0; i < teams.length; i++) {
4524
const team = teams[i]
4625
createdTeams.push(await Team.create(team, moderatorId))
4726
}
4827
return createdTeams
4928
},
50-
'db:seed:add-team-member': async ({ teams, memberId }) => {
29+
'db:seed:add-member-to-teams': async ({ memberId, teams }) => {
5130
for (let i = 0; i < teams.length; i++) {
5231
const team = teams[i]
5332
await Team.addMember(team.id, memberId)
@@ -61,10 +40,10 @@ module.exports = defineConfig({
6140
}
6241
return null
6342
},
64-
'db:seed:team-invitations': async (teamInvitations) => {
43+
'db:seed:create-team-invitations': async (teamInvitations) => {
6544
return Promise.all(teamInvitations.map(TeamInvitation.create))
6645
},
67-
'db:seed:add-organizations': async (orgs) => {
46+
'db:seed:create-organizations': async (orgs) => {
6847
for (let i = 0; i < orgs.length; i++) {
6948
const org = orgs[i]
7049
await Organization.create(
@@ -74,7 +53,7 @@ module.exports = defineConfig({
7453
}
7554
return null
7655
},
77-
'db:seed:add-organization-teams': async ({
56+
'db:seed:create-organization-teams': async ({
7857
orgId,
7958
teams,
8059
managerId,

cypress/e2e/organizations/pagination.cy.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const orgTeam3Members = generateSequenceArray(15, 300).map((i) => ({
4848
describe('Organization page', () => {
4949
before(() => {
5050
cy.task('db:reset')
51-
cy.task('db:seed:add-organizations', [org1])
51+
cy.task('db:seed:create-organizations', [org1])
5252
})
5353

5454
it('Display message when organization has no teams', () => {
@@ -90,7 +90,7 @@ describe('Organization page', () => {
9090
cy.login(user1)
9191

9292
// Add org teams
93-
cy.task('db:seed:add-organization-teams', {
93+
cy.task('db:seed:create-organization-teams', {
9494
orgId: org1.id,
9595
teams: orgTeams,
9696
managerId: user1.id,

cypress/e2e/organizations/permissions.cy.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const publicOrgPublicTeam2 = {
6161
describe('Organizations page: Permissions', () => {
6262
before(() => {
6363
cy.task('db:reset')
64-
cy.task('db:seed:add-organizations', [privateOrg, publicOrg])
64+
cy.task('db:seed:create-organizations', [privateOrg, publicOrg])
6565
})
6666

6767
it('Org is private', () => {
@@ -99,7 +99,7 @@ describe('Organizations page: Permissions', () => {
9999
cy.get('[data-cy=badges-table]').should('not.exist')
100100

101101
// Create org teams
102-
cy.task('db:seed:add-organization-teams', {
102+
cy.task('db:seed:create-organization-teams', {
103103
orgId: privateOrg.id,
104104
teams: [
105105
privateOrgPrivateTeam,
@@ -134,7 +134,7 @@ describe('Organizations page: Permissions', () => {
134134

135135
it('Org is public', () => {
136136
// Create org teams
137-
cy.task('db:seed:add-organization-teams', {
137+
cy.task('db:seed:create-organization-teams', {
138138
orgId: publicOrg.id,
139139
teams: [publicOrgPrivateTeam, publicOrgPublicTeam1, publicOrgPublicTeam2],
140140
managerId: managerUser.id,

cypress/e2e/profile.cy.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,26 @@ describe('Profile page', () => {
5252

5353
it('Teams list is paginated', () => {
5454
// Add teams with user1 as creator
55-
cy.task('db:seed:teams', { teams: user1teams, moderatorId: user1.id })
55+
cy.task('db:seed:create-teams', {
56+
teams: user1teams,
57+
moderatorId: user1.id,
58+
})
5659

5760
// Add teams with user2 and make user1 member
58-
cy.task('db:seed:teams', { teams: user2teams, moderatorId: user2.id })
59-
cy.task('db:seed:add-team-member', {
61+
cy.task('db:seed:create-teams', {
62+
teams: user2teams,
63+
moderatorId: user2.id,
64+
})
65+
cy.task('db:seed:add-member-to-teams', {
6066
teams: user2teams,
6167
memberId: user1.id,
6268
})
6369

6470
// Add teams with user3 with no relation with user 1
65-
cy.task('db:seed:teams', { teams: user3teams, moderatorId: user3.id })
71+
cy.task('db:seed:create-teams', {
72+
teams: user3teams,
73+
moderatorId: user3.id,
74+
})
6675

6776
// Log in and visit profile
6877
cy.login(user1)

cypress/e2e/teams.cy.js

Lines changed: 0 additions & 43 deletions
This file was deleted.

cypress/e2e/teams/index.cy.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const {
2+
generateSequenceArray,
3+
addZeroPadding,
4+
} = require('../../../src/lib/utils')
5+
6+
// Moderator user
7+
const user1 = {
8+
id: 1,
9+
}
10+
11+
// Generate teams
12+
const TEAMS_COUNT = 35
13+
const teams = generateSequenceArray(TEAMS_COUNT, 1).map((i) => ({
14+
id: i,
15+
name: `Team ${addZeroPadding(i, 3)}`,
16+
}))
17+
18+
describe('Teams page', () => {
19+
before(() => {
20+
cy.task('db:reset')
21+
cy.task('db:seed:create-teams', {
22+
teams,
23+
moderatorId: user1.id,
24+
})
25+
})
26+
27+
it('Teams index is public and list teams', () => {
28+
cy.visit('/teams')
29+
30+
cy.get('body').should('contain', 'Team 001')
31+
cy.get("[data-cy='teams-table-pagination']").should('exist')
32+
cy.get('[data-cy=teams-table-pagination]').contains('Showing 1-10 of 35')
33+
34+
// Sort by team name
35+
cy.get('[data-cy=table-head-column-name]').click()
36+
cy.get('[data-cy=teams-table]')
37+
.find('tbody tr:nth-child(1) td:nth-child(2)')
38+
.contains('Team 035')
39+
cy.get('[data-cy=teams-table]')
40+
.find('tbody tr:nth-child(10) td:nth-child(2)')
41+
.contains('Team 026')
42+
43+
// Search by team name
44+
cy.get('[data-cy=teams-table-search-input]').type('02')
45+
cy.get('[data-cy=teams-table-search-submit]').click()
46+
cy.get('[data-cy=teams-table]')
47+
.find('tbody tr:nth-child(5) td:nth-child(2)')
48+
.contains('Team 025')
49+
cy.get('[data-cy=teams-table-pagination]').contains('Showing 1-10 of 11')
50+
})
51+
})

cypress/e2e/team-invitations.cy.js renamed to cypress/e2e/teams/invitations.cy.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
const user1 = {
2+
id: 1,
3+
display_name: 'User 001',
4+
}
5+
16
const expiredInvitation = {
27
uuid: '0a875c3c-ba7c-4132-b08e-427a965177f5',
38
teamId: 1,
@@ -23,8 +28,19 @@ const nonexistentInvitation = {
2328
describe('Team invitation page', () => {
2429
before(() => {
2530
cy.task('db:reset')
26-
cy.task('db:seed')
27-
cy.task('db:seed:team-invitations', [
31+
cy.task('db:seed:create-teams', {
32+
teams: [
33+
{
34+
name: 'Team 1',
35+
},
36+
{
37+
name: 'Team 2',
38+
},
39+
],
40+
moderatorId: user1.id,
41+
})
42+
43+
cy.task('db:seed:create-team-invitations', [
2844
expiredInvitation,
2945
validInvitation,
3046
anotherValidInvitation,

cypress/e2e/teams/view.cy.js

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
const {
2+
generateSequenceArray,
3+
addZeroPadding,
4+
} = require('../../../src/lib/utils')
5+
6+
const team1Members = generateSequenceArray(25, 1).map((i) => ({
7+
id: i,
8+
name: `User ${addZeroPadding(i, 3)}`,
9+
}))
10+
11+
const [user1] = team1Members
12+
13+
const nonMemberUser = {
14+
id: 999,
15+
name: `User 999`,
16+
}
17+
18+
const team1 = {
19+
id: 1,
20+
name: 'Team 1',
21+
privacy: 'public',
22+
}
23+
24+
const team2 = {
25+
id: 2,
26+
name: 'Team 2',
27+
privacy: 'private',
28+
}
29+
30+
describe('Teams page', () => {
31+
before(() => {
32+
cy.task('db:reset')
33+
cy.task('db:seed:create-teams', {
34+
teams: [team1, team2],
35+
moderatorId: user1.id,
36+
})
37+
})
38+
39+
it('Do not list members on public access to private team pages', () => {
40+
// Team 1 is public, should display member list
41+
cy.visit('/teams/1')
42+
cy.get('body').should('contain', 'Team 1')
43+
cy.get("[data-cy='team-members-section']").should('exist')
44+
45+
// Team 2 is private, should NOT display member list
46+
cy.visit('/teams/2')
47+
cy.get('body').should('contain', 'Team 2')
48+
cy.get("[data-cy='team-members-section']").should('not.exist')
49+
})
50+
51+
it('List members on member access to private team pages', () => {
52+
// Sign in as team member
53+
cy.login({
54+
id: 1,
55+
display_name: 'User 001',
56+
})
57+
58+
// Team 1 is public, should display member list
59+
cy.visit('/teams/1')
60+
cy.get('body').should('contain', 'Team 1')
61+
cy.get("[data-cy='team-members-section']").should('exist')
62+
63+
// Team 2 is private, should display member list
64+
cy.visit('/teams/2')
65+
cy.get('body').should('contain', 'Team 2')
66+
cy.get("[data-cy='team-members-section']").should('exist')
67+
})
68+
69+
it('Do not list members on non-member access to private team pages', () => {
70+
// Signed in as non-team member
71+
cy.login(nonMemberUser)
72+
73+
// Team 1 is public, should display member list
74+
cy.visit('/teams/1')
75+
cy.get('body').should('contain', 'Team 1')
76+
cy.get("[data-cy='team-members-section']").should('exist')
77+
78+
// Team 2 is private, should display member list
79+
cy.visit('/teams/2')
80+
cy.get('body').should('contain', 'Team 2')
81+
cy.get("[data-cy='team-members-section']").should('not.exist')
82+
})
83+
84+
it('Public team has a paginated team member table', () => {
85+
cy.task('db:seed:add-members-to-team', {
86+
teamId: team1.id,
87+
members: team1Members,
88+
})
89+
90+
// Team 1 is public, should display member list
91+
cy.visit('/teams/1')
92+
cy.get('body').should('contain', 'Team 1')
93+
cy.get("[data-cy='team-members-section']").should('exist')
94+
cy.get("[data-cy='team-members-table-pagination']").should('exist')
95+
cy.get('[data-cy=team-members-table-pagination]').contains(
96+
'Showing 1-10 of 25'
97+
)
98+
99+
// Perform sort by username
100+
cy.get('[data-cy=table-head-column-name]').click()
101+
cy.get('[data-cy=team-members-table]').contains('User 025')
102+
cy.get('[data-cy=team-members-table]').contains('User 016')
103+
104+
// Perform search by username
105+
cy.get('[data-cy=team-members-table-search-input]').type('USER 02')
106+
cy.get('[data-cy=team-members-table-search-submit]').click()
107+
cy.get('[data-cy=team-members-table-pagination]').contains(
108+
'Showing 1-6 of 6'
109+
)
110+
})
111+
})

src/components/section.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react'
22

3-
export default function Section({ children }) {
4-
return <section>{children}</section>
3+
export default function Section({ children, 'data-cy': dataCy }) {
4+
return <section data-cy={dataCy}>{children}</section>
55
}

0 commit comments

Comments
 (0)