@planning-inspectorate/dynamic-forms 1.14.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/.prettierignore +2 -1
  2. package/README.md +9 -1
  3. package/package.json +7 -2
  4. package/src/components/address/question.js +1 -1
  5. package/src/components/address/question.test.js +3 -3
  6. package/src/components/multi-field-input/index.njk +2 -2
  7. package/src/components/multi-field-input/question.js +5 -1
  8. package/src/components/radio/index.njk +2 -2
  9. package/src/components/select/index.njk +2 -2
  10. package/src/components/text-entry-redact/question.test.js +9 -9
  11. package/src/controller.test.js +2 -1
  12. package/src/index.js +8 -2
  13. package/src/lib/session-answer-store.test.js +1 -1
  14. package/src/questions/question-props.d.ts +5 -24
  15. package/src/questions/question.test.js +1 -1
  16. package/src/section.js +3 -0
  17. package/src/validator/validator.js +4 -1
  18. package/test/check-your-answers.test.js +69 -0
  19. package/test/journey.js +41 -0
  20. package/test/journey.test.js +21 -0
  21. package/test/questions.js +189 -0
  22. package/test/questions.test.js +246 -0
  23. package/test/snapshots/add-insurance.html +44 -0
  24. package/test/snapshots/check-your-answers-not-started.html +201 -0
  25. package/test/snapshots/check-your-answers-with-answers.html +201 -0
  26. package/test/snapshots/companions.html +51 -0
  27. package/test/snapshots/departure-date.html +57 -0
  28. package/test/snapshots/holiday-activities.html +49 -0
  29. package/test/snapshots/holiday-arrival.html +101 -0
  30. package/test/snapshots/holiday-description.html +33 -0
  31. package/test/snapshots/holiday-destination.html +56 -0
  32. package/test/snapshots/holiday-period.html +102 -0
  33. package/test/snapshots/holiday-snack.html +33 -0
  34. package/test/snapshots/hotel-address.html +65 -0
  35. package/test/snapshots/luggage-weight-units.html +75 -0
  36. package/test/snapshots/nights.html +28 -0
  37. package/test/snapshots/secret-wish.html +173 -0
  38. package/test/snapshots/travel-class.html +52 -0
  39. package/test/snapshots/travel-insurance-type.html +56 -0
  40. package/test/utils/app.js +41 -0
  41. package/test/utils/nunjucks.js +20 -0
  42. package/test/utils/test-server.js +167 -0
  43. package/test/utils/test-server.test.js +75 -0
  44. package/{src/lib/test-utils.js → test/utils/utils.js} +15 -16
  45. package/test/views/layout-check-your-answers.njk +1 -0
  46. /package/{src/lib/test-layout.njk → test/views/layout-question.njk} +0 -0
@@ -0,0 +1,33 @@
1
+ <div class="govuk-grid-row">
2
+ <div class="govuk-grid-column-two-thirds">
3
+ </div>
4
+ </div>
5
+
6
+
7
+ <div class="govuk-grid-row">
8
+ <div class="govuk-grid-column-two-thirds">
9
+ <form action="" method="post" novalidate>
10
+ <input type="hidden" name="_csrf" value="">
11
+ <h1 class="govuk-heading-l">
12
+ What is your favourite snack to eat on holiday?
13
+ </h1>
14
+
15
+
16
+
17
+ <div class="govuk-form-group">
18
+ <label class="govuk-label govuk-label" for="holidaySnack">
19
+ Snack
20
+ </label>
21
+ <div id="holidaySnack-hint" class="govuk-hint">
22
+
23
+ </div>
24
+ <input class="govuk-input" id="holidaySnack" name="holidaySnack" type="text" value="" aria-describedby="holidaySnack-hint">
25
+ </div>
26
+
27
+
28
+ <button type="submit" class="govuk-button" data-module="govuk-button" data-cy="button-save-and-continue">
29
+ Continue
30
+ </button>
31
+ </form>
32
+ </div>
33
+ </div>
@@ -0,0 +1,65 @@
1
+ <div class="govuk-grid-row">
2
+ <div class="govuk-grid-column-two-thirds">
3
+ </div>
4
+ </div>
5
+
6
+
7
+ <div class="govuk-grid-row">
8
+ <div class="govuk-grid-column-two-thirds">
9
+ <form action="" method="post" novalidate>
10
+ <input type="hidden" name="_csrf" value="">
11
+ <fieldset class="govuk-fieldset">
12
+ <legend class="govuk-fieldset__legend govuk-fieldset__legend--l">
13
+ <h1 class="govuk-fieldset__heading">
14
+ What is the address of your hotel?
15
+ </h1>
16
+ </legend>
17
+
18
+
19
+ <div class="govuk-form-group">
20
+ <label class="govuk-label" for="address-line-1">
21
+ Address line 1 (optional)
22
+ </label>
23
+ <input class="govuk-input" id="address-line-1" name="hotelAddress_addressLine1" type="text" value="" autocomplete="address-line1">
24
+ </div>
25
+
26
+
27
+ <div class="govuk-form-group">
28
+ <label class="govuk-label" for="address-line-2">
29
+ Address line 2 (optional)
30
+ </label>
31
+ <input class="govuk-input" id="address-line-2" name="hotelAddress_addressLine2" type="text" value="" autocomplete="address-line2">
32
+ </div>
33
+
34
+
35
+ <div class="govuk-form-group">
36
+ <label class="govuk-label" for="address-town">
37
+ Town or city (optional)
38
+ </label>
39
+ <input class="govuk-input govuk-!-width-two-thirds" id="address-town" name="hotelAddress_townCity" type="text" value="" autocomplete="address-level2">
40
+ </div>
41
+
42
+
43
+ <div class="govuk-form-group">
44
+ <label class="govuk-label" for="address-county">
45
+ County (optional)
46
+ </label>
47
+ <input class="govuk-input govuk-!-width-two-thirds" id="address-county" name="hotelAddress_county" type="text" value="" autocomplete="address-level1">
48
+ </div>
49
+
50
+
51
+ <div class="govuk-form-group">
52
+ <label class="govuk-label" for="address-postcode">
53
+ Postcode (optional)
54
+ </label>
55
+ <input class="govuk-input govuk-input--width-10" id="address-postcode" name="hotelAddress_postcode" type="text" value="" autocomplete="postal-code">
56
+ </div>
57
+
58
+ <button type="submit" class="govuk-button" data-module="govuk-button" data-cy="button-save-and-continue">
59
+ Continue
60
+ </button>
61
+
62
+ </fieldset>
63
+ </form>
64
+ </div>
65
+ </div>
@@ -0,0 +1,75 @@
1
+ <div class="govuk-grid-row">
2
+ <div class="govuk-grid-column-two-thirds">
3
+ </div>
4
+ </div>
5
+
6
+
7
+ <div class="govuk-grid-row">
8
+ <div class="govuk-grid-column-two-thirds">
9
+ <form action="" method="post" novalidate>
10
+ <input type="hidden" name="_csrf" value="">
11
+
12
+ <fieldset class="govuk-fieldset">
13
+ <legend class="govuk-fieldset__legend govuk-fieldset__legend--l">
14
+ <h1 class="govuk-fieldset__heading">
15
+ Select your luggage weight and unit.
16
+ </h1>
17
+ </legend>
18
+
19
+ <p class="govuk-body"></p>
20
+
21
+ <div class="govuk-form-group">
22
+ <div class="govuk-radios govuk-radios" data-module="govuk-radios">
23
+ <div class="govuk-radios__item">
24
+ <input class="govuk-radios__input" id="luggageWeightUnits" name="luggageWeightUnits" type="radio" value="kg" data-aria-controls="conditional-luggageWeightUnits" data-cy="answer-kg">
25
+ <label class="govuk-label govuk-radios__label" for="luggageWeightUnits">
26
+ Kilograms
27
+ </label>
28
+ </div>
29
+ <div class="govuk-radios__conditional govuk-radios__conditional--hidden" id="conditional-luggageWeightUnits">
30
+ <div class="govuk-form-group">
31
+ <label class="govuk-label" for="luggageWeightValue_kg">
32
+ Weight in kilograms
33
+ </label>
34
+ <div id="luggageWeightValue_kg-hint" class="govuk-hint">
35
+
36
+ </div>
37
+ <div class="govuk-input__wrapper">
38
+ <input class="govuk-input govuk-input--width-5" id="luggageWeightValue_kg" name="luggageWeightValue_kg" type="text" spellcheck="false" value="" aria-describedby="luggageWeightValue_kg-hint" inputmode="numeric">
39
+ <div class="govuk-input__suffix" aria-hidden="true">kg</div>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ <div class="govuk-radios__item">
44
+ <input class="govuk-radios__input" id="luggageWeightUnits-2" name="luggageWeightUnits" type="radio" value="lbs" data-aria-controls="conditional-luggageWeightUnits-2" data-cy="answer-lbs">
45
+ <label class="govuk-label govuk-radios__label" for="luggageWeightUnits-2">
46
+ Pounds
47
+ </label>
48
+ </div>
49
+ <div class="govuk-radios__conditional govuk-radios__conditional--hidden" id="conditional-luggageWeightUnits-2">
50
+ <div class="govuk-form-group">
51
+ <label class="govuk-label" for="luggageWeightValue_lbs">
52
+ Weight in pounds
53
+ </label>
54
+ <div id="luggageWeightValue_lbs-hint" class="govuk-hint">
55
+
56
+ </div>
57
+ <div class="govuk-input__wrapper">
58
+ <input class="govuk-input govuk-input--width-5" id="luggageWeightValue_lbs" name="luggageWeightValue_lbs" type="text" spellcheck="false" value="" aria-describedby="luggageWeightValue_lbs-hint" inputmode="numeric">
59
+ <div class="govuk-input__suffix" aria-hidden="true">lbs</div>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </div>
65
+
66
+
67
+
68
+ </fieldset>
69
+
70
+ <button type="submit" class="govuk-button" data-module="govuk-button" data-cy="button-save-and-continue">
71
+ Continue
72
+ </button>
73
+ </form>
74
+ </div>
75
+ </div>
@@ -0,0 +1,28 @@
1
+ <div class="govuk-grid-row">
2
+ <div class="govuk-grid-column-two-thirds">
3
+ </div>
4
+ </div>
5
+
6
+
7
+ <div class="govuk-grid-row">
8
+ <div class="govuk-grid-column-two-thirds">
9
+ <h1 class="govuk-heading-l">How many nights will you stay on holiday?</h1>
10
+ <p class="govuk-body"></p>
11
+ <form action="" method="post" novalidate>
12
+ <input type="hidden" name="_csrf" value="">
13
+ <div class="govuk-form-group">
14
+ <label class="govuk-label" for="nights">
15
+ Nights
16
+ </label>
17
+ <div id="nights-hint" class="govuk-hint">
18
+
19
+ </div>
20
+ <input class="govuk-input govuk-input--width-5" id="nights" name="nights" type="text" spellcheck="false" value="" aria-describedby="nights-hint" inputmode="numeric">
21
+ </div>
22
+
23
+ <button type="submit" class="govuk-button" data-module="govuk-button" data-cy="button-save-and-continue">
24
+ Continue
25
+ </button>
26
+ </form>
27
+ </div>
28
+ </div>
@@ -0,0 +1,173 @@
1
+ <div class="govuk-grid-row">
2
+ <div class="govuk-grid-column-two-thirds">
3
+ </div>
4
+ </div>
5
+
6
+
7
+ <div class="govuk-grid-row">
8
+ <div class="govuk-grid-column-two-thirds">
9
+ <form action="" method="post" novalidate>
10
+ <input type="hidden" name="_csrf" value="">
11
+
12
+ <h1 class="govuk-label-wrapper">
13
+ <label class="govuk-label govuk-label--l" for="secretWish">
14
+ Share a secret wish for your holiday (will be redacted).
15
+ </label>
16
+ </h1>
17
+
18
+
19
+
20
+
21
+ <h2 class="govuk-heading-m">Redaction preview</h2>
22
+
23
+ <details id="secretWish-unredacted" class="govuk-details" data-original="">
24
+ <summary class="govuk-details__summary">
25
+ <span class="govuk-details__summary-text">
26
+ Original
27
+ </span>
28
+ </summary>
29
+ <div class="govuk-details__text">
30
+
31
+ </div>
32
+ </details>
33
+
34
+
35
+ <div class="govuk-form-group">
36
+ <label class="govuk-label" for="secretWish">
37
+ Secret wish
38
+ </label>
39
+ <textarea class="govuk-textarea" id="secretWish" name="secretWish" rows="8" readonly="true"></textarea>
40
+ </div>
41
+
42
+
43
+ <div class="govuk-button-group">
44
+ <button type="button" class="govuk-button govuk-button--secondary" data-module="govuk-button" id="redact-selected-text">
45
+ Redact selected text
46
+ </button>
47
+
48
+ <button type="button" class="govuk-button govuk-button--secondary" data-module="govuk-button" id="redact-undo">
49
+ Undo
50
+ </button>
51
+
52
+ <button type="button" class="govuk-button govuk-button--secondary" data-module="govuk-button" id="redact-undo-all">
53
+ Undo all changes
54
+ </button>
55
+ </div>
56
+
57
+ <button type="submit" class="govuk-button" data-module="govuk-button" data-cy="button-save-and-continue">
58
+ Continue
59
+ </button>
60
+ </form>
61
+ </div>
62
+
63
+ <script >
64
+ /**
65
+ * @param {HTMLTextAreaElement} textArea
66
+ * @param {HTMLButtonElement} redactSelectedText
67
+ * @param {HTMLButtonElement} redactUndo
68
+ * @param {HTMLButtonElement} redactUndoAll
69
+ * @param {HTMLDetailsElement} unredactedText
70
+ * @param {HTMLAnchorElement[]} rejectLinks
71
+ * @param {import('@azure/ai-text-analytics').PiiEntity[]} suggestions
72
+ */
73
+ function registerEventListeners({textArea, redactSelectedText, redactUndo, redactUndoAll, unredactedText, rejectLinks, suggestions}) {
74
+ const formGroup = textArea.parentElement;
75
+ const errorMessage = document.createElement('p');
76
+ errorMessage.id = 'secretWish-error';
77
+ errorMessage.classList.add('govuk-error-message');
78
+
79
+ const history = [];
80
+ function showError(message) {
81
+ formGroup.classList.add('govuk-form-group--error');
82
+ errorMessage.textContent = message;
83
+ formGroup.prepend(errorMessage);
84
+ }
85
+ function hideError() {
86
+ textArea.parentElement.classList.remove('govuk-form-group--error');
87
+ errorMessage.remove();
88
+ }
89
+ redactSelectedText.addEventListener('click', () => {
90
+ const { selectionStart, selectionEnd } = textArea;
91
+ if (selectionStart === selectionEnd) {
92
+ // no selected text
93
+ showError('Select text to redact');
94
+ return;
95
+ }
96
+ hideError();
97
+ // replace the text with the 'full block' █ character
98
+ const selectedText = textArea.value.substring(selectionStart, selectionEnd);
99
+ const redactedText = '█'.repeat(selectionEnd - selectionStart);
100
+ textArea.value = textArea.value.substring(0, selectionStart) + redactedText + textArea.value.substring(selectionEnd);
101
+ // add to history for undo functionality
102
+ history.push({
103
+ selectionStart,
104
+ selectionEnd,
105
+ selectedText
106
+ });
107
+ });
108
+ redactUndo.addEventListener('click', () => {
109
+ const { selectionStart, selectionEnd, selectedText } = history.pop() || {};
110
+ if (selectionStart !== undefined && selectionEnd !== undefined && selectedText) {
111
+ hideError();
112
+ textArea.value = textArea.value.substring(0, selectionStart) + selectedText + textArea.value.substring(selectionEnd);
113
+ } else {
114
+ showError('No changes to undo');
115
+ }
116
+ });
117
+ redactUndoAll.addEventListener('click', () => {
118
+ const unredated = unredactedText.dataset.original;
119
+ if (history.length === 0 && textArea.value === unredated) {
120
+ showError('No changes to undo');
121
+ return;
122
+ }
123
+ hideError();
124
+ textArea.value = unredated || '';
125
+ history.length = 0;
126
+ });
127
+ for (const rejectLink of rejectLinks) {
128
+ rejectLink.addEventListener('click', () => {
129
+ const index = parseInt(rejectLink.getAttribute('data-redact').replace('suggestion-reject-', ''));
130
+ const suggestion = suggestions[index];
131
+ if (rejectLink.text === 'Reject') {
132
+ textArea.value = textArea.value.substring(0, suggestion.offset) + suggestion.text + textArea.value.substring(suggestion.offset + suggestion.length);
133
+ rejectLink.text = 'Accept';
134
+ } else {
135
+ const redactedText = '█'.repeat(suggestion.length);
136
+ textArea.value = textArea.value.substring(0, suggestion.offset) + redactedText+ textArea.value.substring(suggestion.offset + suggestion.length);
137
+ rejectLink.text = 'Reject';
138
+ }
139
+ });
140
+ }
141
+ }
142
+
143
+ function initRedact() {
144
+ const textArea = document.querySelector('#secretWish');
145
+ const redactSelectedText = document.querySelector('#redact-selected-text');
146
+ const redactUndo = document.querySelector('#redact-undo');
147
+ const redactUndoAll = document.querySelector('#redact-undo-all');
148
+ const unredactedText = document.querySelector('#secretWish-unredacted');
149
+ const rejectLinks = document.querySelectorAll('[data-redact]');
150
+ const suggestions = []
151
+
152
+ if (!textArea || !(textArea instanceof HTMLTextAreaElement)) {
153
+ return;
154
+ }
155
+ if (!redactSelectedText || !(redactSelectedText instanceof HTMLButtonElement)) {
156
+ return;
157
+ }
158
+ if (!redactUndo || !(redactUndo instanceof HTMLButtonElement)) {
159
+ return;
160
+ }
161
+ if (!redactUndoAll || !(redactUndoAll instanceof HTMLButtonElement)) {
162
+ return;
163
+ }
164
+ if (!unredactedText || !(unredactedText instanceof HTMLDetailsElement)) {
165
+ return;
166
+ }
167
+
168
+ registerEventListeners({textArea, redactSelectedText, redactUndo, redactUndoAll, unredactedText, rejectLinks, suggestions});
169
+ }
170
+
171
+ initRedact();
172
+ </script>
173
+ </div>
@@ -0,0 +1,52 @@
1
+ <div class="govuk-grid-row">
2
+ <div class="govuk-grid-column-two-thirds">
3
+ </div>
4
+ </div>
5
+
6
+
7
+ <div class="govuk-grid-row">
8
+ <div class="govuk-grid-column-two-thirds">
9
+ <form action="" method="POST" novalidate>
10
+ <input type="hidden" name="_csrf" value="">
11
+
12
+
13
+ <div class="govuk-form-group">
14
+ <h1 class="govuk-label-wrapper">
15
+ <label class="govuk-label govuk-label--l" for="travelClass">
16
+ Which class do you want to travel in?
17
+ </label>
18
+ </h1>
19
+ <select class="govuk-select" id="travelClass" name="travelClass">
20
+ <option value="economy" data-cy="answer-economy">Economy</option>
21
+ <option value="business" data-cy="answer-business">Business</option>
22
+ <option value="first" data-cy="answer-first">First Class</option>
23
+ </select>
24
+ </div>
25
+
26
+
27
+ <button type="submit" class="govuk-button" data-module="govuk-button" data-cy="button-save-and-continue">
28
+ Continue
29
+ </button>
30
+
31
+
32
+ </form>
33
+ </div>
34
+ </div>
35
+ <script src="/assets/js/accessible-autocomplete.min.js"></script>
36
+ <script >
37
+ if (window.accessibleAutocomplete) {
38
+ accessibleAutocomplete.enhanceSelectElement({
39
+ selectElement: document.querySelector('#travelClass'),
40
+ onConfirm: () => {
41
+ let input = document.querySelector('#travelClass');
42
+ let select = document.querySelector('#travelClass-select');
43
+ let options = Array.from(select.options);
44
+ if (!input.value || !options.some(option => option.innerText === input.value)) {
45
+ select.options.selectedIndex = -1;
46
+ } else {
47
+ select.options.selectedIndex = options.findIndex(option => option.innerText === input.value);
48
+ }
49
+ }
50
+ })
51
+ }
52
+ </script>
@@ -0,0 +1,56 @@
1
+ <div class="govuk-grid-row">
2
+ <div class="govuk-grid-column-two-thirds">
3
+ </div>
4
+ </div>
5
+
6
+
7
+ <div class="govuk-grid-row">
8
+ <div class="govuk-grid-column-two-thirds">
9
+ <form action="" method="post" novalidate>
10
+ <input type="hidden" name="_csrf" value="">
11
+
12
+
13
+ <fieldset class="govuk-fieldset">
14
+ <legend class="govuk-fieldset__legend govuk-fieldset__legend--l">
15
+ <h1 class="govuk-fieldset__heading">
16
+ Which type of travel insurance would you like?
17
+ </h1>
18
+ </legend>
19
+
20
+ <p class="govuk-body"></p>
21
+
22
+
23
+ <div class="govuk-form-group">
24
+ <div class="govuk-radios govuk-radios" data-module="govuk-radios">
25
+ <div class="govuk-radios__item">
26
+ <input class="govuk-radios__input" id="travelInsuranceType" name="travelInsuranceType" type="radio" value="basic" data-cy="answer-basic">
27
+ <label class="govuk-label govuk-radios__label" for="travelInsuranceType">
28
+ Basic
29
+ </label>
30
+ </div>
31
+ <div class="govuk-radios__item">
32
+ <input class="govuk-radios__input" id="travelInsuranceType-2" name="travelInsuranceType" type="radio" value="comprehensive" data-cy="answer-comprehensive">
33
+ <label class="govuk-label govuk-radios__label" for="travelInsuranceType-2">
34
+ Comprehensive
35
+ </label>
36
+ </div>
37
+ <div class="govuk-radios__item">
38
+ <input class="govuk-radios__input" id="travelInsuranceType-3" name="travelInsuranceType" type="radio" value="premium" data-cy="answer-premium">
39
+ <label class="govuk-label govuk-radios__label" for="travelInsuranceType-3">
40
+ Premium
41
+ </label>
42
+ </div>
43
+ </div>
44
+ </div>
45
+
46
+
47
+
48
+ </fieldset>
49
+
50
+
51
+ <button type="submit" class="govuk-button" data-module="govuk-button" data-cy="button-save-and-continue">
52
+ Continue
53
+ </button>
54
+ </form>
55
+ </div>
56
+ </div>
@@ -0,0 +1,41 @@
1
+ import bodyParser from 'body-parser';
2
+ import express from 'express';
3
+ import session from 'express-session';
4
+ import crypto from 'node:crypto';
5
+ import { configureNunjucksTestEnv } from './nunjucks.js';
6
+
7
+ /**
8
+ * Create an Express app configured for testing with Nunjucks and session support.
9
+ * @returns {import('express').Express}
10
+ */
11
+ export function createApp() {
12
+ const app = express();
13
+
14
+ // configure body-parser, to populate req.body
15
+ // see https://expressjs.com/en/resources/middleware/body-parser.html
16
+ app.use(bodyParser.urlencoded({ extended: true }));
17
+ app.use(bodyParser.json());
18
+
19
+ app.use(
20
+ session({
21
+ secret: crypto.randomUUID(),
22
+ resave: false,
23
+ saveUninitialized: false,
24
+ store: new session.MemoryStore(),
25
+ unset: 'destroy',
26
+ cookie: {
27
+ secure: false,
28
+ maxAge: 86_400_000 // 1 day in milliseconds
29
+ }
30
+ })
31
+ );
32
+
33
+ const nunjucksEnvironment = configureNunjucksTestEnv();
34
+ // Set the express view engine to nunjucks
35
+ // calls to res.render will use nunjucks
36
+ nunjucksEnvironment.addGlobal('govukRebrand', true);
37
+ nunjucksEnvironment.express(app);
38
+ app.set('view engine', 'njk');
39
+
40
+ return app;
41
+ }
@@ -0,0 +1,20 @@
1
+ import nunjucks from 'nunjucks';
2
+ import { createRequire } from 'module';
3
+ import path from 'path';
4
+ import { testDir as getTestDir } from './utils.js';
5
+
6
+ export function configureNunjucksTestEnv() {
7
+ const require = createRequire(import.meta.url);
8
+ const testDir = getTestDir();
9
+ const srcDir = path.resolve(testDir, '..', 'src');
10
+ const govukFrontendRoot = path.resolve(require.resolve('govuk-frontend'), '../..');
11
+
12
+ return nunjucks.configure([testDir, srcDir, govukFrontendRoot], {
13
+ // output with dangerous characters are escaped automatically
14
+ autoescape: true,
15
+ // automatically remove trailing newlines from a block/tag
16
+ trimBlocks: true,
17
+ // automatically remove leading whitespace from a block/tag
18
+ lstripBlocks: true
19
+ });
20
+ }