diff --git a/.gitignore b/.gitignore index 526cd77f..6c2af0f1 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ dist/ coverage/ test/results.json playwright-report/ -test-results/ \ No newline at end of file +test-results/ +e2e/_results_/downloads/*.pdf \ No newline at end of file diff --git a/e2e/README.md b/e2e/README.md index 2d6d3a28..9479cce0 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -10,25 +10,26 @@ The suite uses a three-layer architecture. Tests read like plain English, driver ``` tests/*.spec.js ← Specifications (what to test) - ↓ fixtures (steps / mapSteps) -test-runner-api/ ← FormDriver & MapDriver (how to interact) + ↓ fixtures (steps / mapSteps / pdfDriver) +test-runner-api/ ← FormDriver, MapDriver & PdfDriver (how to interact) ↓ pages/ ← Page definitions & control factories ``` ### Fixtures (`fixtures.js`) -[Playwright fixtures](https://playwright.dev/docs/test-fixtures) provide two drivers to every test: +[Playwright fixtures](https://playwright.dev/docs/test-fixtures) make three drivers available to tests (most tests use `steps` and/or `mapSteps`; PDF tests use `pdfDriver`): | Fixture | Class | Purpose | |------------|-------------|---------| | `steps` | `FormDriver` | Standard GOV.UK form page interactions | | `mapSteps` | `MapDriver` | Map-specific interactions (extends `FormDriver`) | +| `pdfDriver` | `PdfDriver` | PDF download, parsing, and PDF content assertions | ```js import { test } from '../../fixtures.js' -test('example', async ({ steps, mapSteps }) => { +test('example', async ({ steps, mapSteps, pdfDriver }) => { await steps.open(pages.home.page) // ... }) @@ -72,6 +73,19 @@ All Playwright locator logic lives here. Tests never call `page.getByRole(...)` | `addSquare()` | Open the location menu and click "Add square" | | `confirmBoundaryAndContinue()` | Click "Finish" then "Get summary report" | +**PdfDriver** — handles PDF download capture, parsing, and PDF-specific assertions: + +| Method | Description | +|--------|-------------| +| `clearPdfFiles()` | Remove previously generated PDFs from `_results_/downloads` | +| `waitForDownload(triggerDownload, timeout)` | Wait for a PDF via browser download event or PDF response capture | +| `parsePdf(filePath)` | Parse PDF text and links using `pdf-parse` | +| `expectCoreContent(pdf, { reference, scale })` | Validate core report text, reference handling, and scale formatting | +| `expectFloodZone(pdf, floodZone)` | Validate zone text in PDF matches expected flood zone | +| `expectLocation(pdf, polygonString)` | Validate centroid easting/northing rendered in PDF | +| `expectRequiredLinks(pdf, expectedLinks)` | Validate required static links are embedded in PDF | +| `expectAllLinksAreValid(pdf)` | Validate extracted links are syntactically valid URLs | + ### Page Objects (`pages/`) Each page is defined with `definePage({ slug, title })` and exports typed control handles built from factory functions. @@ -142,7 +156,7 @@ await test.step('Home → Triage', async () => { ## Prerequisites -- Node.js 18+ (LTS recommended) +- Node.js 20.16+ (required by `pdf-parse`) - Browsers — install whichever you need to run: ```bash @@ -166,6 +180,15 @@ npm install npx playwright install # all browsers, or pick one as above ``` +## PDF Test Artifacts + +PDF tests generate files in `_results_/downloads/`. + +- The folder is cleared once at the start of the PDF suite. +- Files generated during that run are retained (for example, 6 PDF tests produce 6 PDFs). +- On the next run, the folder is cleared again before new PDF files are created. +- Generated PDFs are ignored by git (`e2e/_results_/downloads/*.pdf`), so they are not committed. + ## Docker (WIP) Tests can be run in Docker using the Playwright base image. This is a work in progress. @@ -289,7 +312,8 @@ e2e/ │ ├── test-runner-api/ │ ├── form-driver.js # FormDriver — GOV.UK form interactions -│ └── map-driver.js # MapDriver — Esri map interactions +│ ├── map-driver.js # MapDriver — Esri map interactions +│ └── pdf-driver.js # PdfDriver — PDF download/parse/assertions │ ├── pages/ │ ├── .utils/ diff --git a/e2e/fixtures.js b/e2e/fixtures.js index 32323b6c..f8070627 100644 --- a/e2e/fixtures.js +++ b/e2e/fixtures.js @@ -1,6 +1,7 @@ import { test as base } from '@playwright/test' import { FormDriver } from './test-runner-api/form-driver.js' import { MapDriver } from './test-runner-api/map-driver.js' +import { PdfDriver } from './test-runner-api/pdf-driver.js' export const test = base.extend({ steps: async ({ page }, run) => { @@ -9,6 +10,9 @@ export const test = base.extend({ mapSteps: async ({ page }, run) => { await run(new MapDriver(page)) }, + pdfDriver: async ({ page }, run) => { + await run(new PdfDriver(page)) + }, }) export { expect } from '@playwright/test' diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 131fc9ff..5e5963a7 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -10,7 +10,8 @@ "devDependencies": { "@mapbox/polyline": "^1.2.1", "@playwright/test": "^1.50.0", - "husky": "^9.1.7" + "husky": "^9.1.7", + "pdf-parse": "^2.4.5" } }, "node_modules/@babel/code-frame": { @@ -50,6 +51,201 @@ "polyline": "bin/polyline.bin.js" } }, + "node_modules/@napi-rs/canvas": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.80.tgz", + "integrity": "sha512-DxuT1ClnIPts1kQx8FBmkk4BQDTfI5kIzywAaMjQSXfNnra5UFU9PwurXrl+Je3bJ6BGsp/zmshVVFbCmyI+ww==", + "dev": true, + "license": "MIT", + "workspaces": [ + "e2e/*" + ], + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@napi-rs/canvas-android-arm64": "0.1.80", + "@napi-rs/canvas-darwin-arm64": "0.1.80", + "@napi-rs/canvas-darwin-x64": "0.1.80", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.80", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.80", + "@napi-rs/canvas-linux-arm64-musl": "0.1.80", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.80", + "@napi-rs/canvas-linux-x64-gnu": "0.1.80", + "@napi-rs/canvas-linux-x64-musl": "0.1.80", + "@napi-rs/canvas-win32-x64-msvc": "0.1.80" + } + }, + "node_modules/@napi-rs/canvas-android-arm64": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.80.tgz", + "integrity": "sha512-sk7xhN/MoXeuExlggf91pNziBxLPVUqF2CAVnB57KLG/pz7+U5TKG8eXdc3pm0d7Od0WreB6ZKLj37sX9muGOQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.80.tgz", + "integrity": "sha512-O64APRTXRUiAz0P8gErkfEr3lipLJgM6pjATwavZ22ebhjYl/SUbpgM0xcWPQBNMP1n29afAC/Us5PX1vg+JNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.80.tgz", + "integrity": "sha512-FqqSU7qFce0Cp3pwnTjVkKjjOtxMqRe6lmINxpIZYaZNnVI0H5FtsaraZJ36SiTHNjZlUB69/HhxNDT1Aaa9vA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.80.tgz", + "integrity": "sha512-eyWz0ddBDQc7/JbAtY4OtZ5SpK8tR4JsCYEZjCE3dI8pqoWUC8oMwYSBGCYfsx2w47cQgQCgMVRVTFiiO38hHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.80.tgz", + "integrity": "sha512-qwA63t8A86bnxhuA/GwOkK3jvb+XTQaTiVML0vAWoHyoZYTjNs7BzoOONDgTnNtr8/yHrq64XXzUoLqDzU+Uuw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.80.tgz", + "integrity": "sha512-1XbCOz/ymhj24lFaIXtWnwv/6eFHXDrjP0jYkc6iHQ9q8oXKzUX1Lc6bu+wuGiLhGh2GS/2JlfORC5ZcXimRcg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.80.tgz", + "integrity": "sha512-XTzR125w5ZMs0lJcxRlS1K3P5RaZ9RmUsPtd1uGt+EfDyYMu4c6SEROYsxyatbbu/2+lPe7MPHOO/0a0x7L/gw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.80.tgz", + "integrity": "sha512-BeXAmhKg1kX3UCrJsYbdQd3hIMDH/K6HnP/pG2LuITaXhXBiNdh//TVVVVCBbJzVQaV5gK/4ZOCMrQW9mvuTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-musl": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.80.tgz", + "integrity": "sha512-x0XvZWdHbkgdgucJsRxprX/4o4sEed7qo9rCQA9ugiS9qE2QvP0RIiEugtZhfLH3cyI+jIRFJHV4Fuz+1BHHMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-win32-x64-msvc": { + "version": "0.1.80", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.80.tgz", + "integrity": "sha512-Z8jPsM6df5V8B1HrCHB05+bDiCxjE9QA//3YrkKIdVDEwn5RKaqOxCJDRJkl48cJbylcrJbW4HxZbTte8juuPg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@playwright/test": { "version": "1.58.2", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", @@ -609,6 +805,40 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/pdf-parse": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-2.4.5.tgz", + "integrity": "sha512-mHU89HGh7v+4u2ubfnevJ03lmPgQ5WU4CxAVmTSh/sxVTEDYd1er/dKS/A6vg77NX47KTEoihq8jZBLr8Cxuwg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@napi-rs/canvas": "0.1.80", + "pdfjs-dist": "5.4.296" + }, + "bin": { + "pdf-parse": "bin/cli.mjs" + }, + "engines": { + "node": ">=20.16.0 <21 || >=22.3.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/mehmet-kozan" + } + }, + "node_modules/pdfjs-dist": { + "version": "5.4.296", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.296.tgz", + "integrity": "sha512-DlOzet0HO7OEnmUmB6wWGJrrdvbyJKftI1bhMitK7O2N8W2gc757yyYBbINy9IDafXAV9wmKr9t7xsTaNKRG5Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=20.16.0 || >=22.3.0" + }, + "optionalDependencies": { + "@napi-rs/canvas": "^0.1.80" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", diff --git a/e2e/package.json b/e2e/package.json index 9d7e2bfa..f12eb41e 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -6,12 +6,13 @@ "scripts": { "test": "npx playwright test --project=public-chromium --project=internal-chromium", "test:ui": "npx playwright test --ui --project=public-chromium --project=internal-chromium", - "test:urlCheck": "npx playwright test --project=urlCheck-chrome", - "test:local": "TEST_ENV=local npx playwright test --project=noDeps-local-chrome", + "test:urlCheck": "npx playwright test --project=urlCheck-chromium", + "test:local": "TEST_ENV=local npx playwright test --project=noDeps-local-chromium", "test:tst": "TEST_ENV=tst npm run test", "test:dev": "TEST_ENV=dev npm run test", "test:edge": "npx playwright test --project=public-edge --project=internal-edge", "test:firefox": "npx playwright test --project=public-firefox --project=internal-firefox", + "test:e2e": "npx playwright test --project=e2e-public-chromium --project=e2e-internal-chromium", "test:all-browsers": "npx playwright test", "report:open": "npx playwright show-report", "prepare": "husky" @@ -20,6 +21,7 @@ "devDependencies": { "@mapbox/polyline": "^1.2.1", "@playwright/test": "^1.50.0", - "husky": "^9.1.7" + "husky": "^9.1.7", + "pdf-parse": "^2.4.5" } } diff --git a/e2e/pages/.utils/form-controls.js b/e2e/pages/.utils/form-controls.js index 341fc671..6e2cff1e 100644 --- a/e2e/pages/.utils/form-controls.js +++ b/e2e/pages/.utils/form-controls.js @@ -8,3 +8,4 @@ export const footerLink = (text, url) => ({ type: 'footerLink', text, url: url = export const errorText = (text) => ({ type: 'errorText', text }) export const button = (text) => ({ type: 'button', text }) export const selectInput = (text) => ({ type: 'selectInput', text }) +export const details = (text) => ({ type: 'details', text }) diff --git a/e2e/pages/next-steps.page.js b/e2e/pages/next-steps.page.js index 7d1b7094..002a77a1 100644 --- a/e2e/pages/next-steps.page.js +++ b/e2e/pages/next-steps.page.js @@ -1,12 +1,13 @@ import { definePage } from './.utils/page.js' -import { button, link, textInput, selectInput } from './.utils/form-controls.js' +import { button, details, link, textInput, selectInput } from './.utils/form-controls.js' export const page = definePage({ slug: '/next-steps', title: 'Next steps for your planning application' }) -export const addReferenceToFloodMapDetails = button('Add a reference to the flood map and set the scale') +// P1 Map Controls +export const addReferenceToFloodMapDetails = details('Add a reference to the flood map and set the scale') export const addReferenceInput = textInput('Add a reference') export const scaleSelect = selectInput('Scale') export const downloadFloodMapButton = button('Download flood map for this location (PDF)') diff --git a/e2e/pages/results.page.js b/e2e/pages/results.page.js index d9f8f4b7..ed7017d0 100644 --- a/e2e/pages/results.page.js +++ b/e2e/pages/results.page.js @@ -1,5 +1,5 @@ import { definePage } from './.utils/page.js' -import { button, link, textInput, selectInput } from './.utils/form-controls.js' +import { button, details, link, textInput, selectInput } from './.utils/form-controls.js' export const titleForZone = (floodZone) => `This location is in flood zone ${floodZone}` export const pageWithZone = (floodZone) => definePage({ @@ -8,7 +8,7 @@ export const pageWithZone = (floodZone) => definePage({ }) // P1 Map Controls -export const addReferenceToFloodMapDetails = button('Add a reference to the flood map and set the scale') +export const addReferenceToFloodMapDetails = details('Add a reference to the flood map and set the scale') export const addReferenceInput = textInput('Add a reference') export const scaleSelect = selectInput('Scale') export const downloadFloodMapButton = button('Download flood map for this location (PDF)') diff --git a/e2e/playwright.config.js b/e2e/playwright.config.js index a7993868..bde1e99d 100644 --- a/e2e/playwright.config.js +++ b/e2e/playwright.config.js @@ -62,7 +62,7 @@ export default defineConfig({ ...browserProjects.flatMap((browserProject) => [ { name: `public-${browserProject.suffix}`, - grepInvert: /@internal|@urlCheck/, + grepInvert: /@internal|@urlCheck|@e2e/, use: { ...browserProject.use, baseURL: publicBaseURL, @@ -70,8 +70,8 @@ export default defineConfig({ }, { name: `internal-${browserProject.suffix}`, - grep: /@internal|@both/, - grepInvert: /@urlCheck/, + grep: /@internal/, + grepInvert: /@urlCheck|@e2e/, use: { ...browserProject.use, baseURL: internalBaseURL, @@ -79,21 +79,37 @@ export default defineConfig({ }, ]), { - name: 'noDeps-local-chrome', + name: 'noDeps-local-chromium', grep: /@noDeps/, - grepInvert: /@urlCheck|@internal|@both/, + grepInvert: /@urlCheck|@internal|@e2e/, use: { ...chromeConfig, baseURL: publicBaseURL, }, }, { - name: 'urlCheck-chrome', + name: 'urlCheck-chromium', grep: /@urlCheck/, use: { ...chromeConfig, baseURL: publicBaseURL, }, }, + { + name: 'e2e-public-chromium', + grep: /@e2e/, + use: { + ...chromeConfig, + baseURL: publicBaseURL, + }, + }, + { + name: 'e2e-internal-chromium', + grep: /@e2e/, + use: { + ...chromeConfig, + baseURL: internalBaseURL, + }, + }, ], }) diff --git a/e2e/test-runner-api/form-driver.js b/e2e/test-runner-api/form-driver.js index afd60c00..629160da 100644 --- a/e2e/test-runner-api/form-driver.js +++ b/e2e/test-runner-api/form-driver.js @@ -55,6 +55,10 @@ export class FormDriver { await this.page.getByRole('button', { name: element.text, exact: true }).click() } + async clickDetails (element) { + await this.page.locator('details').filter({ hasText: element.text }).locator('summary').click() + } + async clickLink (element) { const locator = this.#getLinkLocator(element) // Some pages legitimately contain duplicate link text; click the first visible match. diff --git a/e2e/test-runner-api/pdf-driver.js b/e2e/test-runner-api/pdf-driver.js new file mode 100644 index 00000000..e307c629 --- /dev/null +++ b/e2e/test-runner-api/pdf-driver.js @@ -0,0 +1,82 @@ +import fs from 'node:fs/promises' +import path from 'node:path' +import { PDFParse } from 'pdf-parse' +import { expect } from '@playwright/test' + +const downloadsDir = path.resolve(process.cwd(), '_results_', 'downloads') + +export class PdfDriver { + constructor (page) { + this.page = page + } + + // ----DOWNLOAD HELPERS---- // + + awaitDownload (timeout = 60000) { + return this.page.waitForEvent('download', { timeout }) + .then(async (download) => { + await fs.mkdir(downloadsDir, { recursive: true }) + const dest = path.join(downloadsDir, download.suggestedFilename()) + await download.saveAs(dest) + return dest + }) + } + + // ----PARSE HELPERS---- // + + async parsePdf (filePath) { + const buffer = await fs.readFile(filePath) + const parser = new PDFParse({ data: buffer }) + const parsedPdf = await parser.getText() + const pdfInfo = await parser.getInfo({ parsePageInfo: true }) + await parser.destroy() + + const text = parsedPdf.text.replaceAll(/\s+/g, ' ').toLowerCase() + const links = (pdfInfo.pages || []) + .flatMap((page) => page.links || []) + .map((link) => link?.url || link?.unsafeUrl || link?.href || '') + .filter(Boolean) + return { text, links } + } + + // ----ASSERTIONS---- // + + expectCoreContent (pdf, { reference, scale }) { + const compactText = pdf.text + .replaceAll(/\s+/g, '') + .replaceAll(',', '') + + expect(pdf.text.length).toBeGreaterThan(100) + expect(pdf.text).toContain('flood') + expect(pdf.text).toContain('your reference') + expect(pdf.text).toContain(reference ? reference.toLowerCase() : 'unspecified') + expect(compactText).toContain(`1:${scale}`) + } + + expectFloodZone (pdf, floodZone) { + expect(pdf.text).toContain(`your selected location is in flood zone ${floodZone}`) + } + + expectLocation (pdf, polygonString) { + const coordinates = JSON.parse(polygonString) + const isClosed = JSON.stringify(coordinates[0]) === JSON.stringify(coordinates.at(-1)) + const points = isClosed ? coordinates.slice(0, -1) : coordinates + const easting = Math.floor(points.reduce((sum, [x]) => sum + x, 0) / points.length) + const northing = Math.floor(points.reduce((sum, [, y]) => sum + y, 0) / points.length) + expect(pdf.text).toContain(`${easting}/${northing}`) + } + + expectLinks (pdf, expectedLinks) { + expect(pdf.links.length).toBeGreaterThan(0) + const normalized = pdf.links.map((url) => url.trim().replace(/\/$/, '')) + expectedLinks.forEach((link) => expect(normalized).toContain(link)) + pdf.links.forEach((url) => expect(URL.canParse(url)).toBe(true)) + } + + expectPdfContent (pdf, { reference, scale, floodZone, polygon, expectedLinks }) { + this.expectCoreContent(pdf, { reference, scale }) + this.expectFloodZone(pdf, floodZone) + this.expectLocation(pdf, polygon) + this.expectLinks(pdf, expectedLinks) + } +} diff --git a/e2e/tests/e2e.spec.js b/e2e/tests/e2e.spec.js index d991ff79..bc1420cf 100644 --- a/e2e/tests/e2e.spec.js +++ b/e2e/tests/e2e.spec.js @@ -4,7 +4,7 @@ import { locationData } from '../data/location-data.js' import { userData } from '../data/user-data.js' test.describe('End-to-end planning journey', () => { - test('completes the journey from home to confirmation', { tag: '@both' }, async ({ steps, mapSteps }) => { + test('completes the journey from home to confirmation', { tag: '@e2e' }, async ({ steps, mapSteps }) => { await test.step('Home → Triage', async () => { await steps.open(pages.home.page) await steps.clickButton(pages.home.startButton) diff --git a/e2e/tests/pages/check-your-details-page.spec.js b/e2e/tests/pages/check-your-details-page.spec.js index e1c99be2..ebad851a 100644 --- a/e2e/tests/pages/check-your-details-page.spec.js +++ b/e2e/tests/pages/check-your-details-page.spec.js @@ -26,9 +26,4 @@ test.describe('Check your details page', () => { test('confirms the change location link is present', async ({ steps }) => { await steps.expectLinkExists(pages.checkYourDetails.changeLocationLink) }) - - test('navigates to confirmation page after clicking order button', async ({ steps }) => { - await steps.clickButton(pages.checkYourDetails.orderButton) - await steps.expectOn(pages.confirmation.page) - }) }) diff --git a/e2e/tests/pages/confirmation-page.spec.js b/e2e/tests/pages/confirmation-page.spec.js index c1baeb41..cc8e7668 100644 --- a/e2e/tests/pages/confirmation-page.spec.js +++ b/e2e/tests/pages/confirmation-page.spec.js @@ -26,7 +26,6 @@ test.describe('Confirmation page', () => { }) // The following tests validate that external links can be reached. - test('navigates to to get more information to help you complete a flood risk assessmment page when clicking the link', { tag: '@urlCheck' }, async ({ steps }) => { await steps.clickLink(pages.confirmation.toGetMoreInformationLink) await steps.expectUrlContains('get-information-about-flood-risk') diff --git a/e2e/tests/pages/next-steps-page.spec.js b/e2e/tests/pages/next-steps-page.spec.js index d08395fc..d217c61a 100644 --- a/e2e/tests/pages/next-steps-page.spec.js +++ b/e2e/tests/pages/next-steps-page.spec.js @@ -22,7 +22,6 @@ test.describe('Next steps page', () => { }) // The following tests validate that external links can be reached. - test('navigates to Flood risk assessments: climate change allowances page when clicking the link', { tag: '@urlCheck' }, async ({ steps }) => { await steps.clickLink(pages.nextSteps.takeIntoAccountClimateChangeAllowancesLink) await steps.expectUrlContains('climate-change-allowances') diff --git a/e2e/tests/pages/results-page.spec.js b/e2e/tests/pages/results-page.spec.js index 488f203e..941d7eda 100644 --- a/e2e/tests/pages/results-page.spec.js +++ b/e2e/tests/pages/results-page.spec.js @@ -131,4 +131,50 @@ test.describe('Results page', () => { await steps.expectLinkExists(pages.results.orderFloodRiskDataButton) }) }) + + test.describe('PDF download checks', () => { + const expectedPdfLinks = [ + 'https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3', + 'https://flood-map-for-planning.service.gov.uk/os-terms' + ] + + const pdfScenarios = [ + { label: 'flood zone 1', floodZone: '1', polygon: floodZonedata.FZ1_With_RandS }, + { label: 'flood zone 2', floodZone: '2', polygon: floodZonedata.FZ2_With_RandS }, + { label: 'flood zone 3', floodZone: '3', polygon: floodZonedata.FZ3_With_SW_and_RandS } + ] + + for (const { label, floodZone, polygon } of pdfScenarios) { + test(`downloads pdf with reference and scale for ${label}`, async ({ steps, pdfDriver }) => { + const reference = 'Test123456789101112131415' + const scale = '25000' + + await steps.open({ ...pages.results.pageWithZone(floodZone), slug: slug(polygon) }) + await steps.clickDetails(pages.results.addReferenceToFloodMapDetails) + await steps.type(pages.results.addReferenceInput, reference) + await steps.select(pages.results.scaleSelect, scale) + + const downloadPromise = pdfDriver.awaitDownload() + await steps.clickButton(pages.results.downloadFloodMapButton) + const pdf = await pdfDriver.parsePdf(await downloadPromise) + + pdfDriver.expectPdfContent(pdf, { reference, scale, floodZone, polygon, expectedLinks: expectedPdfLinks }) + }) + } + + test('defaults to unspecified reference when none provided', async ({ steps, pdfDriver }) => { + const polygon = floodZonedata.FZ1_With_RandS + const scale = '2500' + + await steps.open({ ...pages.results.pageWithZone('1'), slug: slug(polygon) }) + await steps.clickDetails(pages.results.addReferenceToFloodMapDetails) + await steps.select(pages.results.scaleSelect, scale) + + const downloadPromise = pdfDriver.awaitDownload() + await steps.clickButton(pages.results.downloadFloodMapButton) + const pdf = await pdfDriver.parsePdf(await downloadPromise) + + pdfDriver.expectPdfContent(pdf, { scale, floodZone: '1', polygon, expectedLinks: expectedPdfLinks }) + }) + }) }) diff --git a/server/views/results.html b/server/views/results.html index fc853d28..393c242e 100644 --- a/server/views/results.html +++ b/server/views/results.html @@ -56,10 +56,7 @@