diff --git a/api.planx.uk/package.json b/api.planx.uk/package.json index 6a900750dd..0fbd584704 100644 --- a/api.planx.uk/package.json +++ b/api.planx.uk/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@airbrake/node": "^2.1.8", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#7e31b2e", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#44e9ebe", "@types/isomorphic-fetch": "^0.0.36", "adm-zip": "^0.5.10", "aws-sdk": "^2.1441.0", diff --git a/api.planx.uk/pnpm-lock.yaml b/api.planx.uk/pnpm-lock.yaml index 2430241e60..da7eb790a8 100644 --- a/api.planx.uk/pnpm-lock.yaml +++ b/api.planx.uk/pnpm-lock.yaml @@ -9,8 +9,8 @@ dependencies: specifier: ^2.1.8 version: 2.1.8 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#7e31b2e - version: git/github.com+theopensystemslab/planx-core/7e31b2e + specifier: git+https://github.com/theopensystemslab/planx-core#44e9ebe + version: github.com/theopensystemslab/planx-core/44e9ebe '@types/isomorphic-fetch': specifier: ^0.0.36 version: 0.0.36 @@ -1179,13 +1179,13 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.49.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.50.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.49.0 + eslint: 8.50.0 eslint-visitor-keys: 3.4.3 dev: false @@ -1219,8 +1219,8 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@eslint/js@8.49.0: - resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==} + /@eslint/js@8.50.0: + resolution: {integrity: sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: false @@ -1962,8 +1962,8 @@ packages: '@types/qs': 6.9.7 '@types/serve-static': 1.15.2 - /@types/geojson@7946.0.10: - resolution: {integrity: sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==} + /@types/geojson@7946.0.11: + resolution: {integrity: sha512-L7A0AINMXQpVwxHJ4jxD6/XjZ4NDufaRlUJHjNIFKYUFBH1SvOW+neaqb0VTRSLW5suSrSu19ObFEFnfNcr+qg==} dev: false /@types/glob@7.2.0: @@ -3743,15 +3743,15 @@ packages: - supports-color dev: true - /eslint@8.49.0: - resolution: {integrity: sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==} + /eslint@8.50.0: + resolution: {integrity: sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) '@eslint-community/regexpp': 4.6.2 '@eslint/eslintrc': 2.1.2 - '@eslint/js': 8.49.0 + '@eslint/js': 8.50.0 '@humanwhocodes/config-array': 0.11.11 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -4102,8 +4102,8 @@ packages: punycode: 1.4.1 dev: false - /fast-xml-parser@4.2.7: - resolution: {integrity: sha512-J8r6BriSLO1uj2miOk1NW0YVm8AGOOu3Si2HQp/cSmo6EA4m3fcwu2WKjJ4RK9wMLBtg69y1kS8baDiQBR41Ig==} + /fast-xml-parser@4.3.1: + resolution: {integrity: sha512-viVv3xb8D+SiS1W4cv4tva3bni08kAkx0gQnWrykMM8nXPc1FxqZPU00dCEVjkiCg4HoXd2jC4x29Nzg/l2DAA==} hasBin: true dependencies: strnum: 1.0.5 @@ -8073,8 +8073,8 @@ packages: resolution: {integrity: sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==} dev: false - git/github.com+theopensystemslab/planx-core/7e31b2e: - resolution: {commit: 7e31b2e, repo: git@github.com:theopensystemslab/planx-core.git, type: git} + github.com/theopensystemslab/planx-core/44e9ebe: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/44e9ebe} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -8083,12 +8083,13 @@ packages: '@emotion/react': 11.11.1(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(react@18.2.0) '@mui/material': 5.14.10(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0) - '@types/geojson': 7946.0.10 + '@types/geojson': 7946.0.11 ajv: 8.12.0 ajv-formats: 2.1.1(ajv@8.12.0) + copyfiles: 2.4.1 docx: 8.2.2 - eslint: 8.49.0 - fast-xml-parser: 4.2.7 + eslint: 8.50.0 + fast-xml-parser: 4.3.1 graphql: 16.8.1 graphql-request: 6.1.0(graphql@16.8.1) json-schema-to-typescript: 13.1.1 diff --git a/e2e/README.md b/e2e/README.md index 2c7f67c250..0585a1bcc9 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -11,7 +11,7 @@ We use [Playwright](https://playwright.dev/docs/api/class-test) to run UI driven ## API driven tests -We use [Gherkin](https://cucumber.io/docs/gherkin/reference) to run API tests where interactions are simulated via automated network requests. +We use [Gherkin](https://cucumber.io/docs/gherkin/reference) to run API tests where interactions are simulated via automated network requests. For more details please see [our README here](https://github.com/theopensystemslab/planx-new/blob/main/e2e/tests/api-driven/README.md)). # Running Tests diff --git a/e2e/tests/api-driven/README.md b/e2e/tests/api-driven/README.md new file mode 100644 index 0000000000..fcc3e0e63e --- /dev/null +++ b/e2e/tests/api-driven/README.md @@ -0,0 +1,27 @@ +# API-Driven Tests + +This subfolder contains end-to-end (E2E) tests aimed at testing the logic of interactions between our services, without requiring a front-end interface to drive user interactions. + +These tests may require interaction with various services, such as Hasura and a PostgreSQL database, or a mocked third-party service such as Uniform or BOPS. + +## Stack + +- **Gherkin**: Gherkin is a language used for specifying structured test scenarios [(docs)](https://docs.cucumber.io/gherkin/reference/) +- **Cucumber-JS**: Cucumber-JS is a JavaScript implementation of the Cucumber framework, allowing us to write and run behaviour-driven development (BDD) tests [(docs)](https://github.com/cucumber/cucumber-js) +- **Node.js assert**: The `assert` module in Node.js is used for making assertions in our tests [(docs)](https://nodejs.org/api/assert.html). If we find this to be insufficient we could move to a richer assertation library such as Jest. + +## Basic setup for adding new tests + +To add new tests to this folder, follow these steps: + +1. Create a subfolder for each test scenario you want to test. + +2. Inside each test subfolder, include the following files: + + - `.feature` file: This file contains the Gherkin syntax to describe the test scenario in a human-readable format. It outlines the steps and expected behaviour of the test. + - `helpers.ts` file: This file should contain utility functions and setup/cleanup logic for your test. At a minimum, it should include the following functions: + + - `setup()`: This function sets up the necessary environment or configurations required for the test scenario. + - `cleanup()`: This function performs cleanup tasks after the test scenario is executed, such as resetting the environment to its initial state. + + - `steps.ts` file: This file contains the step definitions for your Gherkin scenarios. Each step definition maps a step in the `.feature` file to a function that performs the corresponding action and assertion in your test. We aim to keep these steps as readable as possible, with the majority of the logic being contained within `helpers.ts`. diff --git a/e2e/tests/api-driven/package.json b/e2e/tests/api-driven/package.json index de91478a3e..d54098c0e8 100644 --- a/e2e/tests/api-driven/package.json +++ b/e2e/tests/api-driven/package.json @@ -6,13 +6,16 @@ }, "dependencies": { "@cucumber/cucumber": "^9.3.0", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#4e3d09f", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#44e9ebe", "axios": "^1.4.0", "dotenv": "^16.3.1", "dotenv-expand": "^10.0.0", + "graphql-request": "^6.1.0", + "jsonwebtoken": "^9.0.2", "nock": "^13.3.1" }, "devDependencies": { + "@types/lodash.zipobject": "^4.1.7", "@types/node": "18.16.1", "ts-node": "^10.9.1", "typescript": "^5.1.6" diff --git a/e2e/tests/api-driven/pnpm-lock.yaml b/e2e/tests/api-driven/pnpm-lock.yaml index c0b30e9e7b..e93584fddc 100644 --- a/e2e/tests/api-driven/pnpm-lock.yaml +++ b/e2e/tests/api-driven/pnpm-lock.yaml @@ -9,8 +9,8 @@ dependencies: specifier: ^9.3.0 version: 9.3.0 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#4e3d09f - version: github.com/theopensystemslab/planx-core/4e3d09f + specifier: git+https://github.com/theopensystemslab/planx-core#44e9ebe + version: github.com/theopensystemslab/planx-core/44e9ebe axios: specifier: ^1.4.0 version: 1.4.0 @@ -20,11 +20,20 @@ dependencies: dotenv-expand: specifier: ^10.0.0 version: 10.0.0 + graphql-request: + specifier: ^6.1.0 + version: 6.1.0(graphql@16.8.0) + jsonwebtoken: + specifier: ^9.0.2 + version: 9.0.2 nock: specifier: ^13.3.1 version: 13.3.1 devDependencies: + '@types/lodash.zipobject': + specifier: ^4.1.7 + version: 4.1.7 '@types/node': specifier: 18.16.1 version: 18.16.1 @@ -369,13 +378,13 @@ packages: resolution: {integrity: sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==} dev: false - /@eslint-community/eslint-utils@4.4.0(eslint@8.49.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.50.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.49.0 + eslint: 8.50.0 eslint-visitor-keys: 3.4.3 dev: false @@ -401,8 +410,8 @@ packages: - supports-color dev: false - /@eslint/js@8.49.0: - resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==} + /@eslint/js@8.50.0: + resolution: {integrity: sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: false @@ -442,6 +451,14 @@ packages: graphql: 16.8.0 dev: false + /@graphql-typed-document-node/core@3.2.0(graphql@16.8.1): + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + graphql: 16.8.1 + dev: false + /@humanwhocodes/config-array@0.11.11: resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} engines: {node: '>=10.10.0'} @@ -682,8 +699,8 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true - /@types/geojson@7946.0.10: - resolution: {integrity: sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==} + /@types/geojson@7946.0.11: + resolution: {integrity: sha512-L7A0AINMXQpVwxHJ4jxD6/XjZ4NDufaRlUJHjNIFKYUFBH1SvOW+neaqb0VTRSLW5suSrSu19ObFEFnfNcr+qg==} dev: false /@types/glob@7.2.0: @@ -697,9 +714,14 @@ packages: resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} dev: false + /@types/lodash.zipobject@4.1.7: + resolution: {integrity: sha512-bsFXX/ac3fFgW3l/yxwRx7NvTXryi4bMaNcsbSK2MJnTPn0nHvs7NdwfHtvOkNKxSQ0dXgnNwI5oEGLoMA1mug==} + dependencies: + '@types/lodash': 4.14.196 + dev: true + /@types/lodash@4.14.196: resolution: {integrity: sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==} - dev: false /@types/minimatch@5.1.2: resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} @@ -880,6 +902,10 @@ packages: concat-map: 0.0.1 dev: false + /buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + dev: false + /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: false @@ -942,6 +968,14 @@ packages: '@colors/colors': 1.5.0 dev: false + /cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: false + /clsx@2.0.0: resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} engines: {node: '>=6'} @@ -998,6 +1032,19 @@ packages: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} dev: false + /copyfiles@2.4.1: + resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} + hasBin: true + dependencies: + glob: 7.2.3 + minimatch: 3.1.2 + mkdirp: 1.0.4 + noms: 0.0.0 + through2: 2.0.5 + untildify: 4.0.0 + yargs: 16.2.0 + dev: false + /core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} dev: false @@ -1110,6 +1157,12 @@ packages: engines: {node: '>=12'} dev: false + /ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + dependencies: + safe-buffer: 5.1.2 + dev: false + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: false @@ -1160,6 +1213,11 @@ packages: es6-symbol: 3.1.3 dev: false + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: false + /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -1183,15 +1241,15 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: false - /eslint@8.49.0: - resolution: {integrity: sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==} + /eslint@8.50.0: + resolution: {integrity: sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) '@eslint-community/regexpp': 4.6.2 '@eslint/eslintrc': 2.1.2 - '@eslint/js': 8.49.0 + '@eslint/js': 8.50.0 '@humanwhocodes/config-array': 0.11.11 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -1292,8 +1350,8 @@ packages: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: false - /fast-xml-parser@4.2.7: - resolution: {integrity: sha512-J8r6BriSLO1uj2miOk1NW0YVm8AGOOu3Si2HQp/cSmo6EA4m3fcwu2WKjJ4RK9wMLBtg69y1kS8baDiQBR41Ig==} + /fast-xml-parser@4.3.1: + resolution: {integrity: sha512-viVv3xb8D+SiS1W4cv4tva3bni08kAkx0gQnWrykMM8nXPc1FxqZPU00dCEVjkiCg4HoXd2jC4x29Nzg/l2DAA==} hasBin: true dependencies: strnum: 1.0.5 @@ -1370,6 +1428,11 @@ packages: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: false + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: false + /get-stdin@8.0.0: resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} engines: {node: '>=10'} @@ -1433,11 +1496,28 @@ packages: - encoding dev: false + /graphql-request@6.1.0(graphql@16.8.1): + resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==} + peerDependencies: + graphql: 14 - 16 + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.1) + cross-fetch: 3.1.8 + graphql: 16.8.1 + transitivePeerDependencies: + - encoding + dev: false + /graphql@16.8.0: resolution: {integrity: sha512-0oKGaR+y3qcS5mCu1vb7KG+a89vjn06C7Ihq/dDl3jA+A8B3TKomvi3CiEcVLJQGalbu8F52LxkOym7U5sSfbg==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} dev: false + /graphql@16.8.1: + resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + dev: false + /has-ansi@4.0.1: resolution: {integrity: sha512-Qr4RtTm30xvEdqUXbSBVWDu+PrTokJOwe/FU+VdfJPk+MXAPoeOzKpRyrDTnZIJwAkQ4oBLTU53nu0HrkF/Z2A==} engines: {node: '>=8'} @@ -1560,6 +1640,10 @@ packages: engines: {node: '>=8'} dev: false + /isarray@0.0.1: + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + dev: false + /isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} dev: false @@ -1620,6 +1704,22 @@ packages: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} dev: false + /jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.2 + semver: 7.5.4 + dev: false + /jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} dependencies: @@ -1629,6 +1729,21 @@ packages: setimmediate: 1.0.5 dev: false + /jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.1.2 + dev: false + + /jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + dependencies: + jwa: 1.4.1 + safe-buffer: 5.1.2 + dev: false + /knuth-shuffle-seeded@1.0.6: resolution: {integrity: sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg==} dependencies: @@ -1680,14 +1795,38 @@ packages: resolution: {integrity: sha512-rnYUdIo6xRCJnQmbVFEwcxF144erlD+M3YcJUVesflU9paQaE8p+fJDcIQrlMYbxoANFL+AB9hZrzSBBk5PL+g==} dev: false + /lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + dev: false + + /lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + dev: false + /lodash.isempty@4.4.0: resolution: {integrity: sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==} dev: false + /lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + dev: false + /lodash.isnil@4.0.0: resolution: {integrity: sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==} dev: false + /lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + dev: false + + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: false + + /lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + dev: false + /lodash.kebabcase@4.1.1: resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} dev: false @@ -1704,6 +1843,10 @@ packages: resolution: {integrity: sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==} dev: false + /lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + dev: false + /lodash.property@4.4.2: resolution: {integrity: sha512-WVnsHSCea5NFrsWGdZilCFJWhHFtb/nqVrDXzR1bIXmOxIykrsAKOpRaYu9rCYoHYuq1SnG6G4A0inMwShV4dg==} dev: false @@ -1863,6 +2006,13 @@ packages: whatwg-url: 5.0.0 dev: false + /noms@0.0.0: + resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} + dependencies: + inherits: 2.0.4 + readable-stream: 1.0.34 + dev: false + /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -2047,6 +2197,15 @@ packages: loose-envify: 1.4.0 dev: false + /readable-stream@1.0.34: + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 0.0.1 + string_decoder: 0.10.31 + dev: false + /readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} dependencies: @@ -2087,6 +2246,11 @@ packages: engines: {node: '>=0.10'} dev: false + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: false + /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -2162,6 +2326,14 @@ packages: lru-cache: 6.0.0 dev: false + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: false + /setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} dev: false @@ -2213,6 +2385,10 @@ packages: strip-ansi: 6.0.1 dev: false + /string_decoder@0.10.31: + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} + dev: false + /string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: @@ -2231,6 +2407,10 @@ packages: engines: {node: '>=8'} dev: false + /striptags@3.2.0: + resolution: {integrity: sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw==} + dev: false + /strnum@1.0.5: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} dev: false @@ -2282,6 +2462,13 @@ packages: any-promise: 1.3.0 dev: false + /through2@2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + dependencies: + readable-stream: 2.3.8 + xtend: 4.0.2 + dev: false + /timers-ext@0.1.7: resolution: {integrity: sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==} dependencies: @@ -2375,6 +2562,11 @@ packages: hasBin: true dev: true + /untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + dev: false + /upper-case-first@2.0.2: resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==} dependencies: @@ -2437,6 +2629,15 @@ packages: isexe: 2.0.0 dev: false + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: false + /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: false @@ -2457,6 +2658,16 @@ packages: engines: {node: '>=8.0'} dev: false + /xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: false + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: false + /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: false @@ -2471,6 +2682,24 @@ packages: engines: {node: '>= 14'} dev: false + /yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: false + + /yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + dev: false + /yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} @@ -2498,8 +2727,8 @@ packages: resolution: {integrity: sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==} dev: false - github.com/theopensystemslab/planx-core/4e3d09f: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/4e3d09f} + github.com/theopensystemslab/planx-core/44e9ebe: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/44e9ebe} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -2508,14 +2737,15 @@ packages: '@emotion/react': 11.11.1(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(react@18.2.0) '@mui/material': 5.14.10(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0) - '@types/geojson': 7946.0.10 + '@types/geojson': 7946.0.11 ajv: 8.12.0 ajv-formats: 2.1.1(ajv@8.12.0) + copyfiles: 2.4.1 docx: 8.2.2 - eslint: 8.49.0 - fast-xml-parser: 4.2.7 - graphql: 16.8.0 - graphql-request: 6.1.0(graphql@16.8.0) + eslint: 8.50.0 + fast-xml-parser: 4.3.1 + graphql: 16.8.1 + graphql-request: 6.1.0(graphql@16.8.1) json-schema-to-typescript: 13.1.1 lodash.capitalize: 4.2.1 lodash.get: 4.4.2 @@ -2531,6 +2761,7 @@ packages: prettier: 3.0.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) + striptags: 3.2.0 type-fest: 4.3.1 uuid: 9.0.1 zod: 3.22.2 diff --git a/e2e/tests/api-driven/src/client.ts b/e2e/tests/api-driven/src/client.ts index 808dbfb62c..19513cadf6 100644 --- a/e2e/tests/api-driven/src/client.ts +++ b/e2e/tests/api-driven/src/client.ts @@ -1,5 +1,6 @@ import assert from "node:assert"; import { CoreDomainClient } from "@opensystemslab/planx-core"; +import { buildJWT } from "./jwt"; // check env variables are defined assert(process.env.HASURA_GRAPHQL_ADMIN_SECRET); @@ -11,6 +12,21 @@ const targetURL = process.env.HASURA_GRAPHQL_URL!.replace( ); export const $admin = new CoreDomainClient({ - hasuraSecret: process.env.HASURA_GRAPHQL_ADMIN_SECRET!, + auth: { adminSecret: process.env.HASURA_GRAPHQL_ADMIN_SECRET! }, targetURL, }); + +/** + * Get client authorised to the permissions level of the provided user + */ +export const getClient = async (email: string) => { + const jwt = await buildJWT(email); + if (!jwt) throw Error("Unable to generate JWT for test user"); + + const client = new CoreDomainClient({ + targetURL: process.env.HASURA_GRAPHQL_URL!, + auth: { jwt }, + }); + + return client; +}; diff --git a/e2e/tests/api-driven/src/globalHelpers.ts b/e2e/tests/api-driven/src/globalHelpers.ts new file mode 100644 index 0000000000..9894013afd --- /dev/null +++ b/e2e/tests/api-driven/src/globalHelpers.ts @@ -0,0 +1,57 @@ +import { TEST_EMAIL } from "../../ui-driven/src/helpers"; +import { $admin } from "./client"; + +export function createTeam( + args?: Partial[0]>, +) { + return safely(() => + $admin.team.create({ + name: "E2E Test Team", + slug: "E2E", + logo: "https://raw.githubusercontent.com/theopensystemslab/planx-team-logos/main/planx-testing.svg", + primaryColor: "#444444", + submissionEmail: TEST_EMAIL, + homepage: "planx.uk", + ...args, + }), + ); +} + +export function createUser( + args?: Partial[0]>, +) { + return safely(() => + $admin.user.create({ + firstName: "Test", + lastName: "Test", + email: TEST_EMAIL, + ...args, + }), + ); +} + +export function createFlow( + args: Omit[0], "data">, +) { + return safely(() => + $admin.flow.create({ + data: { dummy: "flowData " }, + ...args, + }), + ); +} + +/** + * Error handling boilerplate for client functions + */ +export function safely ReturnType>(callback: T) { + const result = callback(); + if (!result) { + throw new Error("Error setting up E2E test"); + } + return result; +} + +export async function getUser(email: string) { + return await $admin.user.getByEmail(email); +} diff --git a/e2e/tests/api-driven/src/invite-to-pay/helpers.ts b/e2e/tests/api-driven/src/invite-to-pay/helpers.ts index b7175fa247..343ccb0872 100644 --- a/e2e/tests/api-driven/src/invite-to-pay/helpers.ts +++ b/e2e/tests/api-driven/src/invite-to-pay/helpers.ts @@ -1,3 +1,4 @@ +import { CustomWorld } from "./steps"; import axios from "axios"; import { readFileSync } from "node:fs"; import type { @@ -12,6 +13,7 @@ import { } from "./mocks"; import { $admin } from "../client"; import { TEST_EMAIL } from "../../../ui-driven/src/helpers"; +import { createTeam, createUser } from "../globalHelpers"; export async function setUpMocks() { const serverMockFile = readFileSync(`${__dirname}/mocks/server-mocks.yaml`); @@ -25,25 +27,6 @@ export async function setUpMocks() { }); } -export async function createTeam() { - return $admin.team.create({ - name: "E2E Test Team", - slug: "E2E", - logo: "https://raw.githubusercontent.com/theopensystemslab/planx-team-logos/main/planx-testing.svg", - primaryColor: "#444444", - submissionEmail: TEST_EMAIL, - homepage: "planx.uk", - }); -} - -export async function createUser() { - return $admin.user.create({ - firstName: "Test", - lastName: "Test", - email: TEST_EMAIL, - }); -} - export async function buildITPFlow({ userId, teamId, @@ -147,21 +130,14 @@ export async function getSessionSubmittedAt( return detailedSession?.submittedAt; } -export async function tearDownTestContext({ +export async function cleanup({ teamId, userId, flowId, publishedFlowId, sessionId, paymentRequestId, -}: { - teamId?: number; - userId?: number; - flowId?: string; - publishedFlowId?: number; - sessionId?: string; - paymentRequestId?: string; -}) { +}: CustomWorld) { if (paymentRequestId) { await $admin.paymentRequest._destroy(paymentRequestId); } @@ -182,3 +158,13 @@ export async function tearDownTestContext({ await $admin.team._destroy(teamId); } } + +export const setup = async () => { + await setUpMocks(); + const world = { + teamId: await createTeam(), + userId: await createUser(), + }; + + return world; +}; diff --git a/e2e/tests/api-driven/src/invite-to-pay/steps.ts b/e2e/tests/api-driven/src/invite-to-pay/steps.ts index 02e6612e3e..84e2eb6caa 100644 --- a/e2e/tests/api-driven/src/invite-to-pay/steps.ts +++ b/e2e/tests/api-driven/src/invite-to-pay/steps.ts @@ -1,16 +1,6 @@ import { strict as assert } from "node:assert"; +import { Given, When, Then, Before, After, World } from "@cucumber/cucumber"; import { - Given, - When, - Then, - BeforeAll, - After, - AfterAll, -} from "@cucumber/cucumber"; -import { - setUpMocks, - createTeam, - createUser, buildITPFlow, buildSessionForFlow, buildPaymentRequestForSession, @@ -18,91 +8,77 @@ import { getSendResponse, getSessionSubmittedAt, waitForResponse, - tearDownTestContext, + cleanup, + setup, } from "./helpers"; -const context: { - teamId?: number; - userId?: number; +export class CustomWorld extends World { + teamId!: number; + userId!: number; flowId?: string; publishedFlowId?: number; sessionId?: string; paymentRequestId?: string; -} = {}; - -BeforeAll(async () => { - await setUpMocks(); - context.teamId = await createTeam(); - if (!context.teamId) { - throw new Error("team not found"); - } - context.userId = await createUser(); - if (!context.userId) { - throw new Error("user not found"); - } -}); +} -// tear down each example but not user and team -After(async () => { - await tearDownTestContext({ - flowId: context.flowId, - publishedFlowId: context.publishedFlowId, - sessionId: context.sessionId, - paymentRequestId: context.paymentRequestId, - }); +Before("@invite-to-pay", async function () { + const { teamId, userId } = await setup(); + this.teamId = teamId; + this.userId = userId; }); -// tear down everything -AfterAll(async () => { - await tearDownTestContext(context); +After("@invite-to-pay", async function (this: CustomWorld) { + await cleanup(this); }); Given( "a session with a payment request for an invite to pay flow where {string} is a send destination", - async (destination) => { + { timeout: 60 * 1000 }, + async function (this: CustomWorld, destination: string) { const { flowId, publishedFlowId } = await buildITPFlow({ destination, - teamId: context.teamId!, - userId: context.userId!, + teamId: this.teamId, + userId: this.userId, }); - context.flowId = flowId; - if (!context.flowId) { + this.flowId = flowId; + if (!this.flowId) { throw new Error("flow not found"); } - context.publishedFlowId = publishedFlowId; - if (!context.publishedFlowId) { + this.publishedFlowId = publishedFlowId; + if (!this.publishedFlowId) { throw new Error("publishedFlowId not found"); } - context.sessionId = await buildSessionForFlow(context.flowId); - if (!context.sessionId) { + this.sessionId = await buildSessionForFlow(this.flowId); + if (!this.sessionId) { throw new Error("session not found"); } - const paymentRequest = await buildPaymentRequestForSession( - context.sessionId, - ); - context.paymentRequestId = paymentRequest.id; + const paymentRequest = await buildPaymentRequestForSession(this.sessionId); + this.paymentRequestId = paymentRequest.id; }, ); -When("the payment request's `paid_at` date is set", async () => { - if (!context.paymentRequestId) { - throw new Error("payment request not found"); - } - const operationSucceeded = await markPaymentRequestAsPaid( - context.paymentRequestId, - ); - if (!operationSucceeded) { - throw new Error("payment request was not marked as paid"); - } -}); +When( + "the payment request's `paid_at` date is set", + async function (this: CustomWorld) { + if (!this.paymentRequestId) { + throw new Error("payment request not found"); + } + const operationSucceeded = await markPaymentRequestAsPaid( + this.paymentRequestId, + ); + if (!operationSucceeded) { + throw new Error("payment request was not marked as paid"); + } + }, +); Then( "there should be an audit entry for a successful {string} submission", { timeout: 6 * 15000 + 1000 }, - async (destination) => { + async function (this: CustomWorld, destination: string) { const response = await waitForResponse({ name: `Application submission for ${destination}`, - request: getSendResponse.bind(null, destination, context.sessionId!), + request: getSendResponse.bind(null, destination, this.sessionId!), retries: 5, delay: 15000, }); @@ -110,7 +86,10 @@ Then( }, ); -Then("the session's `submitted_at` date should be set", async () => { - const submittedAt = await getSessionSubmittedAt(context.sessionId!); - assert(submittedAt); -}); +Then( + "the session's `submitted_at` date should be set", + async function (this: CustomWorld) { + const submittedAt = await getSessionSubmittedAt(this.sessionId!); + assert(submittedAt); + }, +); diff --git a/e2e/tests/api-driven/src/invite-to-pay/submissions.feature b/e2e/tests/api-driven/src/invite-to-pay/submissions.feature index 1c63c22f79..75e714e34d 100644 --- a/e2e/tests/api-driven/src/invite-to-pay/submissions.feature +++ b/e2e/tests/api-driven/src/invite-to-pay/submissions.feature @@ -1,6 +1,6 @@ Feature: Invite to Pay Submissions - @regression + @regression @invite-to-pay Scenario: Modified `paid_at` on sessions with a valid payment request Given a session with a payment request for an invite to pay flow where "" is a send destination When the payment request's `paid_at` date is set diff --git a/e2e/tests/api-driven/src/jwt.ts b/e2e/tests/api-driven/src/jwt.ts new file mode 100644 index 0000000000..27abe0e0f2 --- /dev/null +++ b/e2e/tests/api-driven/src/jwt.ts @@ -0,0 +1,51 @@ +import { sign } from "jsonwebtoken"; +import { User, Role } from "@opensystemslab/planx-core/types"; +import { $admin } from "./client"; + +// This code is copied from api.planx.uk/modules/auth/service.ts + +export const buildJWT = async (email: string): Promise => { + const user = await $admin.user.getByEmail(email); + if (!user) return; + + const data = { + sub: user.id.toString(), + "https://hasura.io/jwt/claims": generateHasuraClaimsForUser(user), + }; + + const jwt = sign(data, process.env.JWT_SECRET!); + return jwt; +}; + +const generateHasuraClaimsForUser = (user: User) => ({ + "x-hasura-allowed-roles": getAllowedRolesForUser(user), + "x-hasura-default-role": getDefaultRoleForUser(user), + "x-hasura-user-id": user.id.toString(), +}); + +/** + * Get all possible roles for this user + * Requests made outside this scope will not be authorised by Hasura + */ +const getAllowedRolesForUser = (user: User): Role[] => { + const teamRoles = user.teams.map((teamRole) => teamRole.role); + const allowedRoles: Role[] = [ + "public", // Allow public access + "teamEditor", // Least privileged role for authenticated users - required for Editor access + ...teamRoles, // User specific roles + ]; + if (user.isPlatformAdmin) allowedRoles.push("platformAdmin"); + + return [...new Set(allowedRoles)]; +}; + +/** + * The default role is used for all requests + * Can be overwritten on a per-request basis in the client using the x-hasura-role header + * set to a role in the x-hasura-allowed-roles list + * + * This is the role of least privilege for the user + */ +const getDefaultRoleForUser = (user: User): Role => { + return user.isPlatformAdmin ? "platformAdmin" : "teamEditor"; +}; diff --git a/e2e/tests/api-driven/src/permissions/helpers.ts b/e2e/tests/api-driven/src/permissions/helpers.ts new file mode 100644 index 0000000000..903013a37e --- /dev/null +++ b/e2e/tests/api-driven/src/permissions/helpers.ts @@ -0,0 +1,61 @@ +import { $admin, getClient } from "../client"; +import { CustomWorld } from "./steps"; +import { queries } from "./queries"; +import { createFlow, createTeam, createUser } from "../globalHelpers"; + +interface PerformGQLQueryArgs { + world: CustomWorld; + action: string; + table: string; +} + +export const addUserToTeam = async (userId: number, teamId: number) => { + await $admin.team.addMember({ + userId, + teamId, + role: "teamEditor", + }); +}; + +export const cleanup = async () => { + await $admin.flow._destroyAll(); + await $admin.team._destroyAll(); + await $admin.user._destroyAll(); +}; + +export const setup = async () => { + const teamId1 = await createTeam({ name: "E2E Team 1", slug: "e2e-team1" }); + const teamId2 = await createTeam({ name: "E2E Team 2", slug: "e2e-team2" }); + const user1 = { + id: await createUser({ email: "e2e-user-1@opensystemslab.io" }), + email: "e2e-user-1@opensystemslab.io", + }; + const user2 = { + id: await createUser({ email: "e2e-user-2@opensystemslab.io" }), + email: "e2e-user-2@opensystemslab.io", + }; + const team1Flow = await createFlow({ teamId: teamId1, slug: "team-1-flow" }); + const team2Flow = await createFlow({ teamId: teamId2, slug: "team-2-flow" }); + + const world = { + teamId1, + teamId2, + user1, + user2, + team1Flow, + team2Flow, + }; + + return world; +}; + +export const performGQLQuery = async ({ + world, + action, + table, +}: PerformGQLQueryArgs) => { + const query = queries[table][action]; + const client = (await getClient(world.activeUser.email)).client; + const result = await client.request(query, { teamId1: world.teamId1 }); + return result; +}; diff --git a/e2e/tests/api-driven/src/permissions/queries/flows.ts b/e2e/tests/api-driven/src/permissions/queries/flows.ts new file mode 100644 index 0000000000..0427f8ec99 --- /dev/null +++ b/e2e/tests/api-driven/src/permissions/queries/flows.ts @@ -0,0 +1,17 @@ +import { gql } from "graphql-request"; + +export const INSERT_FLOW_QUERY = gql` + mutation InsertFlowE2E($teamId1: Int) { + insert_flows( + objects: { + data: "{hello: 'world'}" + slug: "e2e-test-flow" + team_id: $teamId1 + } + ) { + returning { + id + } + } + } +`; diff --git a/e2e/tests/api-driven/src/permissions/queries/index.ts b/e2e/tests/api-driven/src/permissions/queries/index.ts new file mode 100644 index 0000000000..19a6499ee2 --- /dev/null +++ b/e2e/tests/api-driven/src/permissions/queries/index.ts @@ -0,0 +1,7 @@ +import { INSERT_FLOW_QUERY } from "./flows"; + +export const queries = { + flows: { + insert: INSERT_FLOW_QUERY, + }, +}; diff --git a/e2e/tests/api-driven/src/permissions/steps.ts b/e2e/tests/api-driven/src/permissions/steps.ts new file mode 100644 index 0000000000..58ee8c34c2 --- /dev/null +++ b/e2e/tests/api-driven/src/permissions/steps.ts @@ -0,0 +1,94 @@ +import { After, Before, Given, Then, When, World } from "@cucumber/cucumber"; +import { strict as assert } from "node:assert"; +import { getUser } from "../globalHelpers"; +import { addUserToTeam, cleanup, performGQLQuery, setup } from "./helpers"; + +interface TestUser { + id: number; + email: string; +} + +export class CustomWorld extends World { + user1!: TestUser; + user2!: TestUser; + teamId1!: number; + teamId2!: number; + team1Flow!: string; + team2Flow!: string; + + error?: Error = undefined; + activeUser!: TestUser; +} + +Before("@team-admin-permissions", async function () { + const { user1, user2, teamId1, teamId2, team1Flow, team2Flow } = + await setup(); + this.user1 = user1; + this.user2 = user2; + this.teamId1 = teamId1; + this.teamId2 = teamId2; + this.team1Flow = team1Flow; + this.team2Flow = team2Flow; +}); + +After("@team-admin-permissions", async function () { + await cleanup(); +}); + +Given("a teamAdmin is a member of a team", async function (this: CustomWorld) { + await addUserToTeam(this.user1.id, this.teamId1); + const user = await getUser(this.user1.email); + + assert.ok(user, "User is not defined"); + assert.strictEqual(user.teams.length, 1); + assert.strictEqual(user.teams[0].role, "teamEditor"); + assert.strictEqual(user.teams[0].team.id, this.teamId1); + + this.activeUser = this.user1; +}); + +Given( + "a teamAdmin is not in the requested team", + async function (this: CustomWorld) { + await addUserToTeam(this.user2.id, this.teamId2); + const user = await getUser(this.user2.email); + + assert.ok(user, "User is not defined"); + assert.strictEqual(user.teams.length, 1); + assert.strictEqual(user.teams[0].role, "teamEditor"); + assert.strictEqual(user.teams[0].team.id, this.teamId2); + + this.activeUser = this.user2; + }, +); + +When( + "they perform {string} on {string}", + async function (this: CustomWorld, action: string, table: string) { + try { + await performGQLQuery({ + world: this, + action, + table, + }); + } catch (error) { + if (error instanceof Error) { + this.error = error; + return; + } + throw error; + } + }, +); + +Then("they have access", function (this: CustomWorld) { + if (this.error) { + assert.fail(`Permission query failed with error: ${this.error.message}`); + } +}); + +Then("they do not have access", function (this: CustomWorld) { + if (this.error) { + assert.ok(`Permission query failed with error: ${this.error.message}`); + } +}); diff --git a/e2e/tests/api-driven/src/permissions/teamAdmin.feature b/e2e/tests/api-driven/src/permissions/teamAdmin.feature new file mode 100644 index 0000000000..bc0b49d396 --- /dev/null +++ b/e2e/tests/api-driven/src/permissions/teamAdmin.feature @@ -0,0 +1,21 @@ +Feature: Testing Permissions for teamAdmin Role + + @regression @team-admin-permissions + Scenario Outline: teamAdmin permissions + Given a teamAdmin is a member of a team + When they perform "" on "" + Then they have access + + Examples: + | TABLE | ACTION | + | flows | insert | + + @regression @team-admin-permissions + Scenario Outline: teamAdmin permissions in a different team + Given a teamAdmin is not in the requested team + When they perform "" on "
" + Then they do not have access + + Examples: + | TABLE | ACTION | + | flows | insert | \ No newline at end of file diff --git a/e2e/tests/ui-driven/package.json b/e2e/tests/ui-driven/package.json index eead6768e2..f5bc9accbf 100644 --- a/e2e/tests/ui-driven/package.json +++ b/e2e/tests/ui-driven/package.json @@ -8,7 +8,7 @@ "postinstall": "./install-dependencies.sh" }, "dependencies": { - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#4e3d09f", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#44e9ebe", "axios": "^1.4.0", "dotenv": "^16.3.1", "eslint": "^8.44.0", diff --git a/e2e/tests/ui-driven/pnpm-lock.yaml b/e2e/tests/ui-driven/pnpm-lock.yaml index 07687e408f..097abc1b0b 100644 --- a/e2e/tests/ui-driven/pnpm-lock.yaml +++ b/e2e/tests/ui-driven/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: dependencies: '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#4e3d09f - version: github.com/theopensystemslab/planx-core/4e3d09f + specifier: git+https://github.com/theopensystemslab/planx-core#44e9ebe + version: github.com/theopensystemslab/planx-core/44e9ebe axios: specifier: ^1.4.0 version: 1.4.0 @@ -236,13 +236,13 @@ packages: eslint: 8.47.0 eslint-visitor-keys: 3.4.3 - /@eslint-community/eslint-utils@4.4.0(eslint@8.49.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.50.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.49.0 + eslint: 8.50.0 eslint-visitor-keys: 3.4.3 dev: false @@ -270,8 +270,8 @@ packages: resolution: {integrity: sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - /@eslint/js@8.49.0: - resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==} + /@eslint/js@8.50.0: + resolution: {integrity: sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: false @@ -311,6 +311,14 @@ packages: graphql: 16.8.0 dev: false + /@graphql-typed-document-node/core@3.2.0(graphql@16.8.1): + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + graphql: 16.8.1 + dev: false + /@humanwhocodes/config-array@0.11.10: resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} engines: {node: '>=10.10.0'} @@ -530,8 +538,8 @@ packages: resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} dev: false - /@types/geojson@7946.0.10: - resolution: {integrity: sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==} + /@types/geojson@7946.0.11: + resolution: {integrity: sha512-L7A0AINMXQpVwxHJ4jxD6/XjZ4NDufaRlUJHjNIFKYUFBH1SvOW+neaqb0VTRSLW5suSrSu19ObFEFnfNcr+qg==} dev: false /@types/glob@7.2.0: @@ -820,6 +828,14 @@ packages: is-wsl: 2.2.0 dev: false + /cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: false + /clsx@2.0.0: resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} engines: {node: '>=6'} @@ -885,6 +901,19 @@ packages: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} dev: false + /copyfiles@2.4.1: + resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} + hasBin: true + dependencies: + glob: 7.2.3 + minimatch: 3.1.2 + mkdirp: 1.0.4 + noms: 0.0.0 + through2: 2.0.5 + untildify: 4.0.0 + yargs: 16.2.0 + dev: false + /core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} dev: false @@ -1049,6 +1078,11 @@ packages: es6-symbol: 3.1.3 dev: false + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: false + /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -1126,15 +1160,15 @@ packages: transitivePeerDependencies: - supports-color - /eslint@8.49.0: - resolution: {integrity: sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==} + /eslint@8.50.0: + resolution: {integrity: sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) '@eslint-community/regexpp': 4.6.2 '@eslint/eslintrc': 2.1.2 - '@eslint/js': 8.49.0 + '@eslint/js': 8.50.0 '@humanwhocodes/config-array': 0.11.11 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -1243,8 +1277,8 @@ packages: punycode: 1.4.1 dev: false - /fast-xml-parser@4.2.7: - resolution: {integrity: sha512-J8r6BriSLO1uj2miOk1NW0YVm8AGOOu3Si2HQp/cSmo6EA4m3fcwu2WKjJ4RK9wMLBtg69y1kS8baDiQBR41Ig==} + /fast-xml-parser@4.3.1: + resolution: {integrity: sha512-viVv3xb8D+SiS1W4cv4tva3bni08kAkx0gQnWrykMM8nXPc1FxqZPU00dCEVjkiCg4HoXd2jC4x29Nzg/l2DAA==} hasBin: true dependencies: strnum: 1.0.5 @@ -1316,6 +1350,11 @@ packages: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: false + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: false + /get-stdin@8.0.0: resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} engines: {node: '>=10'} @@ -1373,11 +1412,28 @@ packages: - encoding dev: false + /graphql-request@6.1.0(graphql@16.8.1): + resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==} + peerDependencies: + graphql: 14 - 16 + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.1) + cross-fetch: 3.1.8 + graphql: 16.8.1 + transitivePeerDependencies: + - encoding + dev: false + /graphql@16.8.0: resolution: {integrity: sha512-0oKGaR+y3qcS5mCu1vb7KG+a89vjn06C7Ihq/dDl3jA+A8B3TKomvi3CiEcVLJQGalbu8F52LxkOym7U5sSfbg==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} dev: false + /graphql@16.8.1: + resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + dev: false + /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -1493,6 +1549,10 @@ packages: is-docker: 2.2.1 dev: false + /isarray@0.0.1: + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + dev: false + /isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} dev: false @@ -1792,6 +1852,13 @@ packages: whatwg-url: 5.0.0 dev: false + /noms@0.0.0: + resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} + dependencies: + inherits: 2.0.4 + readable-stream: 1.0.34 + dev: false + /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -1996,6 +2063,15 @@ packages: loose-envify: 1.4.0 dev: false + /readable-stream@1.0.34: + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 0.0.1 + string_decoder: 0.10.31 + dev: false + /readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} dependencies: @@ -2026,6 +2102,11 @@ packages: rc: 1.2.8 dev: false + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: false + /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -2159,6 +2240,10 @@ packages: strip-ansi: 7.1.0 dev: false + /string_decoder@0.10.31: + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} + dev: false + /string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: @@ -2192,6 +2277,10 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + /striptags@3.2.0: + resolution: {integrity: sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw==} + dev: false + /strnum@1.0.5: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} dev: false @@ -2234,6 +2323,13 @@ packages: any-promise: 1.3.0 dev: false + /through2@2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + dependencies: + readable-stream: 2.3.8 + xtend: 4.0.2 + dev: false + /timers-ext@0.1.7: resolution: {integrity: sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==} dependencies: @@ -2278,6 +2374,11 @@ packages: resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} dev: false + /untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + dev: false + /update-check@1.5.4: resolution: {integrity: sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==} dependencies: @@ -2338,6 +2439,15 @@ packages: string-width: 5.1.2 dev: false + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: false + /wrap-ansi@8.1.0: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} @@ -2361,6 +2471,16 @@ packages: resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==} dev: false + /xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: false + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: false + /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: false @@ -2370,6 +2490,24 @@ packages: engines: {node: '>= 6'} dev: false + /yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: false + + /yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + dev: false + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -2378,8 +2516,8 @@ packages: resolution: {integrity: sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==} dev: false - github.com/theopensystemslab/planx-core/4e3d09f: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/4e3d09f} + github.com/theopensystemslab/planx-core/44e9ebe: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/44e9ebe} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -2388,14 +2526,15 @@ packages: '@emotion/react': 11.11.1(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(react@18.2.0) '@mui/material': 5.14.10(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0) - '@types/geojson': 7946.0.10 + '@types/geojson': 7946.0.11 ajv: 8.12.0 ajv-formats: 2.1.1(ajv@8.12.0) + copyfiles: 2.4.1 docx: 8.2.2 - eslint: 8.49.0 - fast-xml-parser: 4.2.7 - graphql: 16.8.0 - graphql-request: 6.1.0(graphql@16.8.0) + eslint: 8.50.0 + fast-xml-parser: 4.3.1 + graphql: 16.8.1 + graphql-request: 6.1.0(graphql@16.8.1) json-schema-to-typescript: 13.1.1 lodash.capitalize: 4.2.1 lodash.get: 4.4.2 @@ -2411,6 +2550,7 @@ packages: prettier: 3.0.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) + striptags: 3.2.0 type-fest: 4.3.1 uuid: 9.0.1 zod: 3.22.2 diff --git a/e2e/tests/ui-driven/src/context.ts b/e2e/tests/ui-driven/src/context.ts index 3e547e86d0..d188eb4e51 100644 --- a/e2e/tests/ui-driven/src/context.ts +++ b/e2e/tests/ui-driven/src/context.ts @@ -127,7 +127,7 @@ export function getCoreDomainClient(): CoreDomainClient { ); const SECRET = process.env.HASURA_GRAPHQL_ADMIN_SECRET!; return new CoreDomainClient({ - hasuraSecret: SECRET, + auth: { adminSecret: SECRET }, targetURL: API, }); } diff --git a/editor.planx.uk/package.json b/editor.planx.uk/package.json index fc83e7bd3d..6b1bfe6e44 100644 --- a/editor.planx.uk/package.json +++ b/editor.planx.uk/package.json @@ -14,7 +14,7 @@ "@mui/styles": "^5.14.5", "@mui/utils": "^5.14.5", "@opensystemslab/map": "^0.7.5", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#7e31b2e", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#44e9ebe", "@tiptap/core": "^2.0.3", "@tiptap/extension-bold": "^2.0.3", "@tiptap/extension-bubble-menu": "^2.1.6", diff --git a/editor.planx.uk/pnpm-lock.yaml b/editor.planx.uk/pnpm-lock.yaml index 40c50f707d..6f78789582 100644 --- a/editor.planx.uk/pnpm-lock.yaml +++ b/editor.planx.uk/pnpm-lock.yaml @@ -46,8 +46,8 @@ dependencies: specifier: ^0.7.5 version: 0.7.5 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#7e31b2e - version: git/github.com+theopensystemslab/planx-core/7e31b2e(@types/react@18.2.20) + specifier: git+https://github.com/theopensystemslab/planx-core#44e9ebe + version: github.com/theopensystemslab/planx-core/44e9ebe(@types/react@18.2.20) '@tiptap/core': specifier: ^2.0.3 version: 2.0.3(@tiptap/pm@2.0.3) @@ -3828,7 +3828,7 @@ packages: '@emotion/core': ^10.0.28 react: '>=16.3.0' dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.22.15 '@emotion/core': 10.3.1(react@18.2.0) '@emotion/is-prop-valid': 0.8.8 '@emotion/serialize': 0.11.16 @@ -4317,13 +4317,13 @@ packages: eslint: 8.44.0 eslint-visitor-keys: 3.4.3 - /@eslint-community/eslint-utils@4.4.0(eslint@8.49.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.50.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.49.0 + eslint: 8.50.0 eslint-visitor-keys: 3.4.3 dev: false @@ -4373,8 +4373,8 @@ packages: resolution: {integrity: sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - /@eslint/js@8.49.0: - resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==} + /@eslint/js@8.50.0: + resolution: {integrity: sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: false @@ -5263,8 +5263,8 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.22.11 - '@mui/utils': 5.14.7(react@18.2.0) + '@babel/runtime': 7.22.15 + '@mui/utils': 5.14.10(@types/react@18.2.20)(react@18.2.0) '@types/react': 18.2.20 prop-types: 15.8.1 react: 18.2.0 @@ -5305,7 +5305,7 @@ packages: '@emotion/styled': optional: true dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.22.15 '@emotion/cache': 11.11.0 '@emotion/react': 11.11.1(@types/react@18.2.20)(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.20)(react@18.2.0) @@ -7784,8 +7784,8 @@ packages: resolution: {integrity: sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==} dev: true - /@types/geojson@7946.0.10: - resolution: {integrity: sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==} + /@types/geojson@7946.0.11: + resolution: {integrity: sha512-L7A0AINMXQpVwxHJ4jxD6/XjZ4NDufaRlUJHjNIFKYUFBH1SvOW+neaqb0VTRSLW5suSrSu19ObFEFnfNcr+qg==} dev: false /@types/glob@7.2.0: @@ -9050,7 +9050,7 @@ packages: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.22.15 cosmiconfig: 7.1.0 resolve: 1.22.2 @@ -10010,6 +10010,19 @@ packages: toggle-selection: 1.0.6 dev: false + /copyfiles@2.4.1: + resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} + hasBin: true + dependencies: + glob: 7.2.3 + minimatch: 3.1.2 + mkdirp: 1.0.4 + noms: 0.0.0 + through2: 2.0.5 + untildify: 4.0.0 + yargs: 16.2.0 + dev: false + /core-js-compat@3.31.1: resolution: {integrity: sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==} dependencies: @@ -10702,7 +10715,7 @@ packages: /dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.22.15 csstype: 3.1.2 /dom-serializer@0.2.2: @@ -11661,15 +11674,15 @@ packages: transitivePeerDependencies: - supports-color - /eslint@8.49.0: - resolution: {integrity: sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==} + /eslint@8.50.0: + resolution: {integrity: sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) '@eslint-community/regexpp': 4.6.2 '@eslint/eslintrc': 2.1.2 - '@eslint/js': 8.49.0 + '@eslint/js': 8.50.0 '@humanwhocodes/config-array': 0.11.11 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -11983,8 +11996,8 @@ packages: resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==} dev: false - /fast-xml-parser@4.2.7: - resolution: {integrity: sha512-J8r6BriSLO1uj2miOk1NW0YVm8AGOOu3Si2HQp/cSmo6EA4m3fcwu2WKjJ4RK9wMLBtg69y1kS8baDiQBR41Ig==} + /fast-xml-parser@4.3.1: + resolution: {integrity: sha512-viVv3xb8D+SiS1W4cv4tva3bni08kAkx0gQnWrykMM8nXPc1FxqZPU00dCEVjkiCg4HoXd2jC4x29Nzg/l2DAA==} hasBin: true dependencies: strnum: 1.0.5 @@ -13501,6 +13514,10 @@ packages: dependencies: is-docker: 2.2.1 + /isarray@0.0.1: + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + dev: false + /isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} @@ -15527,6 +15544,13 @@ packages: /node-releases@2.0.13: resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + /noms@0.0.0: + resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} + dependencies: + inherits: 2.0.4 + readable-stream: 1.0.34 + dev: false + /nopt@6.0.0: resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -17800,6 +17824,15 @@ packages: type-fest: 0.6.0 dev: true + /readable-stream@1.0.34: + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 0.0.1 + string_decoder: 0.10.31 + dev: false + /readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} dependencies: @@ -18978,6 +19011,10 @@ packages: define-properties: 1.2.0 es-abstract: 1.21.2 + /string_decoder@0.10.31: + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} + dev: false + /string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: @@ -19404,7 +19441,6 @@ packages: dependencies: readable-stream: 2.3.8 xtend: 4.0.2 - dev: true /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} @@ -19887,7 +19923,6 @@ packages: /untildify@4.0.0: resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} engines: {node: '>=8'} - dev: true /upath@1.2.0: resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} @@ -20650,7 +20685,6 @@ packages: /xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} - dev: true /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} @@ -20761,9 +20795,9 @@ packages: use-sync-external-store: 1.2.0(react@18.2.0) dev: false - git/github.com+theopensystemslab/planx-core/7e31b2e(@types/react@18.2.20): - resolution: {commit: 7e31b2e, repo: git@github.com:theopensystemslab/planx-core.git, type: git} - id: git@github.com+theopensystemslab/planx-core/7e31b2e + github.com/theopensystemslab/planx-core/44e9ebe(@types/react@18.2.20): + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/44e9ebe} + id: github.com/theopensystemslab/planx-core/44e9ebe name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -20772,12 +20806,13 @@ packages: '@emotion/react': 11.11.1(@types/react@18.2.20)(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.20)(react@18.2.0) '@mui/material': 5.14.10(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) - '@types/geojson': 7946.0.10 + '@types/geojson': 7946.0.11 ajv: 8.12.0 ajv-formats: 2.1.1(ajv@8.12.0) + copyfiles: 2.4.1 docx: 8.2.2 - eslint: 8.49.0 - fast-xml-parser: 4.2.7 + eslint: 8.50.0 + fast-xml-parser: 4.3.1 graphql: 16.8.1 graphql-request: 6.1.0(graphql@16.8.1) json-schema-to-typescript: 13.1.1