Skip to content

Commit

Permalink
WIP Experimental test builder
Browse files Browse the repository at this point in the history
  • Loading branch information
DomBelcher committed Nov 26, 2024
1 parent a837e9c commit 3a3c742
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
const sinon = require('sinon')
const formatSimplifiedAccountPathsFor = require('@utils/simplified-account/format/format-simplified-account-paths-for')
const paths = require('@root/paths')
const ControllerTestBuilder = require('@test/test-helpers/simplified-account/controllers/ControllerTestBuilder.class')
const chai = require('chai')
const sinonChai = require('sinon-chai')
chai.use(sinonChai)
const expect = chai.expect
const Service = require('@models/Service.class')

const ExperimentalTestBuilder = require('@test/test-helpers/simplified-account/controllers/ExperimentalTestBuilder.class')

const mockResponse = sinon.spy()
const updateServiceSpy = sinon.spy()

const ACCOUNT_TYPE = 'live'
const SERVICE_ID = 'service-id-123abc'

const { req, res, call, nextRequest } = new ControllerTestBuilder('@controllers/simplified-account/settings/organisation-details/edit-organisation-details.controller')
const controllerTest = new ExperimentalTestBuilder('@controllers/simplified-account/settings/organisation-details/edit-organisation-details.controller')
.withService(new Service({
id: '123',
external_id: SERVICE_ID,
Expand All @@ -38,19 +39,22 @@ const { req, res, call, nextRequest } = new ControllerTestBuilder('@controllers/

describe('Controller: settings/organisation-details', () => {
describe('get', () => {
before(() => {
call('get')
})
// before(() => {
// call('get')
// })

it('should call the response method', () => {
controllerTest.callMethod('get')
expect(mockResponse).to.have.been.calledOnce // eslint-disable-line no-unused-expressions
})

it('should call the response method with req, res, and template path', () => {
expect(mockResponse).to.have.been.calledWith(req, res, 'simplified-account/settings/organisation-details/edit-organisation-details')
controllerTest.callMethod('get')
expect(mockResponse).to.have.been.calledWith(controllerTest.req, controllerTest.res, 'simplified-account/settings/organisation-details/edit-organisation-details')
})

it('should pass the context to the response method', () => {
controllerTest.callMethod('get')
expect(mockResponse).to.have.been.calledWith(sinon.match.any, sinon.match.any, sinon.match.any, {
messages: [],
organisationDetails: {
Expand All @@ -70,30 +74,21 @@ describe('Controller: settings/organisation-details', () => {
})

describe('post', () => {
before(() => {
nextRequest({
service: new Service({
id: '123',
external_id: SERVICE_ID
}),
account: {
type: ACCOUNT_TYPE
},
body: {
organisationName: 'Flancrest Enterprises',
addressLine1: '744 Evergreen Terrace',
addressLine2: '',
addressCity: 'Springfield',
addressPostcode: 'SP21NG',
addressCountry: 'US',
telephoneNumber: '09876543210',
organisationUrl: 'https://www.flancrest.example.com'
}
const postTest = ExperimentalTestBuilder.copy(controllerTest)
.withRequestBody({
organisationName: 'Flancrest Enterprises',
addressLine1: '744 Evergreen Terrace',
addressLine2: '',
addressCity: 'Springfield',
addressPostcode: 'SP21NG',
addressCountry: 'US',
telephoneNumber: '09876543210',
organisationUrl: 'https://www.flancrest.example.com'
})
call('post', 1)
})
.build()

it('should call the updateService method with the correct PATCH request', () => {
postTest.callMethodAtIndex('post', 1)
expect(updateServiceSpy).to.have.been.calledOnce // eslint-disable-line no-unused-expressions
expect(updateServiceSpy).to.have.been.calledWith(SERVICE_ID, [
{
Expand Down Expand Up @@ -140,8 +135,8 @@ describe('Controller: settings/organisation-details', () => {
})

it('should call redirect with the correct path', () => {
expect(res.redirect).to.have.been.calledOnce // eslint-disable-line no-unused-expressions
expect(res.redirect).to.have.been.calledWith(formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.organisationDetails.index, SERVICE_ID, ACCOUNT_TYPE))
expect(postTest.res.redirect).to.have.been.calledOnce // eslint-disable-line no-unused-expressions
expect(postTest.res.redirect).to.have.been.calledWith(formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.organisationDetails.index, SERVICE_ID, ACCOUNT_TYPE))
})
})
})
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
const sinon = require('sinon')
const formatSimplifiedAccountPathsFor = require('@utils/simplified-account/format/format-simplified-account-paths-for')
const paths = require('@root/paths')
const ControllerTestBuilder = require('@test/test-helpers/simplified-account/controllers/ControllerTestBuilder.class')
const chai = require('chai')
const sinonChai = require('sinon-chai')
chai.use(sinonChai)
const expect = chai.expect
const Service = require('@models/Service.class')
const ExperimentalTestBuilder = require('@test/test-helpers/simplified-account/controllers/ExperimentalTestBuilder.class')

const mockResponse = sinon.spy()

const ACCOUNT_TYPE = 'live'
const SERVICE_ID = 'service-id-123abc'

const { req, res, call, nextRequest } = new ControllerTestBuilder('@controllers/simplified-account/settings/organisation-details/organisation-details.controller')
const controllerTest = new ExperimentalTestBuilder('@controllers/simplified-account/settings/organisation-details/organisation-details.controller')
.withService(new Service({
id: '123',
external_id: SERVICE_ID,
Expand All @@ -36,19 +36,18 @@ const { req, res, call, nextRequest } = new ControllerTestBuilder('@controllers/
describe('Controller: settings/organisation-details', () => {
describe('get', () => {
describe('where organisation details have been set', () => {
before(() => {
call('get')
})

it('should call the response method', () => {
controllerTest.callMethod('get')
expect(mockResponse).to.have.been.calledOnce // eslint-disable-line no-unused-expressions
})

it('should call the response method with req, res, template path, and context', () => {
expect(mockResponse).to.have.been.calledWith(req, res, 'simplified-account/settings/organisation-details/index')
controllerTest.callMethod('get')
expect(mockResponse).to.have.been.calledWith(controllerTest.req, controllerTest.res, 'simplified-account/settings/organisation-details/index')
})

it('should pass the context to the response method', () => {
controllerTest.callMethod('get')
expect(mockResponse).to.have.been.calledWith(sinon.match.any, sinon.match.any, sinon.match.any, {
messages: [],
organisationDetails: {
Expand All @@ -63,20 +62,17 @@ describe('Controller: settings/organisation-details', () => {
})

describe('where organisation details have not been set', () => {
before(() => {
nextRequest({
service: new Service({
id: '123',
external_id: SERVICE_ID
}),
account: { type: ACCOUNT_TYPE }
})
call('get')
})
const controllerTestWithoutOrgDetails = ExperimentalTestBuilder.copy(controllerTest)
.withService(new Service({
id: '123',
external_id: SERVICE_ID
}))
.build()

it('should call the redirect method with the edit organisation details url', () => {
expect(res.redirect).to.have.been.calledOnce // eslint-disable-line no-unused-expressions
expect(res.redirect).to.have.been.calledWith(formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.organisationDetails.edit, SERVICE_ID, ACCOUNT_TYPE))
controllerTestWithoutOrgDetails.callMethod('get')
expect(controllerTestWithoutOrgDetails.res.redirect).to.have.been.calledOnce // eslint-disable-line no-unused-expressions
expect(controllerTestWithoutOrgDetails.res.redirect).to.have.been.calledWith(formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.organisationDetails.edit, SERVICE_ID, ACCOUNT_TYPE))
})
})
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
const sinon = require('sinon')
const _ = require('lodash')
const proxyquire = require('proxyquire')

class TestRequest {
withUser (user) {
this.user = user
return this
}

withAccount (account) {
this.account = account
return this
}

withService (service) {
this.service = service
return this
}

withBody (body) {
this.body = body
return this
}

static copy (sourceRequest) {
const newRequest = new TestRequest()
newRequest
.withAccount(sourceRequest.account)
.withUser(sourceRequest.user)
.withService(sourceRequest.service)
return newRequest
}
}

class TestResponse {
withRedirectSpy () {
this.redirect = sinon.spy()
return this
}
}

module.exports = class ExperimentalTestBuilder {
constructor (controllerPath) {
this.controllerPath = controllerPath
this.next = sinon.spy()
this.req = new TestRequest()
this.res = new TestResponse()
.withRedirectSpy()
}

withAccountType (type) {
this.req.withAccount({ type })
return this
}

withAccount (account) {
this.req.withAccount(account)
return this
}

withRequestBody (body) {
this.req.withBody(body)
return this
}

withServiceExternalId (serviceExternalId) {
this.req.service.externalId = serviceExternalId
return this
}

withService (service) {
this.req.service = service
return this
}

withStubs (stubs) {
this.stubs = stubs
return this
}

withReq (req) {
this.req = new TestRequest()
.withService(req.service)
.withBody(req.body)
.withAccount(req.account)
.withUser(req.user)
return this
}

withRes (res) {
this.res = {
...res
}
return this
}

withNext (next) {
this.next = next
return this
}

build () {
return new ControllerTest(
this.controllerPath,
this.req,
this.res,
this.next,
this.stubs
)
}

static copy (sourceTest) {
return new ExperimentalTestBuilder(sourceTest.controllerPath)
.withReq(sourceTest.req)
.withRes(sourceTest.res)
.withNext(sourceTest.next)
.withStubs(sourceTest.stubs)
// .build()
}
}

class ControllerTest {
constructor (controllerPath, req, res, next, stubs) {
this.controllerPath = controllerPath
this.controller = proxyquire(this.controllerPath, {
...stubs
})

this.req = req
this.res = res
this.next = next
this.stubs = stubs
}

callMethod (method, recursive = false) {
sinon.resetHistory()
const controllerMethod = this.controller[method]
return this.#call(controllerMethod, recursive)
}

call (recursive = false) {
sinon.resetHistory()
return this.#call(this.controller, recursive)
}

#call (method, recursive = false) {
if (method instanceof Array && recursive) {
return method.forEach(async subMethod => {
await this._call(subMethod, recursive)
})
} else if (typeof method !== 'function') {
throw new Error(`Method [${method}] is not a function`)
}

return method(this.req, this.res, this.next)
}

callMethodAtIndex (method, index, recursive = false) {
sinon.resetHistory()
const controllerMethod = this.controller[method][index]
return this.#call(controllerMethod)
}
}

0 comments on commit 3a3c742

Please sign in to comment.