Skip to content

Commit

Permalink
CDPS-161 - Conditionally show content on the banner based on users ca…
Browse files Browse the repository at this point in the history
…seload (#125)

* Conditionally show content on the banner based on users caseload

* Update tests with new auth setup
  • Loading branch information
danielburnley authored May 25, 2023
1 parent af76d14 commit 60ec8cb
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 57 deletions.
3 changes: 1 addition & 2 deletions integration_tests/e2e/alertsPage.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ const visitEmptyAlertsPage = (): AlertsPage => {
context('Alerts Page', () => {
beforeEach(() => {
cy.task('reset')
cy.task('stubSignIn')
cy.task('stubAuthUser')
cy.setupUserAuth()
})

context('Active Alerts', () => {
Expand Down
3 changes: 1 addition & 2 deletions integration_tests/e2e/offencesPage.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ context('Offences Page', () => {

beforeEach(() => {
cy.task('reset')
cy.task('stubSignIn')
cy.task('stubAuthUser')
cy.setupUserAuth()
cy.setupBannerStubs({ prisonerNumber })
cy.setupOffencesPageStubs({ prisonerNumber: 'G6123VU', bookingId: 1102484 })
})
Expand Down
3 changes: 1 addition & 2 deletions integration_tests/e2e/photoPage.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ context('Photo Page', () => {

beforeEach(() => {
cy.task('reset')
cy.task('stubSignIn')
cy.task('stubAuthUser')
cy.setupUserAuth()
cy.setupBannerStubs({ prisonerNumber })
cy.setupOverviewPageStubs({ prisonerNumber, bookingId })
})
Expand Down
43 changes: 43 additions & 0 deletions integration_tests/e2e/profileBanner.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Page from '../pages/page'
import OverviewPage from '../pages/overviewPage'

const visitOverviewPage = (): OverviewPage => {
cy.signIn({ redirectPath: '/prisoner/G6123VU' })
return Page.verifyOnPage(OverviewPage)
}

context('Profile banner', () => {
context('Given the prisoner is not within the users caseload', () => {
context('Given the user has the GLOBAL_SEARCH role', () => {
beforeEach(() => {
cy.task('reset')
cy.setupUserAuth({
roles: ['ROLE_GLOBAL_SEARCH'],
caseLoads: [{ caseloadFunction: '', caseLoadId: '123', currentlyActive: true, description: '', type: '' }],
})
cy.setupOverviewPageStubs({ prisonerNumber: 'G6123VU', bookingId: '1102484' })
})

it('Displays the banner', () => {
visitOverviewPage()
cy.getDataQa('visible-outside-establishment-banner').should('exist')
})
})
})

context('Given the prisoner is within the users caseload', () => {
beforeEach(() => {
cy.task('reset')
cy.setupUserAuth({
roles: ['ROLE_PRISON'],
caseLoads: [{ caseloadFunction: '', caseLoadId: 'MDI', currentlyActive: true, description: '', type: '' }],
})
cy.setupOverviewPageStubs({ prisonerNumber: 'G6123VU', bookingId: '1102484' })
})

it('Hides the banner', () => {
visitOverviewPage()
cy.getDataQa('hidden-outside-establishment-banner').should('exist')
})
})
})
3 changes: 1 addition & 2 deletions integration_tests/e2e/workAndSkillsPage.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ const visitWorkAndSkillsPage = (): WorkAndSkillsPage => {
context('Work and Skills Page', () => {
beforeEach(() => {
cy.task('reset')
cy.task('stubSignIn')
cy.task('stubAuthUser')
cy.setupUserAuth()
})

context('Default Prisoner State', () => {
Expand Down
6 changes: 4 additions & 2 deletions server/controllers/alertsController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { pagedActiveAlertsMock, pagedInactiveAlertsMock } from '../data/localMoc
import AlertsController from './alertsController'
import * as headerMappers from '../mappers/headerMappers'
import { PrisonerMockDataA } from '../data/localMockData/prisoner'
import { CaseLoadsDummyDataA } from '../data/localMockData/caseLoad'

let req: any
let res: any
Expand All @@ -22,6 +23,7 @@ describe('Alerts Controller', () => {
user: {
activeCaseLoadId: 'MDI',
userRoles: ['ROLE_UPDATE_ALERT'],
caseLoads: CaseLoadsDummyDataA,
},
clientToken: 'CLIENT_TOKEN',
},
Expand Down Expand Up @@ -54,7 +56,7 @@ describe('Alerts Controller', () => {
},
true,
)
expect(mapSpy).toHaveBeenCalledWith(PrisonerMockDataA, true, 'alerts')
expect(mapSpy).toHaveBeenCalledWith(PrisonerMockDataA, CaseLoadsDummyDataA, true, 'alerts')
})

it('should get inactive alerts', async () => {
Expand Down Expand Up @@ -84,7 +86,7 @@ describe('Alerts Controller', () => {
},
true,
)
expect(mapSpy).toHaveBeenCalledWith(PrisonerMockDataA, true, 'alerts')
expect(mapSpy).toHaveBeenCalledWith(PrisonerMockDataA, CaseLoadsDummyDataA, true, 'alerts')
})

it('should set canUpdateAlert to true if user has role and active caseload', async () => {
Expand Down
1 change: 1 addition & 0 deletions server/controllers/alertsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export default class AlertsController {
pageTitle: 'Alerts',
...mapHeaderData(
prisonerData,
res.locals.user.caseLoads,
canViewOrAddCaseNotes(res.locals.user.userRoles, res.locals.user.activeCaseLoadId, prisonerData.prisonId),
'alerts',
),
Expand Down
4 changes: 3 additions & 1 deletion server/controllers/caseNotesController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as headerMappers from '../mappers/headerMappers'
import CaseNotesController from './caseNotesController'
import { pagedCaseNotesMock } from '../data/localMockData/pagedCaseNotesMock'
import { caseNoteUsageMock } from '../data/localMockData/caseNoteUsageMock'
import { CaseLoadsDummyDataA } from '../data/localMockData/caseLoad'

let req: any
let res: any
Expand Down Expand Up @@ -31,6 +32,7 @@ describe('Case Notes Controller', () => {
clientToken: 'CLIENT_TOKEN',
user: {
staffId: 487023,
caseLoads: CaseLoadsDummyDataA,
},
},
render: jest.fn(),
Expand Down Expand Up @@ -65,6 +67,6 @@ describe('Case Notes Controller', () => {
},
false,
)
expect(mapSpy).toHaveBeenCalledWith(prisonerDetailMock, true, 'case-notes')
expect(mapSpy).toHaveBeenCalledWith(prisonerDetailMock, CaseLoadsDummyDataA, true, 'case-notes')
})
})
2 changes: 1 addition & 1 deletion server/controllers/caseNotesController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export default class CaseNotesController {
// Render page
return res.render('pages/caseNotesPage', {
pageTitle: 'Case notes',
...mapHeaderData(prisonerData, canViewCaseNotes, 'case-notes'),
...mapHeaderData(prisonerData, res.locals.user.caseLoads, canViewCaseNotes, 'case-notes'),
...caseNotesPageData,
hasCaseNotes,
staffId,
Expand Down
2 changes: 1 addition & 1 deletion server/controllers/overviewController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default class OverviewController {

res.render('pages/overviewPage', {
pageTitle: 'Overview',
...mapHeaderData(prisonerData, canViewCaseNotes, 'overview'),
...mapHeaderData(prisonerData, res.locals.user.caseLoads, canViewCaseNotes, 'overview'),
...overviewPageData,
canViewCaseNotes,
canAddCaseNotes,
Expand Down
16 changes: 13 additions & 3 deletions server/mappers/headerMapper.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import { CaseLoadsDummyDataA } from '../data/localMockData/caseLoad'
import { PrisonerMockDataA, PrisonerMockDataB } from '../data/localMockData/prisoner'
import { mapHeaderData } from './headerMappers'
import { mapHeaderData, mapProfileBannerTopLinks } from './headerMappers'

describe('HeaderMapping', () => {
describe('Header data', () => {
describe('If the prisoner is part of the users case loads', () => {
it('Contains the location', () => {
const topLinks = mapProfileBannerTopLinks(PrisonerMockDataA, CaseLoadsDummyDataA)
expect(topLinks.length).toEqual(4)
expect(topLinks[0].heading).toEqual('Location')
})
})
})
describe('Category A prisoner', () => {
it('Photo type should be photoWithheld for security purposes', async () => {
const headerData = mapHeaderData(PrisonerMockDataA)
const headerData = mapHeaderData(PrisonerMockDataA, CaseLoadsDummyDataA)
expect(headerData.photoType).toBe('photoWithheld')
})
it('Photo type should return as placeholder if the category is not A', async () => {
const headerData = mapHeaderData(PrisonerMockDataB)
const headerData = mapHeaderData(PrisonerMockDataB, CaseLoadsDummyDataA)
expect(headerData.photoType).toBe('placeholder')
})
})
Expand Down
61 changes: 35 additions & 26 deletions server/mappers/headerMappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,42 @@ import { Alert, Prisoner } from '../interfaces/prisoner'
import { tabLinks } from '../data/profileBanner/profileBanner'
import { AlertFlagLabel } from '../interfaces/alertFlagLabels'
import { alertFlagLabels } from '../data/alertFlags/alertFlags'
import { formatName } from '../utils/utils'
import { formatName, prisonerBelongsToUsersCaseLoad } from '../utils/utils'
import { NameFormatStyle } from '../data/enums/nameFormatStyle'
import { CaseLoad } from '../interfaces/caseLoad'

export const placeHolderImagePath = '/assets/images/prisoner-profile-photo.png'

export function mapProfileBannerTopLinks(prisonerData: Prisoner) {
const profileBannerTopLinks = [
{
export function mapProfileBannerTopLinks(prisonerData: Prisoner, userCaseLoads: CaseLoad[]) {
const profileBannerTopLinks = []

if (prisonerBelongsToUsersCaseLoad(prisonerData.prisonId, userCaseLoads)) {
profileBannerTopLinks.push({
heading: 'Location',
hiddenLabel: 'View location details',
info: prisonerData.cellLocation,
classes: '',
},
{
heading: 'Category',
hiddenLabel: 'Manage category',
info: prisonerData.category === 'U' ? 'Unsentenced' : prisonerData.category,
classes: '',
},
{
heading: 'CSRA',
hiddenLabel: 'View CSRA history',
info: prisonerData.csra ? prisonerData.csra : 'Not entered',
classes: '',
},
{
heading: 'Incentive level',
hiddenLabel: 'View incentive level details',
info: prisonerData.currentIncentive ? prisonerData.currentIncentive.level.description : 'Not entered',
classes: 'remove-column-gutter-right',
},
]
})
}

profileBannerTopLinks.push({
heading: 'Category',
hiddenLabel: 'Manage category',
info: prisonerData.category === 'U' ? 'Unsentenced' : prisonerData.category,
classes: '',
})
profileBannerTopLinks.push({
heading: 'CSRA',
hiddenLabel: 'View CSRA history',
info: prisonerData.csra ? prisonerData.csra : 'Not entered',
classes: '',
})
profileBannerTopLinks.push({
heading: 'Incentive level',
hiddenLabel: 'View incentive level details',
info: prisonerData.currentIncentive ? prisonerData.currentIncentive.level.description : 'Not entered',
classes: 'remove-column-gutter-right',
})
return profileBannerTopLinks
}

Expand All @@ -51,7 +55,12 @@ export function mapAlerts(prisonerData: Prisoner, alertFlags: AlertFlagLabel[])
return alerts
}

export function mapHeaderData(prisonerData: Prisoner, canViewCaseNotes?: boolean, pageId?: string) {
export function mapHeaderData(
prisonerData: Prisoner,
userCaseLoads: CaseLoad[],
canViewCaseNotes?: boolean,
pageId?: string,
) {
const photoType = prisonerData.category === 'A' ? 'photoWithheld' : 'placeholder'
const tabs = tabLinks(prisonerData.prisonerNumber, canViewCaseNotes)

Expand All @@ -65,7 +74,7 @@ export function mapHeaderData(prisonerData: Prisoner, canViewCaseNotes?: boolean
style: NameFormatStyle.lastCommaFirst,
}),
prisonerNumber: prisonerData.prisonerNumber,
profileBannerTopLinks: mapProfileBannerTopLinks(prisonerData),
profileBannerTopLinks: mapProfileBannerTopLinks(prisonerData, userCaseLoads),
alerts: mapAlerts(prisonerData, alertFlagLabels),
tabLinks: tabs,
photoType,
Expand Down
4 changes: 4 additions & 0 deletions server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export default function routes(service: Services): Router {
res.render('pages/photoPage', {
...mapHeaderData(
prisonerData,
res.locals.user.caseLoads,
canViewOrAddCaseNotes(res.locals.user.userRoles, res.locals.user.activeCaseLoadId, prisonerData.prisonId),
),
})
Expand All @@ -68,6 +69,7 @@ export default function routes(service: Services): Router {
pageTitle: 'Personal',
...mapHeaderData(
prisonerData,
res.locals.user.caseLoads,
canViewOrAddCaseNotes(res.locals.user.userRoles, res.locals.user.activeCaseLoadId, prisonerData.prisonId),
'personal',
),
Expand All @@ -88,6 +90,7 @@ export default function routes(service: Services): Router {
res.render('pages/workAndSkills', {
...mapHeaderData(
prisonerData,
res.locals.user.caseLoads,
canViewOrAddCaseNotes(res.locals.user.userRoles, res.locals.user.activeCaseLoadId, prisonerData.prisonId),
'work-and-skills',
),
Expand Down Expand Up @@ -121,6 +124,7 @@ export default function routes(service: Services): Router {
pageTitle: 'Offences',
...mapHeaderData(
prisonerData,
res.locals.user.caseLoads,
canViewOrAddCaseNotes(res.locals.user.userRoles, res.locals.user.activeCaseLoadId, prisonerData.prisonId),
'offences',
),
Expand Down
49 changes: 34 additions & 15 deletions server/views/partials/profileBanner/profileBannerTop.njk
Original file line number Diff line number Diff line change
@@ -1,20 +1,39 @@
{% from "../../macros/conditionallyShow.njk" import conditionallyShow %}
<div class="govuk-grid-row">
<div class="govuk-grid-column-full hmpps-profile-banner-top">
<a href="/prisoner/{{ prisonerNumber }}/image" class="hmpps-clickable-image" data-qa=prisoner-photo-link>
<img class="hmpps-profile-photo" src="/api/prisoner/{{ prisonerNumber or photoType }}/image" alt="{{ prisonerName }}" />
</a>
<h2 class="govuk-heading-l hmpps-profile-prisoner-name">{{ prisonerName }}</h2>
<div class="info">
<p class="govuk-body govuk-!-font-size-24 hmpps-profile-prisoner-number" aria-label="Prisoner Number">
{{ prisonerNumber }}
</p>
<div class="info__links">
{% for link in profileBannerTopLinks %}
<div class="{{ link.classes }}">
<h3 class="govuk-heading-s">{{ link.heading }}</h3>
<a href="#" aria-label="{{ link.hiddenLabel }}" class="govuk-link govuk-link--inverse">{{ link.info }}</a>
<div class="govuk-grid-column-full">
{%- call conditionallyShow({condition: prisonerBelongsToUsersCaseLoad(prisonId, user.caseLoads) != true, id: 'outside-establishment-banner'}) -%}
<div class="govuk-grid-row" class="govuk-!-padding-top-3 govuk-!-padding-bottom-3" style="background-color: #fbc9af;">
<div class="govuk-grid-column-full">
<p class="govuk-heading-m govuk-!-margin-top-2 govuk-!-margin-bottom-2">This prisoner is not in your establishment</p>
<p class="govuk-body govuk-!-margin-bottom-2">Some information may be hidden</p>
</div>
</div>
{%- endcall -%}
<div class="govuk-grid-row">
<div class="govuk-grid-column-full hmpps-profile-banner-top">
<a href="/prisoner/{{ prisonerNumber }}/image" class="hmpps-clickable-image" data-qa="prisoner-photo-link">
<img
class="hmpps-profile-photo"
src="/api/prisoner/{{ prisonerNumber or photoType }}/image"
alt="{{ prisonerName }}"
/>
</a>
<h2 class="govuk-heading-l hmpps-profile-prisoner-name">{{ prisonerName }}</h2>
<div class="info">
<p class="govuk-body govuk-!-font-size-24 hmpps-profile-prisoner-number" aria-label="Prisoner Number">
{{ prisonerNumber }}
</p>
<div class="info__links">
{% for link in profileBannerTopLinks %}
<div class="{{ link.classes }}">
<h3 class="govuk-heading-s">{{ link.heading }}</h3>
<a href="#" aria-label="{{ link.hiddenLabel }}" class="govuk-link govuk-link--inverse"
>{{ link.info }}</a
>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
</div>
</div>
Expand Down

0 comments on commit 60ec8cb

Please sign in to comment.