Skip to content

Commit

Permalink
SIR-676: feedback form work (#88)
Browse files Browse the repository at this point in the history
* feedback form work

* sorting test coverage
  • Loading branch information
teddmason authored Mar 22, 2024
1 parent f7d0c47 commit b35056b
Show file tree
Hide file tree
Showing 12 changed files with 338 additions and 7 deletions.
24 changes: 24 additions & 0 deletions server/routes/__tests__/feedback-success.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { submitGetRequest } from '../../__test-helpers__/server.js'
import constants from '../../utils/constants.js'
import { session } from '../../__mock-data__/session-water-pollution.js'

const url = constants.routes.FEEDBACK_SUCCESS

describe(url, () => {
describe('GET', () => {
it(`Should return success response and correct view for ${url} with link to return to referer`, async () => {
session[constants.redisKeys.FEEDBACK] = {
feedbackURL: 'http://localhost:3000/water-pollution'
}
const response = await submitGetRequest({ url }, 'Thank you for your feedback', constants.statusCodes.OK, session)
expect(response.payload).toContain('<a href="http://localhost:3000/water-pollution">Go back to the page you were looking at</a>.')
})
it(`Should return success response and correct view for ${url} with link to start`, async () => {
session[constants.redisKeys.FEEDBACK] = {
feedbackURL: 'http://localhost:3000/report-sent'
}
const response = await submitGetRequest({ url }, 'Thank you for your feedback', constants.statusCodes.OK, session)
expect(response.payload).toContain('<a href="/">Return to the start of the service</a>.')
})
})
})
75 changes: 75 additions & 0 deletions server/routes/__tests__/feedback.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { submitGetRequest, submitPostRequest } from '../../__test-helpers__/server.js'
import constants from '../../utils/constants.js'
import { sendMessage } from '../../services/service-bus.js'
import { session } from '../../__mock-data__/session-water-pollution.js'
jest.mock('../../services/service-bus.js')

const url = constants.routes.FEEDBACK
const refererUrl = 'http://localhost:3000/water-pollution'

describe(url, () => {
describe('GET', () => {
it(`Should return success response and correct view for ${url}`, async () => {
const response = await submitGetRequest({ url }, 'Give feedback on the Report an environmental problem service')
expect(response.request.yar.get(constants.redisKeys.FEEDBACK).feedbackURL).toEqual('')
})
it(`Should return success response and correct view for ${url} and set the feedbackURL`, async () => {
const options = {
url,
headers: {
referer: refererUrl
}
}
const response = await submitGetRequest(options, 'Give feedback on the Report an environmental problem service')
expect(response.request.yar.get(constants.redisKeys.FEEDBACK).feedbackURL).toEqual(refererUrl)
})
})
describe('POST', () => {
it('Should successfully post valid feedback including feedback referer url', async () => {
const feedbackURL = refererUrl
session[constants.redisKeys.FEEDBACK] = {
feedbackURL
}
const options = {
url,
payload: {
feedback: 'vsatisfied',
otherInfo: 'This is test feedback'
}
}
const response = await submitPostRequest(options, 302, session)
expect(sendMessage).toHaveBeenCalledTimes(1)
expect(sendMessage).toHaveBeenCalledWith(expect.objectContaining({
givingFeedbackToAEnvironmentalProblemReport: expect.objectContaining({
feedbackRating: 'vsatisfied',
feedbackText: 'This is test feedback',
feedbackURL
})
}), '-feedback')
expect(response.request.yar.get(constants.redisKeys.FEEDBACK).feedbackURL).toEqual(feedbackURL)
})
it('Should fail validation if no radio selected', async () => {
const options = {
url,
payload: {}
}
const response = await submitPostRequest(options, constants.statusCodes.OK)
expect(response.payload).toContain('There is a problem')
expect(response.payload).toContain('Select how you feel about the service')
})
it('Should fail if payload validation fails', async () => {
session[constants.redisKeys.FEEDBACK] = {
feedbackURL: false
}
const options = {
url,
payload: {
feedback: 'vsatisfied',
otherInfo: 'This is test feedback'
}
}
const response = await submitPostRequest(options, constants.statusCodes.PROBLEM_WITH_SERVICE, session)
expect(response.payload).toContain('Sorry, there is a problem with the service')
})
})
})
23 changes: 23 additions & 0 deletions server/routes/feedback-success.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import constants from '../utils/constants.js'

const handlers = {
get: (request, h) => {
const { feedbackURL } = request.yar.get(constants.redisKeys.FEEDBACK)
const returnToStart = feedbackURL.includes(constants.routes.REPORT_SENT)
return h.view(constants.views.FEEDBACK_SUCCESS, {
returnToStart,
feedbackURL
})
}
}

export default [
{
method: 'GET',
path: constants.routes.FEEDBACK_SUCCESS,
handler: handlers.get,
options: {
auth: false
}
}
]
68 changes: 68 additions & 0 deletions server/routes/feedback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import constants from '../utils/constants.js'
import { getErrorSummary, validatePayload } from '../utils/helpers.js'
import { sendMessage } from '../services/service-bus.js'

const handlers = {
get: (request, h) => {
request.yar.set(constants.redisKeys.FEEDBACK, {
feedbackURL: request.headers.referer || ''
})
return h.view(constants.views.FEEDBACK)
},
post: async (request, h) => {
const { feedback, otherInfo } = request.payload

// validate payload
if (!feedback) {
const errorSummary = getErrorSummary()
errorSummary.errorList.push({
text: 'Select how you feel about the service',
href: '#feedback'
})
return h.view(constants.views.FEEDBACK, {
errorSummary
})
}

const payload = buildPayload(request, feedback, otherInfo)

// test the payload against the schema
if (!validatePayload(payload, true)) {
throw new Error('Invalid payload')
}

await sendMessage(payload, '-feedback')

return h.redirect(constants.routes.FEEDBACK_SUCCESS)
}
}

const buildPayload = (request, feedback, otherInfo) => {
return {
givingFeedbackToAEnvironmentalProblemReport: {
sessionGuid: request.yar.id,
feedbackDateAndTime: (new Date()).toISOString(),
feedbackRating: feedback,
feedbackURL: request.yar.get(constants.redisKeys.FEEDBACK).feedbackURL,
feedbackText: otherInfo
}
}
}

export default [
{
method: 'GET',
path: constants.routes.FEEDBACK,
handler: handlers.get,
options: {
auth: false
}
}, {
method: 'POST',
path: constants.routes.FEEDBACK,
handler: handlers.post,
options: {
auth: false
}
}
]
40 changes: 40 additions & 0 deletions server/schemas/feedback-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "SIR User Response",
"type": "object",
"properties": {
"givingFeedbackToAEnvironmentalProblemReport": {
"type": "object",
"properties": {
"sessionGuid": {
"type": "string",
"format": "uuid"
},
"feedbackRating": {
"type": "string"
},
"feedbackDateAndTime": {
"type": "string",
"format": "date-time"
},
"feedbackURL": {
"type": "string"
},
"feedbackText": {
"type": "string"
}
},
"required": [
"sessionGuid",
"feedbackRating",
"feedbackDateAndTime",
"feedbackURL",
"feedbackText"
],
"additionalProperties": false
}
},
"required": [
"givingFeedbackToAEnvironmentalProblemReport"
]
}
4 changes: 2 additions & 2 deletions server/services/service-bus.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import config from '../utils/config.js'
const connectionString = config.serviceBusConnectionString
const queueName = config.serviceBusQueueName

const sendMessage = async message => {
const sendMessage = async (message, queueSuffix = '') => {
const sbClient = new ServiceBusClient(connectionString)
const sender = sbClient.createSender(queueName)
const sender = sbClient.createSender(`${queueName}${queueSuffix}`)
const batch = await sender.createMessageBatch()
batch.tryAddMessage({ body: message })
await sender.sendMessages(batch)
Expand Down
4 changes: 4 additions & 0 deletions server/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const ERROR = 'error'
const PUBLIC = 'public'
const HOME = 'home'
const REPORT_SENT = 'report-sent'
const FEEDBACK = 'feedback'
const FEEDBACK_SUCCESS = 'feedback-success'

const WATER_POLUTION = 'water-pollution'
const WATER_POLLUTION_WATER_FEATURE = 'water-pollution/water-feature'
Expand All @@ -38,6 +40,8 @@ const views = {
ERROR,
PUBLIC,
HOME,
FEEDBACK,
FEEDBACK_SUCCESS,
REPORT_SENT,
WATER_POLUTION,
WATER_POLLUTION_WATER_FEATURE,
Expand Down
8 changes: 5 additions & 3 deletions server/utils/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ import constants from './constants.js'
import Ajv from 'ajv'
import addFormats from 'ajv-formats'

const jsonSchema = JSON.parse(fs.readFileSync('./server/schemas/sirp-schema.json'))
const sirpSchema = JSON.parse(fs.readFileSync('./server/schemas/sirp-schema.json'))
const feedbackSchema = JSON.parse(fs.readFileSync('./server/schemas/feedback-schema.json'))

const getErrorSummary = () => {
return JSON.parse(JSON.stringify(constants.errorSummary))
}

const validatePayload = payload => {
const validatePayload = (payload, feedback = false) => {
const schema = !feedback ? sirpSchema : feedbackSchema
const ajv = new Ajv({ strict: false })
addFormats(ajv)
const valid = ajv.validate(jsonSchema, payload)
const valid = ajv.validate(schema, payload)
if (!valid) {
console.error(ajv.errors)
}
Expand Down
23 changes: 23 additions & 0 deletions server/views/feedback-success.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{% extends 'layout.html' %}

{% set pageTitle = 'Thank you for your feedback' %}

{% block content %}

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-l">
{{ pageTitle }}
</h1>
<p>
{% if returnToStart %}
<a href="/">Return to the start of the service</a>.
{% else %}
<a href="{{ feedbackURL }}">Go back to the page you were looking at</a>.
{% endif %}

</p>
</div>
</div>

{% endblock %}
71 changes: 71 additions & 0 deletions server/views/feedback.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{% extends 'form-layout.html' %}

{% set pageTitle = 'Give feedback' %}
{% set pageHeader = 'Give feedback on the Report an environmental problem service' %}

{% block formContent %}

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-l">
{{ pageHeader }}
</h1>
<h2 class="govuk-heading-m">
Satisfaction survey
</h2>
{{ govukRadios({
name: "feedback",
id: "feedback",
errorMessage: findErrorMessageById(errorSummary, "feedback"),
fieldset: {
legend: {
text: "Overall, how did you feel about the service?",
isPageHeading: true,
classes: "govuk-fieldset__legend--s"
}
},
items: [
{
value: "vsatisfied",
text: "Very satisfied"
},
{
value: "satisfied",
text: "Satisfied"
},
{
value: "neither",
text: "Neither satisfied or dissatisfied"
},
{
value: "dissatisfied",
text: "Dissatisfied"
},
{
value: "vdissatisfied",
text: "Very dissatisfied"
}
]
}) }}

{{ govukTextarea({
name: "otherInfo",
id: "otherInfo",
rows: "10",
label: {
text: "How could we improve this service?",
classes: "govuk-label--s",
isPageHeading: true
},
hint: {
text: "Do not include any personal or financial information, for example your National Insurance or credit card numbers."
}
}) }}

{{ govukButton({
text: "Send feedback"
}) }}
</div>
</div>

{% endblock %}
2 changes: 1 addition & 1 deletion server/views/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
tag: {
text: "beta"
},
html: 'This is a new service – <a class="govuk-link" href="https://defragroup.eu.qualtrics.com/jfe/form/SV_cMxUuwi2OGTwAfQ" target="_blank" rel="noopener noreferrer">give feedback (opens new tab)</a> to help us to improve it.'
html: 'This is a new service – <a class="govuk-link" href="/feedback">leave feedback</a> to help us to improve it.'
}) }}

<!-- Hide the back link if Javascript is not available -->
Expand Down
Loading

0 comments on commit b35056b

Please sign in to comment.