Skip to content

Commit

Permalink
Merge pull request #112 from digital-land/feat/lpa-autoselect
Browse files Browse the repository at this point in the history
Feat/lpa autoselect
  • Loading branch information
rosado authored Jul 10, 2024
2 parents 22e8824 + 035be4e commit 1880045
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 18 deletions.
19 changes: 19 additions & 0 deletions src/controllers/lpaDetailsController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import PageController from './pageController.js'
import { fetchLocalAuthorities } from '../utils/fetchLocalAuthorities.js'

class LpaDetailsController extends PageController {
async locals (req, res, next) {
const localAuthoritiesNames = await fetchLocalAuthorities()

const listItems = localAuthoritiesNames.map(name => ({
text: name,
value: name
}))

req.form.options.localAuthorities = listItems

super.locals(req, res, next)
}
}

export default LpaDetailsController
3 changes: 2 additions & 1 deletion src/routes/form-wizard/endpoint-submission-form/steps.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// ToDo: Split this into two form wizards
import chooseDatasetController from '../../../controllers/chooseDatasetController.js'
import LpaDetailsController from '../../../controllers/lpaDetailsController.js'
import PageController from '../../../controllers/pageController.js'
import CheckAnswersController from '../../../controllers/CheckAnswersController.js'

Expand All @@ -20,6 +20,7 @@ export default {
...defaultParams,
fields: ['lpa', 'name', 'email'],
next: 'choose-dataset',
controller: LpaDetailsController,
backLink: '/start'
},
'/choose-dataset': {
Expand Down
1 change: 1 addition & 0 deletions src/serverSetup/middlewares.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function setupMiddlewares (app) {
})

app.use('/assets', express.static('./node_modules/govuk-frontend/dist/govuk/assets'))
app.use('/assets', express.static('./node_modules/@x-govuk/govuk-prototype-components/x-govuk'))
app.use('/public', express.static('./public'))

app.use(cookieParser())
Expand Down
42 changes: 42 additions & 0 deletions src/utils/fetchLocalAuthorities.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import axios from 'axios'

/**
* Fetches a list of local authority names from a specified dataset.
*
* This function queries a dataset for local authorities, extracting a distinct list of names.
* It performs an HTTP GET request to retrieve the data, then processes the response to return
* only the names of the local authorities.
*
* @returns {Promise<string[]>} A promise that resolves to an array of local authority names.
* @throws {Error} Throws an error if the HTTP request fails or data processing encounters an issue.
*/
export const fetchLocalAuthorities = async () => {
const sql = `select
distinct provision.organisation,
organisation.name,
organisation.dataset
from
provision,
organisation
where
provision.organisation = organisation.organisation
order by
provision.organisation`

const url = `https://datasette.planning.data.gov.uk/digital-land.json?sql=${encodeURIComponent(sql)}`
try {
const response = await axios.get(url)
const names = response.data.rows.map(row => {
if (row[1] === null) {
console.log('Null value found in response:', row)
return null
} else {
return row[1]
}
}).filter(name => name !== null) // Filter out null values
return names // Return the fetched data
} catch (error) {
console.error('Error fetching local authorities data:', error)
throw error // Rethrow the error to be handled by the caller
}
}
7 changes: 7 additions & 0 deletions src/views/layouts/main.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ <h2 class="govuk-heading-m">Get help</h2>
{% block bodyEnd %}
{{ super()}}
{%block scripts %}
<body>
...
<script type="module" src="/assets/all.js"></script>
<script type="module">
window.GOVUKPrototypeComponents.initAll()
</script>
</body>
{{ super() }}
<script src="/public/js/application.bundle.js"></script>
{% endblock %}
Expand Down
33 changes: 16 additions & 17 deletions src/views/submit/lpa-details.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% extends "layouts/main.html" %}

{% from "govuk/components/input/macro.njk" import govukInput %}
{% from "govuk/components/button/macro.njk" import govukButton %}
{% from "x-govuk/components/autocomplete/macro.njk" import xGovukAutocomplete %}
{% from 'govuk/components/error-summary/macro.njk' import govukErrorSummary %}

{% set serviceType = 'Submit' %}
Expand Down Expand Up @@ -40,22 +40,21 @@ <h1 class="govuk-heading-l">

<form novalidate method="post">

{{
govukInput({
label: {
text: "Local planning authority",
classes: "govuk-label--m",
isPageHeading: false
},
id: "lpa",
name: "lpa",
classes: "govuk-!-width-three-quarters",
errorMessage: {
text: 'lpa' | validationMessageLookup(errors['lpa'].type)
} if 'lpa' in errors,
value: values.lpa
})
}}
{{ xGovukAutocomplete({
id: "lpa",
name: "lpa",
allowEmpty: false,
label: {
classes: "govuk-label--m",
isPageHeading: false,
text: "Choose your local planning authority"
},
items: options.localAuthorities,
errorMessage: {
text: 'lpa' | validationMessageLookup(errors['lpa'].type)
} if 'lpa' in errors,
value: values.lpa
}) }}

{{ govukInput({
label: {
Expand Down
41 changes: 41 additions & 0 deletions test/unit/fetchLocalAuthorities.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import axios from 'axios'
import { vi, it, describe, expect } from 'vitest'
import { fetchLocalAuthorities } from '../../src/utils/fetchLocalAuthorities'

// Mock axios.get to return a fake response
vi.mock('axios')
axios.get.mockResolvedValue({
data: {
rows: [
[1, 'Local Authority 1'],
[2, 'Local Authority 2'],
[3, 'Local Authority 3']
]
}
})

describe('fetchLocalAuthorities', () => {
it('should fetch local authority names', async () => {
const result = await fetchLocalAuthorities()
expect(result).toEqual(['Local Authority 1', 'Local Authority 2', 'Local Authority 3'])
})

it('should throw an error if the HTTP request fails', async () => {
axios.get.mockRejectedValue(new Error('Failed to fetch data'))
await expect(fetchLocalAuthorities()).rejects.toThrow('Failed to fetch data')
})

it('should throw an error if data processing encounters an issue', async () => {
axios.get.mockResolvedValue({
data: {
rows: [
[1, 'Local Authority 1'],
[2, null], // Simulate null value in the response
[3, 'Local Authority 3']
]
}
})
const result = await fetchLocalAuthorities()
expect(result).toEqual(['Local Authority 1', 'Local Authority 3'])
})
})
66 changes: 66 additions & 0 deletions test/unit/lpaDetailsController.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/* eslint-disable no-import-assign */
/* eslint-disable new-cap */

import PageController from '../../src/controllers/pageController.js'
import { vi, it, describe, expect, beforeEach, afterEach } from 'vitest'

vi.mock('../../src/utils/fetchLocalAuthorities.js')

describe('lpaDetailsController', async () => {
let fetchLocalAuthorities
let controller

beforeEach(async () => {
fetchLocalAuthorities = await import('../../src/utils/fetchLocalAuthorities')
const LpaDetailsController = await import('../../src/controllers/lpaDetailsController.js')
controller = new LpaDetailsController.default({
route: '/lpa-details'
})
})

afterEach(() => {
vi.restoreAllMocks()
})

describe('locals', () => {
it('should set localAuthorities options in the form', async () => {
const req = {
form: {
options: {}
}
}
const res = {}
const next = vi.fn()

const localAuthoritiesNames = ['Authority 1', 'Authority 2']

fetchLocalAuthorities.fetchLocalAuthorities = vi.fn().mockResolvedValue(localAuthoritiesNames)

await controller.locals(req, res, next)

expect(fetchLocalAuthorities.fetchLocalAuthorities).toHaveBeenCalled()
expect(req.form.options.localAuthorities).toEqual([
{ text: 'Authority 1', value: 'Authority 1' },
{ text: 'Authority 2', value: 'Authority 2' }
])
expect(next).toHaveBeenCalled()
})

it('should call super.locals', async () => {
const req = {
form: {
options: {}
}
}
const res = {}
const next = vi.fn()

fetchLocalAuthorities.fetchLocalAuthorities = vi.fn().mockResolvedValue([])
const superLocalsSpy = vi.spyOn(PageController.prototype, 'locals')

await controller.locals(req, res, next)

expect(superLocalsSpy).toHaveBeenCalledWith(req, res, next)
})
})
})

0 comments on commit 1880045

Please sign in to comment.