From 2157e1713aae1716f1dd0078d0e653d4b2d3f4ba Mon Sep 17 00:00:00 2001 From: Roland Sadowski Date: Fri, 4 Oct 2024 11:27:11 +0100 Subject: [PATCH 01/13] fix typo in error message --- src/services/asyncRequestApi.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/asyncRequestApi.js b/src/services/asyncRequestApi.js index cdcc831f..e96f7ea5 100644 --- a/src/services/asyncRequestApi.js +++ b/src/services/asyncRequestApi.js @@ -45,8 +45,9 @@ const postRequest = async (formData) => { const errorMessage = `post request failed: response.status = '${error.response?.status}', ` + `data: '${error.response?.data}', ` + `cause: '${error?.cause}' ` + + `code: ${error.code}, ` + (error.request ? 'No response received, ' : '') + - `message: '${error.message ?? 'no meesage provided'}', ` + + `message: '${error.message ?? 'no message provided'}', ` + (error.config ? `Error in Axios configuration ${error?.config}` : '') throw new Error(errorMessage) From f52ad8bce3849ed70ea9f2ae3174a0ad60eec550 Mon Sep 17 00:00:00 2001 From: Roland Sadowski Date: Fri, 4 Oct 2024 11:30:48 +0100 Subject: [PATCH 02/13] make utility fn for param validation middleware --- src/middleware/common.middleware.js | 6 +++++- src/middleware/issueDetails.middleware.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/middleware/common.middleware.js b/src/middleware/common.middleware.js index 97e480ec..27e96c22 100644 --- a/src/middleware/common.middleware.js +++ b/src/middleware/common.middleware.js @@ -79,7 +79,7 @@ export const fetchOrgInfo = fetchOne({ * @param {*} res * @param {*} next */ -export function validateQueryParams (req, res, next) { +export function validateQueryParamsFn (req, res, next) { try { v.parse(this.schema || v.any(), req.params) next() @@ -88,6 +88,10 @@ export function validateQueryParams (req, res, next) { } } +export function validateQueryParams (context) { + return validateQueryParamsFn.bind(context) +} + export const fetchLpaDatasetIssues = fetchMany({ query: ({ params, req }) => performanceDbApi.datasetIssuesQuery(req.resourceStatus.resource, params.dataset), result: 'issues' diff --git a/src/middleware/issueDetails.middleware.js b/src/middleware/issueDetails.middleware.js index 2e79d059..a537a3c9 100644 --- a/src/middleware/issueDetails.middleware.js +++ b/src/middleware/issueDetails.middleware.js @@ -15,7 +15,7 @@ export const IssueDetailsQueryParams = v.object({ resourceId: v.optional(v.string()) }) -const validateIssueDetailsQueryParams = validateQueryParams.bind({ +const validateIssueDetailsQueryParams = validateQueryParams({ schema: IssueDetailsQueryParams }) From af4a50a8a643d1d55d6491d75964abca1be69252 Mon Sep 17 00:00:00 2001 From: Roland Sadowski Date: Fri, 4 Oct 2024 11:31:27 +0100 Subject: [PATCH 03/13] fix typos in JSDoc --- src/middleware/overview.middleware.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/middleware/overview.middleware.js b/src/middleware/overview.middleware.js index 10bb11d2..55f738b8 100644 --- a/src/middleware/overview.middleware.js +++ b/src/middleware/overview.middleware.js @@ -56,7 +56,7 @@ const fetchEntityCounts = async (req, res, next) => { const statusOrdering = new Map(['Live', 'Needs fixing', 'Error', 'Not submitted'].map((status, i) => [status, i])) /** - * The overview data can contain multiple rows per dataset + * The overview data can contain multiple rows per dataset, * and we want a collection of with one item per dataset, * because that's how we display it on the page. * @@ -94,7 +94,7 @@ export function aggregateOverviewData (lpaOverview) { } /** - * Calculates overal "health" of the datasets (not)provided by an organisation. + * Calculates overall "health" of the datasets (not)provided by an organisation. * * @param {[number, number, number]} accumulator * @param {{ endpoint?: string, status: string }} dataset From 44daa61633ba552fd661210f91dd2f2d557ce8f5 Mon Sep 17 00:00:00 2001 From: Roland Sadowski Date: Fri, 11 Oct 2024 16:38:44 +0100 Subject: [PATCH 04/13] check tool: support for deep links - new route: '/check/link' which requires query params and redirects to the correct step in the wizard - added DeepLinkController - to populate the session/history with passed query params - new filter for generating a deep link (path+params) - removed cookie parser (express docs say it's no longer needed and can actually cause issues) - update check tool templates to display the dataset and organisation names when the wizard was entered via a deep link - some utility functions/maps to simplify looking up datasets info - moved a predicate out of DatasetController so it can be reused --- src/controllers/chooseDatasetController.js | 10 +--- src/controllers/datasetController.js | 41 +++++--------- src/controllers/deepLinkController.js | 59 ++++++++++++++++++++ src/controllers/pageController.js | 15 +++-- src/filters/checkToolDeepLink.js | 11 ++++ src/filters/filters.js | 2 + src/middleware/datasetTaskList.middleware.js | 11 +++- src/routes/form-wizard/check/steps.js | 40 +++++++++---- src/routes/schemas.js | 2 +- src/serverSetup/session.js | 2 - src/utils/utils.js | 29 ++++++++++ src/views/check/geometry-type.html | 6 ++ src/views/check/results/errors.html | 2 +- src/views/check/results/no-errors.html | 7 ++- src/views/check/upload-method.html | 7 +++ src/views/check/upload.html | 7 +++ src/views/check/url.html | 6 ++ src/views/components/dataset-banner.html | 8 +++ src/views/organisations/datasetTaskList.html | 2 +- src/views/organisations/get-started.html | 4 +- src/views/organisations/issueDetails.html | 4 ++ test/unit/chooseDatasetController.test.js | 6 +- test/unit/datasetController.test.js | 32 +++++++---- test/unit/deepLinkController.test.js | 53 ++++++++++++++++++ test/unit/util.test.js | 33 +++++++++++ 25 files changed, 325 insertions(+), 74 deletions(-) create mode 100644 src/controllers/deepLinkController.js create mode 100644 src/filters/checkToolDeepLink.js create mode 100644 src/views/components/dataset-banner.html create mode 100644 test/unit/deepLinkController.test.js create mode 100644 test/unit/util.test.js diff --git a/src/controllers/chooseDatasetController.js b/src/controllers/chooseDatasetController.js index 71480192..1612fb72 100644 --- a/src/controllers/chooseDatasetController.js +++ b/src/controllers/chooseDatasetController.js @@ -1,16 +1,10 @@ import PageController from './pageController.js' -import { dataSubjects } from '../utils/utils.js' +import { dataSubjects, availableDatasets } from '../utils/utils.js' class ChooseDatasetController extends PageController { locals (req, res, next) { - const availableDataSubjects = Object.values(dataSubjects).filter(dataSubject => dataSubject.available) - const dataSets = Object.values(availableDataSubjects).map(dataSubject => dataSubject.dataSets).flat() - const availableDatasets = dataSets.filter(dataSet => dataSet.available) - availableDatasets.sort((a, b) => a.text.localeCompare(b.text)) - - req.form.options.datasetItems = availableDatasets - + req.form.options.datasetItems = availableDatasets(dataSubjects) super.locals(req, res, next) } } diff --git a/src/controllers/datasetController.js b/src/controllers/datasetController.js index 04c25a95..88ab1bf0 100644 --- a/src/controllers/datasetController.js +++ b/src/controllers/datasetController.js @@ -4,46 +4,31 @@ import PageController from './pageController.js' // ToDo: we shouldn't hardcode these values here, should we get them from the API // maybe take from specification -import { dataSubjects } from '../utils/utils.js' +import { dataSubjects, datasets, availableDatasets } from '../utils/utils.js' + +/** + * @param req + * @returns {boolean} + */ +export function requiresGeometryTypeToBeSelected (req) { + const dataset = req.body.dataset + const dataSet = datasets.get(dataset) + return dataSet?.requiresGeometryTypeSelection || false +} class DatasetController extends PageController { locals (req, res, next) { - const availableDataSubjects = Object.values(dataSubjects).filter(dataSubject => dataSubject.available) - const dataSets = Object.values(availableDataSubjects).map(dataSubject => dataSubject.dataSets).flat() - const availableDatasets = dataSets.filter(dataSet => dataSet.available) - availableDatasets.sort((a, b) => a.text.localeCompare(b.text)) - - req.form.options.datasetItems = availableDatasets - + req.form.options.datasetItems = availableDatasets(dataSubjects) super.locals(req, res, next) } // we shouldn't need this here but as we dont currently set the datasubject, we need to do so here post (req, res, next) { const dataset = req.body.dataset - // set the data-subject based on the dataset selected - let dataSubject = '' - for (const [key, value] of Object.entries(dataSubjects)) { - if (value.dataSets.find(dataSet => dataSet.value === dataset)) { - dataSubject = key - break - } - } + const { dataSubject } = datasets.get(dataset) || { dataSubject: '' } req.body['data-subject'] = dataSubject super.post(req, res, next) } - - requiresGeometryTypeToBeSelected (req) { - const dataset = req.body.dataset - - if (!dataset) { - return false - } - - const dataSubject = Object.values(dataSubjects).find(dataSubject => dataSubject.dataSets.find(dataSet => dataSet.value === dataset)) - const dataSet = dataSubject.dataSets.find(dataSet => dataSet.value === dataset) - return dataSet.requiresGeometryTypeSelection || false - } } export default DatasetController diff --git a/src/controllers/deepLinkController.js b/src/controllers/deepLinkController.js new file mode 100644 index 00000000..cb2bf139 --- /dev/null +++ b/src/controllers/deepLinkController.js @@ -0,0 +1,59 @@ +import PageController from './pageController.js' + +import { datasets } from '../utils/utils.js' +import logger from '../utils/logger.js' +import { types } from '../utils/logging.js' +import * as v from 'valibot' +import { NonEmptyString } from '../routes/schemas.js' + +const QueryParams = v.object({ + dataset: NonEmptyString, + orgName: NonEmptyString +}) + +/** + * Handles deep links in the Check Tool. + * + * It is meant to extract required params from query params + * and partially pre-populate the session with them, + * then redirect the user to the "next" page in the wizard + */ +class DeepLinkController extends PageController { + get (req, res, next) { + // if the query params don't contain what we need, redirect to the "get started" page, + // this way the user can still proceed (but need to fill the dataset+orgName themselves) + const { dataset, orgName } = req.query + const validationResult = v.safeParse(QueryParams, req.query) + if (!(validationResult.success && datasets.has(dataset))) { + logger.info('DeepLinkController.get(): invalid params for deep link, redirecting to start page', + { type: types.App, query: req.query }) + return res.redirect('/check') + } + + req.sessionModel.set('dataset', dataset) + const datasetInfo = datasets.get(dataset) ?? { dataSubject: '', requiresGeometryTypeSelection: false } + req.sessionModel.set('data-subject', datasetInfo.dataSubject) + req.sessionModel.set(this.checkToolDeepLinkSessionKey, + { 'data-subject': datasetInfo.dataSubject, orgName, dataset, datasetName: datasetInfo.text }) + + this.#addHistoryStep(req, '/check/dataset') + + super.post(req, res, next) + } + + #addHistoryStep (req, path, next) { + const newItem = { + path, + wizard: 'check-wizard', + fields: ['dataset', 'data-subject'], + skip: false, + continueOnEdit: false + } + + const history = req.journeyModel.get('history') || [] + history.push(newItem) + req.journeyModel.set('history', history) + } +} + +export default DeepLinkController diff --git a/src/controllers/pageController.js b/src/controllers/pageController.js index ae2a4d17..6f314d4d 100644 --- a/src/controllers/pageController.js +++ b/src/controllers/pageController.js @@ -3,15 +3,22 @@ import { logPageView } from '../utils/logging.js' const { Controller } = hmpoFormWizard class PageController extends Controller { - configure (req, res, callback) { - req.form.options.lastPage = this.options.backLink ? this.options.backLink : undefined - super.configure(req, res, callback) - } + checkToolDeepLinkSessionKey = 'check-tool-deep-link' get (req, res, next) { logPageView(this.options.route, req.sessionID, req.ip) super.get(req, res, next) } + + locals (req, res, next) { + if (this.options.backLink) { + req.form.options.lastPage = this.options.backLink + } + if (req.sessionModel) { + req.form.options.deepLink = req.sessionModel.get(this.checkToolDeepLinkSessionKey) + } + super.locals(req, res, next) + } } export default PageController diff --git a/src/filters/checkToolDeepLink.js b/src/filters/checkToolDeepLink.js new file mode 100644 index 00000000..df6f6e8e --- /dev/null +++ b/src/filters/checkToolDeepLink.js @@ -0,0 +1,11 @@ +/** + * Returns the deep link to the check tool for a given dataset and organisation + * + * @param {{name:string}} organisation + * @param {{dataset:string, name:string}} dataset + * + * @return {string} + */ +export function checkToolDeepLink (organisation, dataset) { + return `/check/link?dataset=${encodeURIComponent(dataset.dataset)}&orgName=${encodeURIComponent(organisation.name)}` +} diff --git a/src/filters/filters.js b/src/filters/filters.js index e333f633..8e1c33ab 100644 --- a/src/filters/filters.js +++ b/src/filters/filters.js @@ -5,6 +5,7 @@ import toErrorList from './toErrorList.js' import prettifyColumnName from './prettifyColumnName.js' import getFullServiceName from './getFullServiceName.js' import { makeDatasetSlugToReadableNameFilter } from './makeDatasetSlugToReadableNameFilter.js' +import { checkToolDeepLink } from './checkToolDeepLink.js' import pluralize from 'pluralize' /** maps dataset status (as returned by `fetchLpaOverview` middleware to a @@ -40,6 +41,7 @@ const addFilters = (nunjucksEnv, { datasetNameMapping }) => { nunjucksEnv.addFilter('getFullServiceName', getFullServiceName) nunjucksEnv.addFilter('statusToTagClass', statusToTagClass) nunjucksEnv.addFilter('pluralise', pluralize) + nunjucksEnv.addFilter('checkToolDeepLink', checkToolDeepLink) } export default addFilters diff --git a/src/middleware/datasetTaskList.middleware.js b/src/middleware/datasetTaskList.middleware.js index 802cdbe6..e77d3b1a 100644 --- a/src/middleware/datasetTaskList.middleware.js +++ b/src/middleware/datasetTaskList.middleware.js @@ -1,7 +1,8 @@ -import { fetchDatasetInfo, isResourceAccessible, isResourceNotAccessible, fetchLatestResource, fetchEntityCount, logPageError, fetchLpaDatasetIssues } from './common.middleware.js' +import { fetchDatasetInfo, isResourceAccessible, isResourceNotAccessible, fetchLatestResource, fetchEntityCount, logPageError, fetchLpaDatasetIssues, validateQueryParams } from './common.middleware.js' import { fetchOne, fetchIf, onlyIf, renderTemplate } from './middleware.builders.js' import performanceDbApi from '../services/performanceDbApi.js' import { statusToTagClass } from '../filters/filters.js' +import * as v from 'valibot' /** * Fetches the resource status @@ -111,7 +112,15 @@ const getDatasetTaskListError = renderTemplate({ handlerName: 'getDatasetTaskListError' }) +const validateParams = validateQueryParams({ + schema: v.object({ + lpa: v.string(), + dataset: v.string() + }) +}) + export default [ + validateParams, fetchResourceStatus, fetchOrgInfoWithStatGeo, fetchDatasetInfo, diff --git a/src/routes/form-wizard/check/steps.js b/src/routes/form-wizard/check/steps.js index a38e9c8c..c7eca24f 100644 --- a/src/routes/form-wizard/check/steps.js +++ b/src/routes/form-wizard/check/steps.js @@ -1,10 +1,11 @@ // ToDo: Split this into two form wizards import PageController from '../../../controllers/pageController.js' -import datasetController from '../../../controllers/datasetController.js' +import datasetController, { requiresGeometryTypeToBeSelected } from '../../../controllers/datasetController.js' import uploadFileController from '../../../controllers/uploadFileController.js' import submitUrlController from '../../../controllers/submitUrlController.js' import statusController from '../../../controllers/statusController.js' import resultsController from '../../../controllers/resultsController.js' +import deepLinkController from '../../../controllers/deepLinkController.js' const baseSettings = { controller: PageController, @@ -20,17 +21,13 @@ export default { template: 'check/start.html', noPost: true }, - // '/data-subject': { - // ...baseSettings, - // fields: ['data-subject'], - // next: 'dataset' - // }, '/dataset': { ...baseSettings, controller: datasetController, fields: ['dataset', 'data-subject'], + checkJourney: false, next: [ - { field: 'dataset', fn: 'requiresGeometryTypeToBeSelected', next: 'geometry-type' }, + { field: 'dataset', fn: requiresGeometryTypeToBeSelected, next: 'geometry-type' }, 'upload-method' ], backLink: './' @@ -39,7 +36,8 @@ export default { ...baseSettings, fields: ['geomType'], next: 'upload-method', - backLink: './dataset' + backLink: './dataset', + checkJourney: false }, '/upload-method': { ...baseSettings, @@ -48,21 +46,24 @@ export default { { field: 'upload-method', op: '===', value: 'url', next: 'url' }, 'upload' ], - backLink: './dataset' + backLink: './dataset', + checkJourney: false }, '/url': { ...baseSettings, controller: submitUrlController, fields: ['url', 'request_id'], next: (req, res) => `status/${req.sessionModel.get('request_id')}`, - backLink: './upload-method' + backLink: './upload-method', + checkJourney: false }, '/upload': { ...baseSettings, controller: uploadFileController, fields: ['datafile', 'request_id'], next: (req, res) => `status/${req.sessionModel.get('request_id')}`, - backLink: './upload-method' + backLink: './upload-method', + checkJourney: false }, '/status/:id': { ...baseSettings, @@ -90,5 +91,22 @@ export default { noPost: true, checkJourney: false, // ToDo: it would be useful here if we make sure they have selected if their results are ok from the previous step template: 'check/confirmation.html' + }, + // This step allows to fill in some of the required data via query params. + // This way we can link from a dataset issues page to the Check Tool without + // the user having to go through the whole process again. + // This means it doesn't render a page, but redirects the client to the next step + '/link': { + ...baseSettings, + controller: deepLinkController, + next: [ + { field: 'dataset', fn: requiresGeometryTypeToBeSelected, next: 'geometry-type' }, + 'upload-method' + ], + entryPoint: true, + resetJourney: true, + reset: true, + skip: true, + checkJourney: false } } diff --git a/src/routes/schemas.js b/src/routes/schemas.js index 7c9ed66b..7434f22d 100644 --- a/src/routes/schemas.js +++ b/src/routes/schemas.js @@ -14,7 +14,7 @@ export const ErrorParams = v.strictObject({ err: v.object({}) }) -const NonEmptyString = v.pipe(v.string(), v.nonEmpty()) +export const NonEmptyString = v.pipe(v.string(), v.nonEmpty()) export const Base = v.object({ // serviceName: NonEmptyString, diff --git a/src/serverSetup/session.js b/src/serverSetup/session.js index 19f0de57..2c1ee45a 100644 --- a/src/serverSetup/session.js +++ b/src/serverSetup/session.js @@ -1,13 +1,11 @@ import session from 'express-session' import { createClient } from 'redis' import RedisStore from 'connect-redis' -import cookieParser from 'cookie-parser' import config from '../../config/index.js' import logger from '../utils/logger.js' import { types } from '../utils/logging.js' export async function setupSession (app) { - app.use(cookieParser()) let sessionStore if ('redis' in config) { const urlPrefix = `redis${config.redis.secure ? 's' : ''}` diff --git a/src/utils/utils.js b/src/utils/utils.js index 0f63291c..136be3c2 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -94,6 +94,35 @@ export const dataSubjects = { } } +export function makeDatasetsLookup (dataSubjects) { + const lookup = new Map() + for (const [key, dataSubject] of Object.entries(dataSubjects)) { + for (const dataSet of dataSubject.dataSets) { + lookup.set(dataSet.value, { ...dataSet, dataSubject: key }) + } + } + + return lookup +} + +/** + * @type {Map} + */ +export const datasets = makeDatasetsLookup(dataSubjects) + +/** + * + * @param dataSubjects + * @returns {FlatArray<*[], 1>[]} datasets sorted by 'text' property + */ +export function availableDatasets (dataSubjects) { + const availableDataSubjects = Object.values(dataSubjects).filter(dataSubject => dataSubject.available) + const dataSets = Object.values(availableDataSubjects).map(dataSubject => dataSubject.dataSets).flat() + const availableDatasets = dataSets.filter(dataSet => dataSet.available) + availableDatasets.sort((a, b) => a.text.localeCompare(b.text)) + return availableDatasets +} + export const finishedProcessingStatuses = [ 'COMPLETE', 'FAILED' diff --git a/src/views/check/geometry-type.html b/src/views/check/geometry-type.html index 24c8853b..20aad265 100644 --- a/src/views/check/geometry-type.html +++ b/src/views/check/geometry-type.html @@ -2,6 +2,7 @@ {% from 'govuk/components/radios/macro.njk' import govukRadios %} {% from 'govuk/components/error-message/macro.njk' import govukErrorMessage %} {% from 'govuk/components/error-summary/macro.njk' import govukErrorSummary %} +{% from 'components/dataset-banner.html' import datasetBanner %} {% extends "layouts/main.html" %} @@ -23,6 +24,11 @@ {% endblock %} {% block content %} + + {% if options.deepLink %} + {{ datasetBanner(options.deepLink) }} + {% endif %} +
{% if error %} diff --git a/src/views/check/results/errors.html b/src/views/check/results/errors.html index e123c145..0c0306f7 100644 --- a/src/views/check/results/errors.html +++ b/src/views/check/results/errors.html @@ -4,7 +4,6 @@ {% from 'govuk/components/radios/macro.njk' import govukRadios %} {% from 'govuk/components/inset-text/macro.njk' import govukInsetText %} {% from "govuk/components/pagination/macro.njk" import govukPagination %} - {% from "../../components/table.html" import table %} {% set serviceType = 'Check' %} @@ -21,6 +20,7 @@ {% endblock %} {% block content %} +
diff --git a/src/views/check/results/no-errors.html b/src/views/check/results/no-errors.html index 1466c87c..6d03bafc 100644 --- a/src/views/check/results/no-errors.html +++ b/src/views/check/results/no-errors.html @@ -4,7 +4,7 @@ {% from 'govuk/components/radios/macro.njk' import govukRadios %} {% from 'govuk/components/error-summary/macro.njk' import govukErrorSummary %} {% from "govuk/components/pagination/macro.njk" import govukPagination %} - +{% from '../../components/dataset-banner.html' import datasetBanner %} {% from "../../components/table.html" import table %} @@ -33,6 +33,11 @@ {% block content %} + + {% if options.deepLink %} + {{ datasetBanner(options.deepLink) }} + {% endif %} +

diff --git a/src/views/check/upload-method.html b/src/views/check/upload-method.html index d0b08f27..05817583 100644 --- a/src/views/check/upload-method.html +++ b/src/views/check/upload-method.html @@ -2,6 +2,7 @@ {% from 'govuk/components/radios/macro.njk' import govukRadios %} {% from 'govuk/components/error-message/macro.njk' import govukErrorMessage %} {% from 'govuk/components/error-summary/macro.njk' import govukErrorSummary %} +{% from 'components/dataset-banner.html' import datasetBanner %} {% extends "layouts/main.html" %} @@ -23,6 +24,12 @@ {% endblock %} {% block content %} + + {% if options.deepLink %} + {{ datasetBanner(options.deepLink) }} + {% endif %} + +
{% if error %} diff --git a/src/views/check/upload.html b/src/views/check/upload.html index 67630bf4..b9f43cbd 100644 --- a/src/views/check/upload.html +++ b/src/views/check/upload.html @@ -4,6 +4,7 @@ {% from "govuk/components/button/macro.njk" import govukButton %} {% from 'govuk/components/error-message/macro.njk' import govukErrorMessage %} {% from 'govuk/components/error-summary/macro.njk' import govukErrorSummary %} +{% from 'components/dataset-banner.html' import datasetBanner %} {% set serviceType = 'Check' %} {% set pageName = 'Upload data' %} @@ -30,6 +31,12 @@ {% endblock %} {% block content %} + + {% if options.deepLink %} + {{ datasetBanner(options.deepLink) }} + {% endif %} + +
{% if error %} diff --git a/src/views/check/url.html b/src/views/check/url.html index 6f703517..203f56ea 100644 --- a/src/views/check/url.html +++ b/src/views/check/url.html @@ -4,6 +4,7 @@ {% from 'govuk/components/input/macro.njk' import govukInput %} {% from 'govuk/components/error-message/macro.njk' import govukErrorMessage %} {% from 'govuk/components/error-summary/macro.njk' import govukErrorSummary %} +{% from 'components/dataset-banner.html' import datasetBanner %} {% set serviceType = 'Check' %} {% set pageName = 'URL' %} @@ -32,6 +33,11 @@ {% endblock %} {% block content %} + + {% if options.deepLink %} + {{ datasetBanner(options.deepLink) }} + {% endif %} +
{% if error %} diff --git a/src/views/components/dataset-banner.html b/src/views/components/dataset-banner.html new file mode 100644 index 00000000..db3bd92c --- /dev/null +++ b/src/views/components/dataset-banner.html @@ -0,0 +1,8 @@ +{% macro datasetBanner(params) %} +
+
+ {{ params.orgName }} +

{{ params.datasetName }}

+
+
+{% endmacro %} \ No newline at end of file diff --git a/src/views/organisations/datasetTaskList.html b/src/views/organisations/datasetTaskList.html index b61dbc1e..83cba216 100644 --- a/src/views/organisations/datasetTaskList.html +++ b/src/views/organisations/datasetTaskList.html @@ -77,7 +77,7 @@

  1. Fix the errors indicated
  2. -
  3. Use the check service to make sure the data meets +
  4. Use the check service to make sure the data meets the standard
  5. Publish the updated data on the data URL
diff --git a/src/views/organisations/get-started.html b/src/views/organisations/get-started.html index 74ff3bf0..ddb762a5 100644 --- a/src/views/organisations/get-started.html +++ b/src/views/organisations/get-started.html @@ -100,7 +100,7 @@

  1. - The check service can help you understand + The check service can help you understand if your data is ready to submit or if you need to change anything before you publish it on your website.

    @@ -120,7 +120,7 @@

    1. Check your data + href="{{ organisation | checkToolDeepLink(dataset) }}">Check your data

  2. diff --git a/src/views/organisations/issueDetails.html b/src/views/organisations/issueDetails.html index 72379f28..6e974815 100644 --- a/src/views/organisations/issueDetails.html +++ b/src/views/organisations/issueDetails.html @@ -4,6 +4,7 @@ {% from "govuk/components/summary-list/macro.njk" import govukSummaryList %} {% from "govuk/components/pagination/macro.njk" import govukPagination %} {% from "govuk/components/breadcrumbs/macro.njk" import govukBreadcrumbs %} +{% from 'components/dataset-banner.html' import datasetBanner %} {% set serviceType = 'Submit'%} @@ -45,6 +46,9 @@ {% block content %} +{% if options.deepLink %} + {{ datasetBanner(options.deepLink) }} +{% endif %}
    {% include "includes/_dataset-page-header.html" %} diff --git a/test/unit/chooseDatasetController.test.js b/test/unit/chooseDatasetController.test.js index 1fd2c8d4..995e4893 100644 --- a/test/unit/chooseDatasetController.test.js +++ b/test/unit/chooseDatasetController.test.js @@ -10,13 +10,15 @@ describe('ChooseDatasetController', () => { route: '/dataset' }) - vi.mock('../../src/utils/utils.js', () => { + vi.mock(import('../../src/utils/utils.js'), async (importOriginal) => { + const { availableDatasets } = await importOriginal() return { dataSubjects: { subject1: { available: true, dataSets: [{ available: true, text: 'B', value: 'B', requiresGeometryTypeSelection: true }, { available: false, text: 'A', value: 'A', requiresGeometryTypeSelection: false }] }, subject2: { available: false, dataSets: [{ available: true, text: 'C', value: 'C', requiresGeometryTypeSelection: false }] }, subject3: { available: true, dataSets: [{ available: true, text: 'A', value: 'A', requiresGeometryTypeSelection: true }] } - } + }, + availableDatasets } }) }) diff --git a/test/unit/datasetController.test.js b/test/unit/datasetController.test.js index 889afff7..faf62d6f 100644 --- a/test/unit/datasetController.test.js +++ b/test/unit/datasetController.test.js @@ -1,5 +1,4 @@ -import DatasetController from '../../src/controllers/datasetController.js' - +import DatasetController, { requiresGeometryTypeToBeSelected } from '../../src/controllers/datasetController.js' import { describe, it, vi, expect, beforeEach } from 'vitest' describe('DatasetController', () => { @@ -10,13 +9,22 @@ describe('DatasetController', () => { route: '/dataset' }) - vi.mock('../../src/utils/utils.js', () => { + vi.mock(import('../../src/utils/utils.js'), async (importOriginal) => { + const { availableDatasets, makeDatasetsLookup } = await importOriginal() + const dataSubjects = { + subject1: { + available: true, + dataSets: + [{ available: true, text: 'B', value: 'B', requiresGeometryTypeSelection: true }, + { available: false, text: 'D', value: 'D', requiresGeometryTypeSelection: false }] + }, + subject2: { available: false, dataSets: [{ available: true, text: 'C', value: 'C', requiresGeometryTypeSelection: false }] }, + subject3: { available: true, dataSets: [{ available: true, text: 'A', value: 'A', requiresGeometryTypeSelection: true }] } + } return { - dataSubjects: { - subject1: { available: true, dataSets: [{ available: true, text: 'B', value: 'B', requiresGeometryTypeSelection: true }, { available: false, text: 'A', value: 'A', requiresGeometryTypeSelection: false }] }, - subject2: { available: false, dataSets: [{ available: true, text: 'C', value: 'C', requiresGeometryTypeSelection: false }] }, - subject3: { available: true, dataSets: [{ available: true, text: 'A', value: 'A', requiresGeometryTypeSelection: true }] } - } + availableDatasets, + dataSubjects, + datasets: makeDatasetsLookup(dataSubjects) } }) }) @@ -58,14 +66,14 @@ describe('DatasetController', () => { it('Correctly determines whether a geometry type selection is required', () => { // Mock req with dataset that requires geometry type selection const req1 = { body: { dataset: 'B' } } - expect(datasetController.requiresGeometryTypeToBeSelected(req1)).toEqual(true) + expect(requiresGeometryTypeToBeSelected(req1)).toEqual(true) // Mock req with dataset that does not require geometry type selection - const req2 = { body: { dataset: 'A' } } - expect(datasetController.requiresGeometryTypeToBeSelected(req2)).toEqual(false) + const req2 = { body: { dataset: 'D' } } + expect(requiresGeometryTypeToBeSelected(req2)).toEqual(false) // Mock req with no dataset const req3 = { body: {} } - expect(datasetController.requiresGeometryTypeToBeSelected(req3)).toEqual(false) + expect(requiresGeometryTypeToBeSelected(req3)).toEqual(false) }) }) diff --git a/test/unit/deepLinkController.test.js b/test/unit/deepLinkController.test.js new file mode 100644 index 00000000..5fafe574 --- /dev/null +++ b/test/unit/deepLinkController.test.js @@ -0,0 +1,53 @@ +import { describe, it, vi, expect, beforeEach } from 'vitest' +import DeepLinkController from '../../src/controllers/deepLinkController.js' + +function mockRequestObject () { + const sessionModel = new Map() + const journeyModel = new Map() + return { sessionModel, journeyModel, query: {} } +} + +function mockMiddlewareArgs (reqOpts) { + return { + req: { ...mockRequestObject(), ...reqOpts }, + res: { redirect: vi.fn() }, + next: vi.fn() + } +} + +describe('DeepLinkController', () => { + let deepLinkController + + beforeEach(() => { + deepLinkController = new DeepLinkController({ + route: '/deep-link' + }) + }) + + describe('get()', () => { + it('should redirect to check tool start page when params invalid', async () => { + const { req, res, next } = mockMiddlewareArgs({ query: {} }) + deepLinkController.get(req, res, next) + + expect(res.redirect).toHaveBeenCalledWith('/check') + expect(Array.from(req.sessionModel.keys())).toStrictEqual([]) + expect(next).toBeCalledTimes(0) + }) + + it('should update session with deep link info', async () => { + const query = { dataset: 'conservation-area', orgName: 'Some Org' } + const { req, res, next } = mockMiddlewareArgs({ query }) + + deepLinkController.get(req, res, next) + + expect(req.sessionModel.get(deepLinkController.checkToolDeepLinkSessionKey)).toStrictEqual({ + 'data-subject': 'conservation-area', + orgName: 'Some Org', + dataset: 'conservation-area', + datasetName: 'Conservation area dataset' + }) + expect(req.journeyModel.get('history').length).toBe(1) + expect(next).toBeCalledTimes(1) + }) + }) +}) diff --git a/test/unit/util.test.js b/test/unit/util.test.js new file mode 100644 index 00000000..3436295d --- /dev/null +++ b/test/unit/util.test.js @@ -0,0 +1,33 @@ +import * as util from '../../src/utils/utils.js' +import { describe, it, expect } from 'vitest' + +const dataSubjects = { + subject1: { + available: true, + dataSets: + [{ available: true, text: 'B', value: 'B', requiresGeometryTypeSelection: true }, + { available: false, text: 'D', value: 'D' }] + }, + subject2: { available: false, dataSets: [{ available: true, text: 'C', value: 'C', requiresGeometryTypeSelection: false }] }, + subject3: { available: true, dataSets: [{ available: true, text: 'A', value: 'A', requiresGeometryTypeSelection: true }] } +} + +describe('utils/utils', () => { + it('makeDatasetsLookup()', () => { + const lookup = util.makeDatasetsLookup(dataSubjects) + + expect(lookup.get('A')).toEqual({ ...dataSubjects.subject3.dataSets[0], dataSubject: 'subject3' }) + expect(lookup.get('B')).toEqual({ ...dataSubjects.subject1.dataSets[0], dataSubject: 'subject1' }) + expect(lookup.get('C')).toEqual({ ...dataSubjects.subject2.dataSets[0], dataSubject: 'subject2' }) + expect(lookup.get('D')).toEqual({ ...dataSubjects.subject1.dataSets[1], dataSubject: 'subject1' }) + + const allDatasets = Object.entries(dataSubjects).map(([_, sub]) => sub.dataSets.map(ds => ds.value)).flat() + const uniqueDatasets = new Set(allDatasets) + expect(lookup.length).toBe(uniqueDatasets.length) + }) + + it('availableDatasets()', () => { + const datasets = util.availableDatasets(dataSubjects) + expect(new Set(datasets.map(ds => ds.value))).toEqual(new Set(['A', 'B'])) + }) +}) From 4aca2848944acb58840552138264540865999c99 Mon Sep 17 00:00:00 2001 From: Roland Sadowski Date: Mon, 14 Oct 2024 11:50:48 +0100 Subject: [PATCH 05/13] geometry type selection predicate for deep link route --- src/controllers/datasetController.js | 12 +++++++++++- src/routes/form-wizard/check/steps.js | 7 +++++-- test/unit/datasetController.test.js | 19 ++++++++++++++++++- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/controllers/datasetController.js b/src/controllers/datasetController.js index 88ab1bf0..8d5b1b6d 100644 --- a/src/controllers/datasetController.js +++ b/src/controllers/datasetController.js @@ -7,7 +7,7 @@ import PageController from './pageController.js' import { dataSubjects, datasets, availableDatasets } from '../utils/utils.js' /** - * @param req + * @param {Object} req * @returns {boolean} */ export function requiresGeometryTypeToBeSelected (req) { @@ -16,6 +16,16 @@ export function requiresGeometryTypeToBeSelected (req) { return dataSet?.requiresGeometryTypeSelection || false } +/** + * @param {Object} req - The HTTP request object. + * @returns {boolean} + */ +export function requiresGeometryTypeToBeSelectedViaDeepLink (req) { + const { dataset } = req.query + const dataSet = datasets.get(dataset) + return dataSet?.requiresGeometryTypeSelection || false +} + class DatasetController extends PageController { locals (req, res, next) { req.form.options.datasetItems = availableDatasets(dataSubjects) diff --git a/src/routes/form-wizard/check/steps.js b/src/routes/form-wizard/check/steps.js index c7eca24f..125c070b 100644 --- a/src/routes/form-wizard/check/steps.js +++ b/src/routes/form-wizard/check/steps.js @@ -1,6 +1,9 @@ // ToDo: Split this into two form wizards import PageController from '../../../controllers/pageController.js' -import datasetController, { requiresGeometryTypeToBeSelected } from '../../../controllers/datasetController.js' +import datasetController, { + requiresGeometryTypeToBeSelected, + requiresGeometryTypeToBeSelectedViaDeepLink +} from '../../../controllers/datasetController.js' import uploadFileController from '../../../controllers/uploadFileController.js' import submitUrlController from '../../../controllers/submitUrlController.js' import statusController from '../../../controllers/statusController.js' @@ -100,7 +103,7 @@ export default { ...baseSettings, controller: deepLinkController, next: [ - { field: 'dataset', fn: requiresGeometryTypeToBeSelected, next: 'geometry-type' }, + { field: 'dataset', fn: requiresGeometryTypeToBeSelectedViaDeepLink, next: 'geometry-type' }, 'upload-method' ], entryPoint: true, diff --git a/test/unit/datasetController.test.js b/test/unit/datasetController.test.js index faf62d6f..21608ca1 100644 --- a/test/unit/datasetController.test.js +++ b/test/unit/datasetController.test.js @@ -1,4 +1,7 @@ -import DatasetController, { requiresGeometryTypeToBeSelected } from '../../src/controllers/datasetController.js' +import DatasetController, { + requiresGeometryTypeToBeSelected, + requiresGeometryTypeToBeSelectedViaDeepLink +} from '../../src/controllers/datasetController.js' import { describe, it, vi, expect, beforeEach } from 'vitest' describe('DatasetController', () => { @@ -76,4 +79,18 @@ describe('DatasetController', () => { const req3 = { body: {} } expect(requiresGeometryTypeToBeSelected(req3)).toEqual(false) }) + + it('Correctly determines whether a geometry type selection is required via deep link', () => { + // Mock req with dataset that requires geometry type selection + const req1 = { query: { dataset: 'B' } } + expect(requiresGeometryTypeToBeSelectedViaDeepLink(req1)).toEqual(true) + + // Mock req with dataset that does not require geometry type selection + const req2 = { query: { dataset: 'D' } } + expect(requiresGeometryTypeToBeSelectedViaDeepLink(req2)).toEqual(false) + + // Mock req with no dataset + const req3 = { query: {} } + expect(requiresGeometryTypeToBeSelectedViaDeepLink(req3)).toEqual(false) + }) }) From 46b8de25eeae4b24378bb8b599c75a8d2839b732 Mon Sep 17 00:00:00 2001 From: Roland Sadowski Date: Mon, 14 Oct 2024 14:39:42 +0100 Subject: [PATCH 06/13] WIP: show error message in hidden HTML elem --- src/views/errorPages/500.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/views/errorPages/500.html b/src/views/errorPages/500.html index 2182ac7f..33e3c2ef 100644 --- a/src/views/errorPages/500.html +++ b/src/views/errorPages/500.html @@ -10,6 +10,9 @@

    {{pageName}}

    You'll need to start from the beginning of the service.

    +
    + {{ err.message }} +

{% endblock %} From d6f4c69e5cf266f0e4d871f2c58df50a451d54ca Mon Sep 17 00:00:00 2001 From: Roland Sadowski Date: Mon, 14 Oct 2024 14:57:51 +0100 Subject: [PATCH 07/13] Revert "WIP: show error message in hidden HTML elem" This reverts commit 24df8bdb53275801e06a36632a77f628c8e56edf. --- src/views/errorPages/500.html | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/views/errorPages/500.html b/src/views/errorPages/500.html index 33e3c2ef..2182ac7f 100644 --- a/src/views/errorPages/500.html +++ b/src/views/errorPages/500.html @@ -10,9 +10,6 @@

{{pageName}}

You'll need to start from the beginning of the service.

-
- {{ err.message }} -
{% endblock %} From f18d4fe7ac402e6b45579961aead8e04bc8bfb17 Mon Sep 17 00:00:00 2001 From: Roland Sadowski Date: Mon, 14 Oct 2024 14:58:45 +0100 Subject: [PATCH 08/13] add missing await --- src/controllers/uploadFileController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/uploadFileController.js b/src/controllers/uploadFileController.js index 674cbc04..53ba3aa7 100644 --- a/src/controllers/uploadFileController.js +++ b/src/controllers/uploadFileController.js @@ -64,7 +64,7 @@ class UploadFileController extends UploadController { logger.info('UploadFileController: file submitted for processing:', { type: 'fileUploaded', name: req.file.originalname, mimetype: req.file.mimetype, size: req.file.size }) - super.post(req, res, next) + await super.post(req, res, next) } catch (error) { next(error) } From 9cdf7e4504237c2077fb763986084e8c1b23c8de Mon Sep 17 00:00:00 2001 From: Roland Sadowski Date: Mon, 14 Oct 2024 16:27:03 +0100 Subject: [PATCH 09/13] update datasetBanner to be in line with latest design - remove the org name from the banner - move the banner closer to the page title --- src/views/check/geometry-type.html | 8 ++++---- src/views/check/results/errors.html | 10 +++++++--- src/views/check/results/no-errors.html | 7 +++---- src/views/check/statusPage/status.html | 4 ++++ src/views/check/upload-method.html | 9 ++++----- src/views/check/upload.html | 9 ++++----- src/views/check/url.html | 9 ++++----- src/views/components/dataset-banner.html | 7 +------ src/views/organisations/issueDetails.html | 5 ----- 9 files changed, 31 insertions(+), 37 deletions(-) diff --git a/src/views/check/geometry-type.html b/src/views/check/geometry-type.html index 20aad265..acdc8696 100644 --- a/src/views/check/geometry-type.html +++ b/src/views/check/geometry-type.html @@ -25,10 +25,6 @@ {% block content %} - {% if options.deepLink %} - {{ datasetBanner(options.deepLink) }} - {% endif %} -
{% if error %} @@ -44,6 +40,10 @@ {% endif %}
+ {% if options.deepLink %} + {{ datasetBanner(options.deepLink) }} + {% endif %} + {{ govukRadios({ name: "geomType", fieldset: { diff --git a/src/views/check/results/errors.html b/src/views/check/results/errors.html index 0c0306f7..40b6c40c 100644 --- a/src/views/check/results/errors.html +++ b/src/views/check/results/errors.html @@ -23,9 +23,13 @@
- - {{options.requestParams.dataset}} - + + {% if options.deepLink %} + {{ datasetBanner(options.deepLink) }} + {% else %} + {{options.requestParams.dataset}} + {% endif %} +

{{pageName}}

diff --git a/src/views/check/results/no-errors.html b/src/views/check/results/no-errors.html index 6d03bafc..a27665d2 100644 --- a/src/views/check/results/no-errors.html +++ b/src/views/check/results/no-errors.html @@ -34,12 +34,11 @@ {% block content %} - {% if options.deepLink %} - {{ datasetBanner(options.deepLink) }} - {% endif %} -
+ {% if options.deepLink %} + {{ datasetBanner(options.deepLink) }} + {% endif %}

{{pageName}}

diff --git a/src/views/check/statusPage/status.html b/src/views/check/statusPage/status.html index 0ac2b69b..b655b8c7 100644 --- a/src/views/check/statusPage/status.html +++ b/src/views/check/statusPage/status.html @@ -1,6 +1,7 @@ {% from "govuk/components/button/macro.njk" import govukButton %} {% from "./checkingFileMacro.html" import checkingFileContent %} {% from "./fileCheckedMacro.html" import fileCheckedContent %} +{% from 'components/dataset-banner.html' import datasetBanner %} {% extends "layouts/main.html" %} @@ -20,6 +21,9 @@ {% block content %}
+ {% if options.deepLink %} + {{ datasetBanner(options.deepLink) }} + {% endif %} {{ pageContent }}
diff --git a/src/views/check/upload-method.html b/src/views/check/upload-method.html index 05817583..e00832b4 100644 --- a/src/views/check/upload-method.html +++ b/src/views/check/upload-method.html @@ -25,11 +25,6 @@ {% block content %} - {% if options.deepLink %} - {{ datasetBanner(options.deepLink) }} - {% endif %} - -
{% if error %} @@ -45,6 +40,10 @@ {% endif %} + {% if options.deepLink %} + {{ datasetBanner(options.deepLink) }} + {% endif %} + {{ govukRadios({ name: "upload-method", fieldset: { diff --git a/src/views/check/upload.html b/src/views/check/upload.html index b9f43cbd..1d0a53d8 100644 --- a/src/views/check/upload.html +++ b/src/views/check/upload.html @@ -32,11 +32,6 @@ {% block content %} - {% if options.deepLink %} - {{ datasetBanner(options.deepLink) }} - {% endif %} - -
{% if error %} @@ -52,6 +47,10 @@ {% endif %} + {% if options.deepLink %} + {{ datasetBanner(options.deepLink) }} + {% endif %} + {{ govukFileUpload({ id: "datafile", name: "datafile", diff --git a/src/views/check/url.html b/src/views/check/url.html index 203f56ea..5953e1a1 100644 --- a/src/views/check/url.html +++ b/src/views/check/url.html @@ -34,11 +34,8 @@ {% block content %} - {% if options.deepLink %} - {{ datasetBanner(options.deepLink) }} - {% endif %} -
+
{% if error %} {{ govukErrorSummary({ @@ -52,7 +49,9 @@ }) }} {% endif %} - + {% if options.deepLink %} + {{ datasetBanner(options.deepLink) }} + {% endif %} {{ govukInput({ id: "url", name: "url", diff --git a/src/views/components/dataset-banner.html b/src/views/components/dataset-banner.html index db3bd92c..b402c483 100644 --- a/src/views/components/dataset-banner.html +++ b/src/views/components/dataset-banner.html @@ -1,8 +1,3 @@ {% macro datasetBanner(params) %} -
-
- {{ params.orgName }} -

{{ params.datasetName }}

-
-
+{{ params.datasetName }} {% endmacro %} \ No newline at end of file diff --git a/src/views/organisations/issueDetails.html b/src/views/organisations/issueDetails.html index 6e974815..988acd3f 100644 --- a/src/views/organisations/issueDetails.html +++ b/src/views/organisations/issueDetails.html @@ -4,7 +4,6 @@ {% from "govuk/components/summary-list/macro.njk" import govukSummaryList %} {% from "govuk/components/pagination/macro.njk" import govukPagination %} {% from "govuk/components/breadcrumbs/macro.njk" import govukBreadcrumbs %} -{% from 'components/dataset-banner.html' import datasetBanner %} {% set serviceType = 'Submit'%} @@ -46,10 +45,6 @@ {% block content %} -{% if options.deepLink %} - {{ datasetBanner(options.deepLink) }} -{% endif %} -
{% include "includes/_dataset-page-header.html" %}
From ee60f00f30470caa025badde8ffb1c26687a926d Mon Sep 17 00:00:00 2001 From: Roland Sadowski Date: Tue, 15 Oct 2024 12:22:49 +0100 Subject: [PATCH 10/13] update check service page title to 'submit' Closes #555 --- config/default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/default.yaml b/config/default.yaml index 05c6ca63..a216dcac 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -21,7 +21,7 @@ serviceName: 'Submit and update your planning data' # NOTE: the keys in this map are sometimes referred to as "serviceType" in the templates serviceNames: { submit: 'Submit and update your planning data', - check: 'Check planning and housing data for England', + check: 'Submit and update your planning data', manage: 'Submit and update your planning data' } checkService: From 96395fa4702a7bea7a90986e451e28481b3a053c Mon Sep 17 00:00:00 2001 From: Roland Sadowski Date: Tue, 15 Oct 2024 12:49:52 +0100 Subject: [PATCH 11/13] update check service title - tests --- test/unit/check-answers.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/check-answers.test.js b/test/unit/check-answers.test.js index 3bf450f4..6a19a89d 100644 --- a/test/unit/check-answers.test.js +++ b/test/unit/check-answers.test.js @@ -21,7 +21,7 @@ describe('check-answers View', async () => { const html = stripWhitespace(nunjucks.render('check-answers.html', params)) runGenericPageTests(html, { - pageTitle: 'Check your answers - Check planning and housing data for England' + pageTitle: 'Check your answers - Submit and update your planning data' }) it('should render the lpa selected', () => { From ff04a79717223d3cc86de77b183f7564724d910a Mon Sep 17 00:00:00 2001 From: Roland Sadowski Date: Tue, 15 Oct 2024 17:21:53 +0100 Subject: [PATCH 12/13] missing datasetBanner import in errors page --- src/views/check/results/errors.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/check/results/errors.html b/src/views/check/results/errors.html index 40b6c40c..9027c5cb 100644 --- a/src/views/check/results/errors.html +++ b/src/views/check/results/errors.html @@ -5,6 +5,7 @@ {% from 'govuk/components/inset-text/macro.njk' import govukInsetText %} {% from "govuk/components/pagination/macro.njk" import govukPagination %} {% from "../../components/table.html" import table %} +{% from '../../components/dataset-banner.html' import datasetBanner %} {% set serviceType = 'Check' %} {% set pageName = 'Your data has errors' %} From b097e7f54e34210082cee907f84a1a285cc1423f Mon Sep 17 00:00:00 2001 From: Roland Sadowski Date: Tue, 15 Oct 2024 17:43:32 +0100 Subject: [PATCH 13/13] fix path to template macro import --- src/views/check/statusPage/status.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/check/statusPage/status.html b/src/views/check/statusPage/status.html index b655b8c7..f4e66ec9 100644 --- a/src/views/check/statusPage/status.html +++ b/src/views/check/statusPage/status.html @@ -1,7 +1,7 @@ {% from "govuk/components/button/macro.njk" import govukButton %} {% from "./checkingFileMacro.html" import checkingFileContent %} {% from "./fileCheckedMacro.html" import fileCheckedContent %} -{% from 'components/dataset-banner.html' import datasetBanner %} +{% from '../../components/dataset-banner.html' import datasetBanner %} {% extends "layouts/main.html" %}