Skip to content

Commit

Permalink
Haar 1904 view base client page (#27)
Browse files Browse the repository at this point in the history
* add tests for list base clients page

* add view base client page

* Fix errors in template
  • Loading branch information
thomasridd committed Oct 23, 2023
1 parent 5e5d216 commit 6d13bd5
Show file tree
Hide file tree
Showing 14 changed files with 830 additions and 4 deletions.
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
"uuid": "^9.0.1"
},
"devDependencies": {
"@golevelup/ts-jest": "^0.4.0",
"@types/bunyan": "^1.8.9",
"@types/bunyan-format": "^0.2.6",
"@types/compression": "^1.7.3",
Expand Down
82 changes: 82 additions & 0 deletions server/controllers/baseClientController.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type { DeepMocked } from '@golevelup/ts-jest'
import { createMock } from '@golevelup/ts-jest'
import type { NextFunction, Request, Response } from 'express'

import BaseClientController from './baseClientController'
import { BaseClientService } from '../services'
import { baseClientFactory, clientFactory } from '../testutils/factories'
import listBaseClientsPresenter from '../views/presenters/listBaseClientsPresenter'
import createUserToken from '../testutils/createUserToken'
import viewBaseClientPresenter from '../views/presenters/viewBaseClientPresenter'
import nunjucksUtils from '../views/helpers/nunjucksUtils'

describe('BaseClientController', () => {
const token = createUserToken(['ADMIN'])
let request: DeepMocked<Request>
let response: DeepMocked<Response>
const next: DeepMocked<NextFunction> = createMock<NextFunction>({})
const baseClientService = createMock<BaseClientService>({})
let baseClientController: BaseClientController

beforeEach(() => {
request = createMock<Request>()
response = createMock<Response>({
locals: {
clientToken: 'CLIENT_TOKEN',
user: {
token,
authSource: 'auth',
},
},
render: jest.fn(),
redirect: jest.fn(),
})

baseClientController = new BaseClientController(baseClientService)
})

describe('displayBaseClients', () => {
it('renders the list index template with a list of base clients', async () => {
// GIVEN a list of base clients
const baseClients = baseClientFactory.buildList(3)
baseClientService.listBaseClients.mockResolvedValue(baseClients)

// WHEN the index page is requested
await baseClientController.displayBaseClients()(request, response, next)

// THEN the list of base clients is rendered
const presenter = listBaseClientsPresenter(baseClients)
expect(response.render).toHaveBeenCalledWith('pages/base-clients.njk', {
presenter,
})

// AND the list of base clients is retrieved from the base client service
expect(baseClientService.listBaseClients).toHaveBeenCalledWith(token)
})
})

describe('view base client', () => {
it('renders the main view of a base clients', async () => {
// GIVEN a list of base clients
const baseClient = baseClientFactory.build()
const clients = clientFactory.buildList(3)
baseClientService.getBaseClient.mockResolvedValue(baseClient)
baseClientService.listClientInstances.mockResolvedValue(clients)

// WHEN the index page is requested
request = createMock<Request>({ params: { baseClientId: baseClient.baseClientId } })
await baseClientController.displayBaseClient()(request, response, next)

// THEN the view base client page is rendered
const presenter = viewBaseClientPresenter(baseClient, clients)
expect(response.render).toHaveBeenCalledWith('pages/base-client.njk', {
baseClient,
presenter,
...nunjucksUtils,
})

// AND the base client is retrieved from the base client service
expect(baseClientService.getBaseClient).toHaveBeenCalledWith(token, baseClient.baseClientId)
})
})
})
18 changes: 18 additions & 0 deletions server/controllers/baseClientController.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { RequestHandler } from 'express'
import { BaseClientService } from '../services'
import listBaseClientsPresenter from '../views/presenters/listBaseClientsPresenter'
import viewBaseClientPresenter from '../views/presenters/viewBaseClientPresenter'
import nunjucksUtils from '../views/helpers/nunjucksUtils'

export default class BaseClientController {
constructor(private readonly baseClientService: BaseClientService) {}
Expand All @@ -17,4 +19,20 @@ export default class BaseClientController {
})
}
}

public displayBaseClient(): RequestHandler {
return async (req, res) => {
const userToken = res.locals.user.token
const { baseClientId } = req.params
const baseClient = await this.baseClientService.getBaseClient(userToken, baseClientId)
const clients = await this.baseClientService.listClientInstances(userToken, baseClient)

const presenter = viewBaseClientPresenter(baseClient, clients)
res.render('pages/base-client.njk', {
baseClient,
presenter,
...nunjucksUtils,
})
}
}
}
1 change: 1 addition & 0 deletions server/routes/baseClientRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default function baseClientRouter(services: Services): Router {
const baseClientController = new BaseClientController(services.baseClientService)

get('/', baseClientController.displayBaseClients())
get('/base-clients/:baseClientId', baseClientController.displayBaseClient())

return router
}
14 changes: 14 additions & 0 deletions server/testutils/createUserToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import jwt from 'jsonwebtoken'

export default function createUserToken(authorities: string[]) {
const payload = {
user_name: 'user1',
scope: ['read', 'write'],
auth_source: 'nomis',
authorities,
jti: 'a610a10-cca6-41db-985f-e87efb303aaf',
client_id: 'clientid',
}

return jwt.sign(payload, 'secret', { expiresIn: '1h' })
}
47 changes: 47 additions & 0 deletions server/views/helpers/nunjucksUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import nunjucksUtils from './nunjucksUtils'

describe('nunjucksUtils', () => {
describe('capitalCase', () => {
it.each([
[null, null, ''],
['an empty string', '', ''],
['lower case', 'robert', 'Robert'],
['upper case', 'ROBERT', 'Robert'],
['mixed case', 'RoBErT', 'Robert'],
['multiple words', 'RobeRT SMiTH', 'Robert Smith'],
['leading spaces', ' RobeRT', ' Robert'],
['trailing spaces', 'RobeRT ', 'Robert '],
])('handles %s: %s -> %s', (_inputType: string | null, input: string | null, expectedOutput: string) => {
expect(nunjucksUtils.capitalCase(input)).toEqual(expectedOutput)
})
})

describe('sentenceCase', () => {
it.each([
['an empty string', '', ''],
['a single lower case letter', 'a', 'A'],
['a single upper case letter', 'A', 'A'],
['a lower case word', 'rosa', 'Rosa'],
['an upper case word', 'ROSA', 'Rosa'],
['a proper case word', 'Rosa', 'Rosa'],
['a mixed case word', 'RoSa', 'Rosa'],
['multiple words', 'the fish swam', 'The fish swam'],
])('handles %s: %s -> %s', (_inputType: string, input: string, expectedOutput: string) => {
expect(nunjucksUtils.sentenceCase(input)).toEqual(expectedOutput)
})
})

describe('capitalize', () => {
it.each([
['an empty string', '', ''],
['a single lower case letter', 'a', 'A'],
['a single upper case letter', 'A', 'A'],
['a lower case word', 'rosa', 'Rosa'],
['an upper case word', 'ROSA', 'Rosa'],
['a proper case word', 'Rosa', 'Rosa'],
['a mixed case word', 'RoSa', 'Rosa'],
])('handles %s: %s -> %s', (_inputType: string, input: string, expectedOutput: string) => {
expect(nunjucksUtils.capitalize(input)).toEqual(expectedOutput)
})
})
})
39 changes: 39 additions & 0 deletions server/views/helpers/nunjucksUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const isBlank = (str: string): boolean => {
return !str || /^\s*$/.test(str)
}

const capitalCase = (sentence: string | null): string => {
return sentence === null || isBlank(sentence) ? '' : sentence.split(' ').map(capitalize).join(' ')
}

const sentenceCase = (sentence: string | null): string => {
if (sentence === null || isBlank(sentence)) {
return ''
}

const words = sentence.split(' ')
if (words.length === 1) {
return capitalize(words[0])
}
return `${capitalize(words[0])} ${words.slice(1).join(' ')}`
}

const capitalize = (word: string): string => {
return word.length >= 1 ? word[0].toUpperCase() + word.toLowerCase().slice(1) : word
}

const toLinesHtml = (str?: string[]): string | null => {
return str.join('<br>')
}

const toLines = (str?: string[]): string | null => {
return str.join('/n')
}

export default {
toLinesHtml,
toLines,
sentenceCase,
capitalize,
capitalCase,
}

0 comments on commit 6d13bd5

Please sign in to comment.