diff --git a/__tests__/components/AccessList.test.jsx b/__tests__/components/AccessList.test.jsx new file mode 100644 index 0000000000..1afb88b127 --- /dev/null +++ b/__tests__/components/AccessList.test.jsx @@ -0,0 +1,142 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import { MemoryRouter } from 'react-router-dom'; +import { I18nProvider } from '@lingui/react'; + +import AccessList from '../../src/components/AccessList'; + +const mockResults = [ + { + id: 1, + username: 'boo', + url: '/foo/bar/', + first_name: 'john', + last_name: 'smith', + summary_fields: { + foo: [ + { + role: { + name: 'foo', + } + } + ] + } + } +]; + +const mockUserRoles = [ + { + id: 2, + name: 'bar', + } +]; + +const mockUserTeams = [ + { + id: 3, + } +]; + +const mockTeamRoles = [ + { + id: 4, + name: 'baz', + } +]; + +describe('', () => { + test('initially renders succesfully', () => { + mount( + + + {}} + getUserRoles={() => {}} + getUserTeams={() => {}} + getTeamRoles={() => {}} + /> + + + ); + }); + + test('api response data passed to component gets set to state properly', (done) => { + const wrapper = mount( + + + ({ data: { count: 1, results: mockResults } })} + getUserRoles={() => ({ data: { results: mockUserRoles } })} + getUserTeams={() => ({ data: { results: mockUserTeams } })} + getTeamRoles={() => ({ data: { results: mockTeamRoles } })} + /> + + + ).find('AccessList'); + + setImmediate(() => { + expect(wrapper.state().results).toEqual(mockResults); + done(); + }); + }); + + test('onExpand and onCompact methods called when user clicks on Expand and Compact icons respectively', async (done) => { + const onExpand = jest.spyOn(AccessList.prototype, 'onExpand'); + const onCompact = jest.spyOn(AccessList.prototype, 'onCompact'); + const wrapper = mount( + + + ({ data: { count: 1, results: mockResults } })} + getUserRoles={() => ({ data: { results: mockUserRoles } })} + getUserTeams={() => ({ data: { results: mockUserTeams } })} + getTeamRoles={() => ({ data: { results: mockTeamRoles } })} + /> + + + ).find('AccessList'); + expect(onExpand).not.toHaveBeenCalled(); + expect(onCompact).not.toHaveBeenCalled(); + + setImmediate(() => { + const rendered = wrapper.update(); + rendered.find('button[aria-label="Expand"]').simulate('click'); + rendered.find('button[aria-label="Collapse"]').simulate('click'); + expect(onExpand).toHaveBeenCalled(); + expect(onCompact).toHaveBeenCalled(); + done(); + }); + }); + + test('onSort being passed properly to DataListToolbar component', async (done) => { + const onSort = jest.spyOn(AccessList.prototype, 'onSort'); + const wrapper = mount( + + + ({ data: { count: 1, results: mockResults } })} + getUserRoles={() => ({ data: { results: mockUserRoles } })} + getUserTeams={() => ({ data: { results: mockUserTeams } })} + getTeamRoles={() => ({ data: { results: mockTeamRoles } })} + /> + + + ).find('AccessList'); + expect(onSort).not.toHaveBeenCalled(); + + setImmediate(() => { + const rendered = wrapper.update(); + rendered.find('button[aria-label="Sort"]').simulate('click'); + expect(onSort).toHaveBeenCalled(); + done(); + }); + }); +}); diff --git a/__tests__/pages/Organizations/screens/Organization/OrganizationAccess.test.jsx b/__tests__/pages/Organizations/screens/Organization/OrganizationAccess.test.jsx new file mode 100644 index 0000000000..4771f7a4e8 --- /dev/null +++ b/__tests__/pages/Organizations/screens/Organization/OrganizationAccess.test.jsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import { MemoryRouter } from 'react-router-dom'; + +import OrganizationAccess from '../../../../../src/pages/Organizations/screens/Organization/OrganizationAccess'; + +const mockAPIAccessList = { + foo: 'bar', +}; +const mockAPIRoles = { + bar: 'baz', +}; +const mockAPITeams = { + qux: 'quux', +}; +const mockAPITeamRoles = { + quuz: 'quuz', +}; + +const mockGetOrganzationAccessList = jest.fn(() => ( + Promise.resolve(mockAPIAccessList) +)); + +const mockGetUserRoles = jest.fn(() => ( + Promise.resolve(mockAPIRoles) +)); + +const mockGetUserTeams = jest.fn(() => ( + Promise.resolve(mockAPITeams) +)); + +const mockGetTeamRoles = jest.fn(() => ( + Promise.resolve(mockAPITeamRoles) +)); + +describe('', () => { + test('initially renders succesfully', () => { + mount( + + + + ); + }); + + test('passed methods as props are called appropriately', async () => { + const wrapper = mount( + + + + ).find('OrganizationAccess'); + const accessList = await wrapper.instance().getOrgAccessList(); + expect(accessList).toEqual(mockAPIAccessList); + const userRoles = await wrapper.instance().getUserRoles(); + expect(userRoles).toEqual(mockAPIRoles); + const userTeams = await wrapper.instance().getUserTeams(); + expect(userTeams).toEqual(mockAPITeams); + const teamRoles = await wrapper.instance().getTeamRoles(); + expect(teamRoles).toEqual(mockAPITeamRoles); + }); +}); diff --git a/src/components/AccessList/Access.list.jsx b/src/components/AccessList/Access.list.jsx index dc9a53a592..8f096622be 100644 --- a/src/components/AccessList/Access.list.jsx +++ b/src/components/AccessList/Access.list.jsx @@ -14,8 +14,8 @@ import { } from 'react-router-dom'; import BasicChip from '../BasicChip/BasicChip'; -import Pagination from '../Pagination/Pagination'; -import DataListToolbar from '../DataListToolbar/DataListToolbar'; +import Pagination from '../Pagination'; +import DataListToolbar from '../DataListToolbar'; import { parseQueryString, @@ -91,7 +91,6 @@ class AccessList extends React.Component { page, page_size, count: null, - results: [], sortOrder: 'ascending', sortedColumnKey: 'username', isCompact: true, @@ -107,19 +106,22 @@ class AccessList extends React.Component { componentDidMount () { const queryParams = this.getQueryParams(); - - this.fetchOrgAccessList(queryParams); + try { + this.fetchOrgAccessList(queryParams); + } catch (error) { + this.setState({ error }); + } } - onExpand = () => { + onExpand () { this.setState({ isCompact: false }); } - onCompact = () => { + onCompact () { this.setState({ isCompact: true }); } - onSetPage = (pageNumber, pageSize) => { + onSetPage (pageNumber, pageSize) { const { sortOrder, sortedColumnKey } = this.state; const page = parseInt(pageNumber, 10); const page_size = parseInt(pageSize, 10); @@ -132,18 +134,9 @@ class AccessList extends React.Component { const queryParams = this.getQueryParams({ page, page_size, order_by }); this.fetchOrgAccessList(queryParams); - }; - - getQueryParams (overrides = {}) { - const { location } = this.props; - const { search } = location; - - const searchParams = parseQueryString(search.substring(1)); - - return Object.assign({}, this.defaultParams, searchParams, overrides); } - onSort = (sortedColumnKey, sortOrder) => { + onSort (sortedColumnKey, sortOrder) { const { page_size } = this.state; let order_by = sortedColumnKey; @@ -155,7 +148,16 @@ class AccessList extends React.Component { const queryParams = this.getQueryParams({ order_by, page_size }); this.fetchOrgAccessList(queryParams); - }; + } + + getQueryParams (overrides = {}) { + const { location } = this.props; + const { search } = location; + + const searchParams = parseQueryString(search.substring(1)); + + return Object.assign({}, this.defaultParams, searchParams, overrides); + } async fetchOrgAccessList (queryParams) { const { match, getAccessList, getUserRoles, getTeamRoles, getUserTeams } = this.props; @@ -186,7 +188,7 @@ class AccessList extends React.Component { results, }; - results.map(async result => { + results.forEach(async result => { result.user_roles = []; result.team_roles = []; // Grab each Role Type and set as a top-level value @@ -197,7 +199,7 @@ class AccessList extends React.Component { // Grab User Roles and set as a top-level value try { const resp = await getUserRoles(result.id); - const roles = resp.data.results; + const roles = resp.data.results || []; roles.forEach(role => { result.user_roles.push(role); }); @@ -209,14 +211,18 @@ class AccessList extends React.Component { // Grab Team Roles and set as a top-level value try { const team_data = await getUserTeams(result.id); - const teams = team_data.data.results; + const teams = team_data.data.results || []; teams.forEach(async team => { - let team_roles = await getTeamRoles(team.id); - team_roles = team_roles.data.results; - team_roles.forEach(role => { - result.team_roles.push(role); - }); - this.setState(stateToUpdate); + try { + let team_roles = await getTeamRoles(team.id); + team_roles = team_roles.data.results || []; + team_roles.forEach(role => { + result.team_roles.push(role); + }); + this.setState(stateToUpdate); + } catch (error) { + this.setState({ error }); + } }); } catch (error) { this.setState({ error }); @@ -239,85 +245,90 @@ class AccessList extends React.Component { sortOrder, isCompact, } = this.state; - - if (!results) { - return null; - } return ( - - {({ i18n }) => ( + + {!results && ( +

Loading...

// TODO: replace with something nicer + )} + {results && ( {}} + onSearch={() => { }} onSort={this.onSort} onCompact={this.onCompact} onExpand={this.onExpand} isCompact={isCompact} showExpandCollapse /> - - {results.map(result => ( - - - - {result.first_name || result.last_name ? ( - - ) : ( - null - )} - - - - {result.user_roles.length > 0 && ( -
    - {i18n._(t`User Roles`)} - {result.user_roles.map(role => ( - + + {({ i18n }) => ( + + {results.map(result => ( + + + - ))} -
- )} - {result.team_roles.length > 0 && ( -
    - {i18n._(t`Team Roles`)} - {result.team_roles.map(role => ( - + ) : ( + null + )} + + + - ))} -
- )} -
-
- ))} -
+ {result.user_roles.length > 0 && ( +
    + {i18n._(t`User Roles`)} + {result.user_roles.map(role => ( + + ))} +
+ )} + {result.team_roles.length > 0 && ( +
    + {i18n._(t`Team Roles`)} + {result.team_roles.map(role => ( + + ))} +
+ )} + + + ))} + + )} +
+ - { error ?
{error}
: '' } + {error ?
{error}
: ''} )} - + + ); } } diff --git a/src/pages/Organizations/screens/Organization/OrganizationAccess.jsx b/src/pages/Organizations/screens/Organization/OrganizationAccess.jsx index 969bb4a370..108d409885 100644 --- a/src/pages/Organizations/screens/Organization/OrganizationAccess.jsx +++ b/src/pages/Organizations/screens/Organization/OrganizationAccess.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import AccessList from '../../../../components/AccessList/Access.list'; +import AccessList from '../../../../components/AccessList'; class OrganizationAccess extends React.Component { constructor (props) {