Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions package-lock.json

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

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
"@aws-sdk/client-sns": "^3.864.0",
"@defra/forms-engine-plugin": "^3.0.7",
"@defra/forms-model": "^3.0.552",
"@defra/forms-engine-plugin": "^3.0.9",
"@defra/forms-model": "^3.0.559",
"@defra/hapi-tracing": "^1.26.0",
"@elastic/ecs-pino-format": "^1.5.0",
"@hapi/boom": "^10.0.1",
Expand Down
96 changes: 0 additions & 96 deletions src/server/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
import { getUploadStatus } from '@defra/forms-engine-plugin/engine/services/uploadService.js'
import {
FileStatus,
UploadStatus,
type UploadStatusResponse
} from '@defra/forms-engine-plugin/engine/types.js'
import { FormStatus } from '@defra/forms-engine-plugin/types'
import { type Server } from '@hapi/hapi'
import { StatusCodes } from 'http-status-codes'
Expand Down Expand Up @@ -495,93 +489,3 @@ describe('Model cache', () => {
})
})
})
Comment thread
jbarnsley10 marked this conversation as resolved.

describe('Upload status route', () => {
let server: Server

beforeAll(async () => {
server = await createServer()
await server.initialize()
})

afterAll(async () => {
await server.stop()
})

beforeEach(() => {
jest.resetAllMocks()
})

test('GET /upload-status/{uploadId} returns upload status with 200 when successful', async () => {
const mockStatus: UploadStatusResponse = {
uploadStatus: UploadStatus.ready,
metadata: {
retrievalKey: 'some-key'
},
form: {
file: {
fileId: 'some-file-id',
filename: 'some-file-name',
contentLength: 1024,
fileStatus: FileStatus.complete
}
},
numberOfRejectedFiles: 0
}
jest.mocked(getUploadStatus).mockResolvedValueOnce(mockStatus)

const options = {
method: 'GET',
url: `${FORM_PREFIX}/upload-status/123e4567-e89b-12d3-a456-426614174000`
}

const res = await server.inject(options)

expect(res.statusCode).toBe(StatusCodes.OK)
expect(res.result).toEqual(mockStatus)
expect(getUploadStatus).toHaveBeenCalledWith(
'123e4567-e89b-12d3-a456-426614174000'
)
})

test('GET /upload-status/{uploadId} returns 400 when status check fails', async () => {
jest.mocked(getUploadStatus).mockResolvedValueOnce(undefined)

const options = {
method: 'GET',
url: `${FORM_PREFIX}/upload-status/123e4567-e89b-12d3-a456-426614174000`
}

const res = await server.inject(options)

expect(res.statusCode).toBe(StatusCodes.BAD_REQUEST)
expect(res.result).toEqual({ error: 'Status check failed' })
})

test('GET /upload-status/{uploadId} returns 500 when exception occurs', async () => {
jest
.mocked(getUploadStatus)
.mockRejectedValueOnce(new Error('Service unavailable'))

const options = {
method: 'GET',
url: `${FORM_PREFIX}/upload-status/123e4567-e89b-12d3-a456-426614174000`
}

const res = await server.inject(options)

expect(res.statusCode).toBe(StatusCodes.INTERNAL_SERVER_ERROR)
expect(res.result).toEqual({ error: 'Status check error' })
})

test('GET /upload-status/{uploadId} returns 400 for invalid uploadId format', async () => {
const options = {
method: 'GET',
url: `${FORM_PREFIX}/upload-status/not-a-valid-guid`
}

const res = await server.inject(options)

expect(res.statusCode).toBe(StatusCodes.BAD_REQUEST)
})
})
9 changes: 8 additions & 1 deletion src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { requestLogger } from '~/src/server/common/helpers/logging/request-logge
import { requestTracing } from '~/src/server/common/helpers/logging/request-tracing.js'
import { buildRedisClient } from '~/src/server/common/helpers/redis-client.js'
import { FORM_PREFIX } from '~/src/server/constants.js'
import { SummaryPageWithConfirmationEmailController } from '~/src/server/plugins/SummaryPageWithConfirmationEmailController.js'
import { configureBlankiePlugin } from '~/src/server/plugins/blankie.js'
import { configureCrumbPlugin } from '~/src/server/plugins/crumb.js'
import pluginErrorPages from '~/src/server/plugins/errorPages.js'
Expand Down Expand Up @@ -138,7 +139,10 @@ export const configureEnginePlugin = async ({
cache: 'session',
nunjucks: {
baseLayoutPath: 'layout.html',
paths
paths: [
...paths,
join(config.get('appDir'), 'views', 'custom-engine-views')
]
},
model,
services,
Expand All @@ -158,6 +162,9 @@ export const configureEnginePlugin = async ({
? `/save-and-exit/${slug}`
: `/save-and-exit/${slug}/${state}`
)
},
controllers: {
SummaryPageWithConfirmationEmailController
}
}
}
Expand Down
146 changes: 146 additions & 0 deletions src/server/plugins/SummaryPageWithConfirmationEmailController.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { type CacheService } from '@defra/forms-engine-plugin/cache-service.js'
import { type QuestionPageController } from '@defra/forms-engine-plugin/controllers/QuestionPageController.js'
import { FormModel } from '@defra/forms-engine-plugin/engine/models/FormModel.js'
import { buildFormRequest } from '@defra/forms-engine-plugin/engine/pageControllers/__stubs__/request.js'
import { type FormSubmissionState } from '@defra/forms-engine-plugin/engine/types.js'
import {
type FormRequest,
type FormRequestPayload,
type FormResponseToolkit
} from '@defra/forms-engine-plugin/types'
import {
ControllerType,
type PageSummaryWithConfirmationEmail
} from '@defra/forms-model'

import {
SummaryPageWithConfirmationEmailController,
getUserConfirmationEmailAddress
} from '~/src/server/plugins/SummaryPageWithConfirmationEmailController.js'
import definition from '~/test/form/definitions/basic.js'

describe('SummaryPageWithConfirmationEmailController', () => {
let model: FormModel
let controller: SummaryPageWithConfirmationEmailController
let requestPage: FormRequest

const response = {
code: jest.fn().mockImplementation(() => response)
}
const h: FormResponseToolkit = {
redirect: jest.fn().mockReturnValue(response),
view: jest.fn()
}

beforeEach(() => {
model = new FormModel(definition, {
basePath: 'test'
})

// Create a mock page for SummaryPageWithConfirmationEmailController
const mockPage = {
...definition.pages[0],
controller: ControllerType.SummaryWithConfirmationEmail
} as unknown as PageSummaryWithConfirmationEmail

controller = new SummaryPageWithConfirmationEmailController(model, mockPage)

requestPage = buildFormRequest({
method: 'get',
url: new URL('http://example.com/test/summary'),
path: '/test/summary',
params: {
path: 'summary',
slug: 'test'
},
query: {},
app: { model }
} as FormRequest)
})

describe('handle errors', () => {
it('should display errors including summary', async () => {
const state: FormSubmissionState = {
$$__referenceNumber: 'foobar',
licenceLength: 365,
fullName: 'John Smith'
}
const request = {
...requestPage,
method: 'post',
payload: { invalid: '123', action: 'send' }
} as unknown as FormRequestPayload

const context = model.getFormContext(request, state)

jest
.spyOn(controller as unknown as QuestionPageController, 'getState')
.mockResolvedValue({})
jest
.spyOn(controller as unknown as QuestionPageController, 'setState')
.mockResolvedValue(state)

const postHandler = controller.makePostRouteHandler()
await postHandler(request, context, h)

const viewModel = controller.getSummaryViewModel(request, context)

expect(h.view).toHaveBeenCalledWith(
'summary-with-confirmation',
expect.anything()
)
expect(viewModel.errors).toHaveLength(1)
const errorText = viewModel.errors ? viewModel.errors[0].text : ''
expect(errorText).toBe('"invalid" is not allowed')
})
})

describe('handleSaveAndExit', () => {
it('should invoke saveAndExit plugin option', async () => {
const saveAndExitMock = jest.fn(() => ({}))
const state: FormSubmissionState = {
$$__referenceNumber: 'foobar',
licenceLength: 365,
fullName: 'John Smith'
}
const request = {
...requestPage,
server: {
plugins: {
'forms-engine-plugin': {
saveAndExit: saveAndExitMock,
cacheService: {
clearState: jest.fn()
} as unknown as CacheService
}
}
},
method: 'post',
payload: { fullName: 'John Smith', action: 'save-and-exit' }
} as unknown as FormRequestPayload

const context = model.getFormContext(request, state)

const postHandler = controller.makePostRouteHandler()
await postHandler(request, context, h)

expect(saveAndExitMock).toHaveBeenCalledWith(request, h, context)
})
})

describe('getUserConfirmationEmailAddress', () => {
test('should get confirmation email', () => {
const field = getUserConfirmationEmailAddress()
expect(field.name).toBe('userConfirmationEmailAddress')
expect(field.value).toBeUndefined()
})

test('should get confirmation email with retained value', () => {
const field = getUserConfirmationEmailAddress({
userConfirmationEmailAddress: 'emailval'
})
expect(field.name).toBe('userConfirmationEmailAddress')
expect(field.value).toBe('emailval')
})
})
})
Loading
Loading