From 0c32d5f67e235bf34c3ecea4ef050689e7bf51cb Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 23 Jan 2024 20:56:12 -0600 Subject: [PATCH 01/43] test(integration): added a cucumber test for granting team access --- cucumber.mjs | 21 + package-lock.json | 2244 +++++++++++++++++ package.json | 12 +- .../step_definitions/common-steps.mjs | 73 + .../step_definitions/github-api-steps.mjs | 17 + .../features/step_definitions/team-steps.mjs | 49 + test/integration/features/teams.feature | 7 + 7 files changed, 2421 insertions(+), 2 deletions(-) create mode 100644 cucumber.mjs create mode 100644 test/integration/features/step_definitions/common-steps.mjs create mode 100644 test/integration/features/step_definitions/github-api-steps.mjs create mode 100644 test/integration/features/step_definitions/team-steps.mjs create mode 100644 test/integration/features/teams.feature diff --git a/cucumber.mjs b/cucumber.mjs new file mode 100644 index 0000000000..ea69900fbc --- /dev/null +++ b/cucumber.mjs @@ -0,0 +1,21 @@ +const base = { + formatOptions: { snippetInterface: 'async-await' }, + import: ['test/integration/features/**/*.mjs'] +} + +export default base + +export const wip = { + ...base, + tags: '@wip and not @skip' +} + +export const noWip = { + ...base, + tags: 'not @skip and not @wip' +} + +export const focus = { + ...base, + tags: '@focus' +} diff --git a/package-lock.json b/package-lock.json index b0371af02a..af31c37c4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "probot": "12.3.3" }, "devDependencies": { + "@cucumber/cucumber": "10.3.1", "@form8ion/remark-preset": "1.0.0", "@travi/any": "3.0.2", "http-status-codes": "2.3.0", @@ -21,6 +22,7 @@ "jest-when": "3.6.0", "lockfile-lint": "4.12.1", "ls-engines": "0.9.1", + "msw": "2.1.4", "nock": "13.5.0", "nodemon": "3.0.3", "npm-run-all2": "6.1.1", @@ -700,6 +702,499 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz", + "integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==", + "dev": true, + "dependencies": { + "cookie": "^0.5.0" + } + }, + "node_modules/@bundled-es-modules/cookie/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "dependencies": { + "statuses": "^2.0.1" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cucumber/ci-environment": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-10.0.0.tgz", + "integrity": "sha512-lRkiehckosIOdc7p1L44nZsttO5dVHFjpwKKWZ07x8SeoAdV/sPuGe1PISe0AmAowFGza62nMOgG4KaroGzwFQ==", + "dev": true + }, + "node_modules/@cucumber/cucumber": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-10.3.1.tgz", + "integrity": "sha512-0H0NkOXcYTCG1qCh3o0p1HPSMODGJmlHi1rm5yfoiMx5tJbBjxVNI2VVD2xtPWA+D6ehHQD9asewuzjMXrbPIw==", + "dev": true, + "dependencies": { + "@cucumber/ci-environment": "10.0.0", + "@cucumber/cucumber-expressions": "17.0.1", + "@cucumber/gherkin": "27.0.0", + "@cucumber/gherkin-streams": "5.0.1", + "@cucumber/gherkin-utils": "8.0.5", + "@cucumber/html-formatter": "21.2.0", + "@cucumber/message-streams": "4.0.1", + "@cucumber/messages": "24.0.1", + "@cucumber/tag-expressions": "6.1.0", + "assertion-error-formatter": "^3.0.0", + "capital-case": "^1.0.4", + "chalk": "^4.1.2", + "cli-table3": "0.6.3", + "commander": "^10.0.0", + "debug": "^4.3.4", + "error-stack-parser": "^2.1.4", + "figures": "^3.2.0", + "glob": "^10.3.10", + "has-ansi": "^4.0.1", + "indent-string": "^4.0.0", + "is-installed-globally": "^0.4.0", + "is-stream": "^2.0.0", + "knuth-shuffle-seeded": "^1.0.6", + "lodash.merge": "^4.6.2", + "lodash.mergewith": "^4.6.2", + "luxon": "3.2.1", + "mkdirp": "^2.1.5", + "mz": "^2.7.0", + "progress": "^2.0.3", + "read-pkg-up": "^7.0.1", + "resolve-pkg": "^2.0.0", + "semver": "7.5.3", + "string-argv": "0.3.1", + "strip-ansi": "6.0.1", + "supports-color": "^8.1.1", + "tmp": "^0.2.1", + "type-fest": "^4.8.3", + "util-arity": "^1.1.0", + "xmlbuilder": "^15.1.1", + "yaml": "^2.2.2", + "yup": "1.2.0" + }, + "bin": { + "cucumber-js": "bin/cucumber.js" + }, + "engines": { + "node": "18 || >=20" + } + }, + "node_modules/@cucumber/cucumber-expressions": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-17.0.1.tgz", + "integrity": "sha512-reR7/sNRmDWgdz8BtFuHEwpksPnAkHty7gxUC2n0iaUPmckv9G5I5i+Vonc6xwUHDb/hmHPz/DyUL+Iv4Ao96w==", + "dev": true, + "dependencies": { + "regexp-match-indices": "1.0.2" + } + }, + "node_modules/@cucumber/cucumber/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@cucumber/cucumber/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@cucumber/cucumber/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@cucumber/cucumber/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@cucumber/cucumber/node_modules/has-ansi": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-4.0.1.tgz", + "integrity": "sha512-Qr4RtTm30xvEdqUXbSBVWDu+PrTokJOwe/FU+VdfJPk+MXAPoeOzKpRyrDTnZIJwAkQ4oBLTU53nu0HrkF/Z2A==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@cucumber/cucumber/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@cucumber/cucumber/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@cucumber/cucumber/node_modules/mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@cucumber/cucumber/node_modules/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@cucumber/cucumber/node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/@cucumber/cucumber/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@cucumber/cucumber/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/@cucumber/cucumber/node_modules/type-fest": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.0.tgz", + "integrity": "sha512-NPaKJsb4wyJ16qc8zBQrWswLKv/YirgBFykvUQ1Iajt2wd+twC8E4hFXdlIXqiMl6kWA0zY8tUJ9ELVAdu5h7w==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@cucumber/cucumber/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@cucumber/cucumber/node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@cucumber/gherkin": { + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-27.0.0.tgz", + "integrity": "sha512-j5rCsjqzRiC3iVTier3sa0kzyNbkcAmF7xr7jKnyO7qDeK3Z8Ye1P3KSVpeQRMY+KCDJ3WbTDdyxH0FwfA/fIw==", + "dev": true, + "dependencies": { + "@cucumber/messages": ">=19.1.4 <=22" + } + }, + "node_modules/@cucumber/gherkin-streams": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin-streams/-/gherkin-streams-5.0.1.tgz", + "integrity": "sha512-/7VkIE/ASxIP/jd4Crlp4JHXqdNFxPGQokqWqsaCCiqBiu5qHoKMxcWNlp9njVL/n9yN4S08OmY3ZR8uC5x74Q==", + "dev": true, + "dependencies": { + "commander": "9.1.0", + "source-map-support": "0.5.21" + }, + "bin": { + "gherkin-javascript": "bin/gherkin" + }, + "peerDependencies": { + "@cucumber/gherkin": ">=22.0.0", + "@cucumber/message-streams": ">=4.0.0", + "@cucumber/messages": ">=17.1.1" + } + }, + "node_modules/@cucumber/gherkin-streams/node_modules/commander": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.1.0.tgz", + "integrity": "sha512-i0/MaqBtdbnJ4XQs4Pmyb+oFQl+q0lsAmokVUH92SlSw4fkeAcG3bVon+Qt7hmtF+u3Het6o4VgrcY3qAoEB6w==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/@cucumber/gherkin-streams/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@cucumber/gherkin-utils": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-8.0.5.tgz", + "integrity": "sha512-kxM1OCDjYddF26VKc892PF0GokW4wUIl1PUz3TIXsPZgS39ExM1pF8oww8mlGFD2B0+4op/cSE3SSIME5H3aNw==", + "dev": true, + "dependencies": { + "@cucumber/gherkin": "^26.0.0", + "@cucumber/messages": "^22.0.0", + "@teppeis/multimaps": "3.0.0", + "commander": "10.0.1", + "source-map-support": "^0.5.21" + }, + "bin": { + "gherkin-utils": "bin/gherkin-utils" + } + }, + "node_modules/@cucumber/gherkin-utils/node_modules/@cucumber/gherkin": { + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-26.2.0.tgz", + "integrity": "sha512-iRSiK8YAIHAmLrn/mUfpAx7OXZ7LyNlh1zT89RoziSVCbqSVDxJS6ckEzW8loxs+EEXl0dKPQOXiDmbHV+C/fA==", + "dev": true, + "dependencies": { + "@cucumber/messages": ">=19.1.4 <=22" + } + }, + "node_modules/@cucumber/gherkin-utils/node_modules/@cucumber/messages": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-22.0.0.tgz", + "integrity": "sha512-EuaUtYte9ilkxcKmfqGF9pJsHRUU0jwie5ukuZ/1NPTuHS1LxHPsGEODK17RPRbZHOFhqybNzG2rHAwThxEymg==", + "dev": true, + "dependencies": { + "@types/uuid": "9.0.1", + "class-transformer": "0.5.1", + "reflect-metadata": "0.1.13", + "uuid": "9.0.0" + } + }, + "node_modules/@cucumber/gherkin-utils/node_modules/@types/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==", + "dev": true + }, + "node_modules/@cucumber/gherkin-utils/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@cucumber/gherkin-utils/node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "node_modules/@cucumber/gherkin-utils/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@cucumber/gherkin-utils/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@cucumber/gherkin/node_modules/@cucumber/messages": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-22.0.0.tgz", + "integrity": "sha512-EuaUtYte9ilkxcKmfqGF9pJsHRUU0jwie5ukuZ/1NPTuHS1LxHPsGEODK17RPRbZHOFhqybNzG2rHAwThxEymg==", + "dev": true, + "dependencies": { + "@types/uuid": "9.0.1", + "class-transformer": "0.5.1", + "reflect-metadata": "0.1.13", + "uuid": "9.0.0" + } + }, + "node_modules/@cucumber/gherkin/node_modules/@types/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==", + "dev": true + }, + "node_modules/@cucumber/gherkin/node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "node_modules/@cucumber/gherkin/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@cucumber/html-formatter": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-21.2.0.tgz", + "integrity": "sha512-4OcSa12Y0v5e4ySDl67+QFTxCG/Y9fxGSkFqvm98ggpTvS7b75whwzupu+lM2lMBw+h3H6P8ZURQr0xQIAwE2A==", + "dev": true, + "peerDependencies": { + "@cucumber/messages": ">=18" + } + }, + "node_modules/@cucumber/message-streams": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/message-streams/-/message-streams-4.0.1.tgz", + "integrity": "sha512-Kxap9uP5jD8tHUZVjTWgzxemi/0uOsbGjd4LBOSxcJoOCRbESFwemUzilJuzNTB8pcTQUh8D5oudUyxfkJOKmA==", + "dev": true, + "peerDependencies": { + "@cucumber/messages": ">=17.1.1" + } + }, + "node_modules/@cucumber/messages": { + "version": "24.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-24.0.1.tgz", + "integrity": "sha512-dKfNkvgc6stSQIyeHk7p/221iqEZe1BP+e/Js8XKtSmc0sS8khKMvbSBwYVeonn/67/vYKiAyo6Eo0SzXd5Plw==", + "dev": true, + "dependencies": { + "@types/uuid": "9.0.7", + "class-transformer": "0.5.1", + "reflect-metadata": "0.2.1", + "uuid": "9.0.1" + } + }, + "node_modules/@cucumber/messages/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@cucumber/tag-expressions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-6.1.0.tgz", + "integrity": "sha512-+3DwRumrCJG27AtzCIL37A/X+A/gSfxOPLg8pZaruh5SLumsTmpvilwroVWBT2fPzmno/tGXypeK5a7NHU4RzA==", + "dev": true + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -1430,6 +1925,32 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mswjs/cookies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-1.1.0.tgz", + "integrity": "sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mswjs/interceptors": { + "version": "0.25.14", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.25.14.tgz", + "integrity": "sha512-2dnIxl+obqIqjoPXTFldhe6pcdOrqiz+GcLaQQ6hmL02OldAF7nIC+rUgTWm+iF6lvmyCVhFFqbgbapNhR8eag==", + "dev": true, + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.2.1", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2807,6 +3328,28 @@ "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-5.8.0.tgz", "integrity": "sha512-8adktjIb76A7viIdayQSFuBEwOzwhDC+9yxZpKNHjfzrlostHCw0/N7JWpWMObfElwvJMk2fY2l1noENCk9wmw==" }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -3023,6 +3566,15 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@teppeis/multimaps": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@teppeis/multimaps/-/multimaps-3.0.0.tgz", + "integrity": "sha512-ID7fosbc50TbT0MK0EG12O+gAP3W3Aa/Pz4DaTtQtEvlc9Odaqi0de+xuZ7Li2GtK4HzEX7IuRWS/JmZLksR3Q==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -3175,6 +3727,12 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -3424,6 +3982,12 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "node_modules/@types/statuses": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.4.tgz", + "integrity": "sha512-eqNDvZsCNY49OAXB0Firg/Sc2BgoWsntsLUdybGFOhAfCD6QJ2n9HXUIHGqt5qjrxmMv4wS8WLAw43ZkKcJ8Pw==", + "dev": true + }, "node_modules/@types/supports-color": { "version": "8.1.3", "resolved": "https://registry.npmjs.org/@types/supports-color/-/supports-color-8.1.3.tgz", @@ -3448,6 +4012,12 @@ "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", "dev": true }, + "node_modules/@types/uuid": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", + "integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==", + "dev": true + }, "node_modules/@types/yargs": { "version": "17.0.24", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", @@ -3721,6 +4291,12 @@ "node": ">=6" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -4054,6 +4630,17 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "node_modules/assertion-error-formatter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/assertion-error-formatter/-/assertion-error-formatter-3.0.0.tgz", + "integrity": "sha512-6YyAVLrEze0kQ7CmJfUgrLHb+Y7XghmL2Ie7ijVa2Y9ynP3LV+VDiwFk62Dn0qtqbmY0BT0ss6p1xxpiF2PYbQ==", + "dev": true, + "dependencies": { + "diff": "^4.0.1", + "pad-right": "^0.2.2", + "repeat-string": "^1.6.1" + } + }, "node_modules/astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -4279,6 +4866,55 @@ "node": ">=8" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -4646,6 +5282,17 @@ } ] }, + "node_modules/capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, "node_modules/ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", @@ -4805,6 +5452,12 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "dev": true + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -4825,6 +5478,33 @@ "node": ">=8" } }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, "node_modules/cli-truncate": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", @@ -4925,6 +5605,15 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, "node_modules/cluster-key-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", @@ -5275,6 +5964,18 @@ "node": ">=0.10.0" } }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-data-property": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", @@ -5645,6 +6346,15 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dev": true, + "dependencies": { + "stackframe": "^1.3.4" + } + }, "node_modules/es-abstract": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", @@ -8002,6 +8712,30 @@ "node": ">= 6" } }, + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dev": true, + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-dirs/node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -8211,6 +8945,12 @@ "node": ">= 0.4" } }, + "node_modules/headers-polyfill": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.2.tgz", + "integrity": "sha512-EWGTfnTqAO2L/j5HZgoM/3z82L7necsJ0pO9Tp0X1wil3PDLrkypTBRgVO2ExehEEvUycejZD3FuRaXpZZc3kw==", + "dev": true + }, "node_modules/hosted-git-info": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", @@ -8876,6 +9616,31 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-lambda": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", @@ -8909,6 +9674,12 @@ "integrity": "sha512-KUwX8L3QU5YheJw8FiFwBFf+gIZ3OX2wifID5sHjn1pWrFMfIaBZLSszs7vHtiJR9y7plGM+Y4jtfwrsCI1D8Q==", "dev": true }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -9098,6 +9869,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -10152,6 +10935,15 @@ "node": ">=6" } }, + "node_modules/knuth-shuffle-seeded": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz", + "integrity": "sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg==", + "dev": true, + "dependencies": { + "seed-random": "~2.2.0" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -10852,6 +11644,12 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true + }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -11035,6 +11833,15 @@ "loose-envify": "cli.js" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lru_map": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", @@ -11114,6 +11921,15 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/luxon": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.2.1.tgz", + "integrity": "sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -13593,6 +14409,127 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/msw": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.1.4.tgz", + "integrity": "sha512-YyIQpfLqAJf/O1kYPWBSbDqjgv71kRBmEbGLxkkai1Btcs/LcxKiAwT1My3COa9J/vTh9Ua41B/ReuiW9cXmkw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.0", + "@bundled-es-modules/statuses": "^1.0.1", + "@mswjs/cookies": "^1.1.0", + "@mswjs/interceptors": "^0.25.14", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "chalk": "^4.1.2", + "chokidar": "^3.4.2", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "inquirer": "^8.2.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.2", + "path-to-regexp": "^6.2.0", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.9.0", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.7.x <= 5.3.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/msw/node_modules/graphql": { + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", + "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/msw/node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/msw/node_modules/path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", + "dev": true + }, + "node_modules/msw/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/msw/node_modules/type-fest": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.0.tgz", + "integrity": "sha512-NPaKJsb4wyJ16qc8zBQrWswLKv/YirgBFykvUQ1Iajt2wd+twC8E4hFXdlIXqiMl6kWA0zY8tUJ9ELVAdu5h7w==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/msw/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/multimatch": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-3.0.0.tgz", @@ -13614,6 +14551,17 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/n-readlines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/n-readlines/-/n-readlines-1.0.0.tgz", @@ -13666,6 +14614,16 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/nock": { "version": "13.5.0", "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.0.tgz", @@ -14561,6 +15519,45 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -14570,6 +15567,12 @@ "node": ">=0.10.0" } }, + "node_modules/outvariant": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.2.tgz", + "integrity": "sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==", + "dev": true + }, "node_modules/p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -14694,6 +15697,18 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/pad-right": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz", + "integrity": "sha512-4cy8M95ioIGolCoMmm2cMntGR1lPLEbOMzOKu8bzjuJP6JpzEMQcDHmh7hHLYGgob+nKe1YHFMaG4V59HQa89g==", + "dev": true, + "dependencies": { + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -16030,6 +17045,12 @@ "node": ">= 8" } }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", + "dev": true + }, "node_modules/propose": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/propose/-/propose-0.0.5.tgz", @@ -16272,6 +17293,83 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg-up/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg-up/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/read-pkg/node_modules/hosted-git-info": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.0.tgz", @@ -16457,12 +17555,36 @@ "node": ">=4" } }, + "node_modules/reflect-metadata": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", + "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==", + "dev": true + }, "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "dev": true }, + "node_modules/regexp-match-indices": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz", + "integrity": "sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ==", + "dev": true, + "dependencies": { + "regexp-tree": "^0.1.11" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true, + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, "node_modules/regexp-util": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/regexp-util/-/regexp-util-1.2.2.tgz", @@ -20943,6 +22065,18 @@ "node": ">=8" } }, + "node_modules/resolve-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg/-/resolve-pkg-2.0.0.tgz", + "integrity": "sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", @@ -21122,6 +22256,12 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/seed-random": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", + "integrity": "sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ==", + "dev": true + }, "node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -21595,6 +22735,12 @@ "node": ">=8" } }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "dev": true + }, "node_modules/standard": { "version": "17.1.0", "resolved": "https://registry.npmjs.org/standard/-/standard-17.1.0.tgz", @@ -22168,6 +23314,12 @@ "node": ">= 0.4" } }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -22534,12 +23686,39 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "node_modules/tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==", + "dev": true + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -22587,6 +23766,12 @@ "node": ">=0.6" } }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", + "dev": true + }, "node_modules/touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", @@ -23679,6 +24864,15 @@ "dotenv": "*" } }, + "node_modules/upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -23697,6 +24891,12 @@ "node": ">=6" } }, + "node_modules/util-arity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/util-arity/-/util-arity-1.1.0.tgz", + "integrity": "sha512-kkyIsXKwemfSy8ZEoaIz06ApApnWsk5hQO0vLjZS6UkBiGiW++Jsyb8vSBoc0WKlffGoGs5yYy/j5pp8zckrFA==", + "dev": true + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -24289,6 +25489,15 @@ "makeerror": "1.0.12" } }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -24489,6 +25698,15 @@ "node": ">=8" } }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "engines": { + "node": ">=8.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -24581,6 +25799,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yup": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.2.0.tgz", + "integrity": "sha512-PPqYKSAXjpRCgLgLKVGPA33v5c/WgEx3wi6NFjIiegz90zSwyMpvTFp/uGcVnnbx6to28pgnzp/q8ih3QRjLMQ==", + "dev": true, + "dependencies": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } + }, + "node_modules/yup/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", @@ -25108,6 +26350,402 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@bundled-es-modules/cookie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz", + "integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==", + "dev": true, + "requires": { + "cookie": "^0.5.0" + }, + "dependencies": { + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true + } + } + }, + "@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "requires": { + "statuses": "^2.0.1" + } + }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true + }, + "@cucumber/ci-environment": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-10.0.0.tgz", + "integrity": "sha512-lRkiehckosIOdc7p1L44nZsttO5dVHFjpwKKWZ07x8SeoAdV/sPuGe1PISe0AmAowFGza62nMOgG4KaroGzwFQ==", + "dev": true + }, + "@cucumber/cucumber": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-10.3.1.tgz", + "integrity": "sha512-0H0NkOXcYTCG1qCh3o0p1HPSMODGJmlHi1rm5yfoiMx5tJbBjxVNI2VVD2xtPWA+D6ehHQD9asewuzjMXrbPIw==", + "dev": true, + "requires": { + "@cucumber/ci-environment": "10.0.0", + "@cucumber/cucumber-expressions": "17.0.1", + "@cucumber/gherkin": "27.0.0", + "@cucumber/gherkin-streams": "5.0.1", + "@cucumber/gherkin-utils": "8.0.5", + "@cucumber/html-formatter": "21.2.0", + "@cucumber/message-streams": "4.0.1", + "@cucumber/messages": "24.0.1", + "@cucumber/tag-expressions": "6.1.0", + "assertion-error-formatter": "^3.0.0", + "capital-case": "^1.0.4", + "chalk": "^4.1.2", + "cli-table3": "0.6.3", + "commander": "^10.0.0", + "debug": "^4.3.4", + "error-stack-parser": "^2.1.4", + "figures": "^3.2.0", + "glob": "^10.3.10", + "has-ansi": "^4.0.1", + "indent-string": "^4.0.0", + "is-installed-globally": "^0.4.0", + "is-stream": "^2.0.0", + "knuth-shuffle-seeded": "^1.0.6", + "lodash.merge": "^4.6.2", + "lodash.mergewith": "^4.6.2", + "luxon": "3.2.1", + "mkdirp": "^2.1.5", + "mz": "^2.7.0", + "progress": "^2.0.3", + "read-pkg-up": "^7.0.1", + "resolve-pkg": "^2.0.0", + "semver": "7.5.3", + "string-argv": "0.3.1", + "strip-ansi": "6.0.1", + "supports-color": "^8.1.1", + "tmp": "^0.2.1", + "type-fest": "^4.8.3", + "util-arity": "^1.1.0", + "xmlbuilder": "^15.1.1", + "yaml": "^2.2.2", + "yup": "1.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true + }, + "glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + } + }, + "has-ansi": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-4.0.1.tgz", + "integrity": "sha512-Qr4RtTm30xvEdqUXbSBVWDu+PrTokJOwe/FU+VdfJPk+MXAPoeOzKpRyrDTnZIJwAkQ4oBLTU53nu0HrkF/Z2A==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", + "dev": true + }, + "semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "type-fest": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.0.tgz", + "integrity": "sha512-NPaKJsb4wyJ16qc8zBQrWswLKv/YirgBFykvUQ1Iajt2wd+twC8E4hFXdlIXqiMl6kWA0zY8tUJ9ELVAdu5h7w==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true + } + } + }, + "@cucumber/cucumber-expressions": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-17.0.1.tgz", + "integrity": "sha512-reR7/sNRmDWgdz8BtFuHEwpksPnAkHty7gxUC2n0iaUPmckv9G5I5i+Vonc6xwUHDb/hmHPz/DyUL+Iv4Ao96w==", + "dev": true, + "requires": { + "regexp-match-indices": "1.0.2" + } + }, + "@cucumber/gherkin": { + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-27.0.0.tgz", + "integrity": "sha512-j5rCsjqzRiC3iVTier3sa0kzyNbkcAmF7xr7jKnyO7qDeK3Z8Ye1P3KSVpeQRMY+KCDJ3WbTDdyxH0FwfA/fIw==", + "dev": true, + "requires": { + "@cucumber/messages": ">=19.1.4 <=22" + }, + "dependencies": { + "@cucumber/messages": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-22.0.0.tgz", + "integrity": "sha512-EuaUtYte9ilkxcKmfqGF9pJsHRUU0jwie5ukuZ/1NPTuHS1LxHPsGEODK17RPRbZHOFhqybNzG2rHAwThxEymg==", + "dev": true, + "requires": { + "@types/uuid": "9.0.1", + "class-transformer": "0.5.1", + "reflect-metadata": "0.1.13", + "uuid": "9.0.0" + } + }, + "@types/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==", + "dev": true + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "dev": true + } + } + }, + "@cucumber/gherkin-streams": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin-streams/-/gherkin-streams-5.0.1.tgz", + "integrity": "sha512-/7VkIE/ASxIP/jd4Crlp4JHXqdNFxPGQokqWqsaCCiqBiu5qHoKMxcWNlp9njVL/n9yN4S08OmY3ZR8uC5x74Q==", + "dev": true, + "requires": { + "commander": "9.1.0", + "source-map-support": "0.5.21" + }, + "dependencies": { + "commander": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.1.0.tgz", + "integrity": "sha512-i0/MaqBtdbnJ4XQs4Pmyb+oFQl+q0lsAmokVUH92SlSw4fkeAcG3bVon+Qt7hmtF+u3Het6o4VgrcY3qAoEB6w==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "@cucumber/gherkin-utils": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-8.0.5.tgz", + "integrity": "sha512-kxM1OCDjYddF26VKc892PF0GokW4wUIl1PUz3TIXsPZgS39ExM1pF8oww8mlGFD2B0+4op/cSE3SSIME5H3aNw==", + "dev": true, + "requires": { + "@cucumber/gherkin": "^26.0.0", + "@cucumber/messages": "^22.0.0", + "@teppeis/multimaps": "3.0.0", + "commander": "10.0.1", + "source-map-support": "^0.5.21" + }, + "dependencies": { + "@cucumber/gherkin": { + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-26.2.0.tgz", + "integrity": "sha512-iRSiK8YAIHAmLrn/mUfpAx7OXZ7LyNlh1zT89RoziSVCbqSVDxJS6ckEzW8loxs+EEXl0dKPQOXiDmbHV+C/fA==", + "dev": true, + "requires": { + "@cucumber/messages": ">=19.1.4 <=22" + } + }, + "@cucumber/messages": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-22.0.0.tgz", + "integrity": "sha512-EuaUtYte9ilkxcKmfqGF9pJsHRUU0jwie5ukuZ/1NPTuHS1LxHPsGEODK17RPRbZHOFhqybNzG2rHAwThxEymg==", + "dev": true, + "requires": { + "@types/uuid": "9.0.1", + "class-transformer": "0.5.1", + "reflect-metadata": "0.1.13", + "uuid": "9.0.0" + } + }, + "@types/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==", + "dev": true + }, + "commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "dev": true + } + } + }, + "@cucumber/html-formatter": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-21.2.0.tgz", + "integrity": "sha512-4OcSa12Y0v5e4ySDl67+QFTxCG/Y9fxGSkFqvm98ggpTvS7b75whwzupu+lM2lMBw+h3H6P8ZURQr0xQIAwE2A==", + "dev": true, + "requires": {} + }, + "@cucumber/message-streams": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/message-streams/-/message-streams-4.0.1.tgz", + "integrity": "sha512-Kxap9uP5jD8tHUZVjTWgzxemi/0uOsbGjd4LBOSxcJoOCRbESFwemUzilJuzNTB8pcTQUh8D5oudUyxfkJOKmA==", + "dev": true, + "requires": {} + }, + "@cucumber/messages": { + "version": "24.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-24.0.1.tgz", + "integrity": "sha512-dKfNkvgc6stSQIyeHk7p/221iqEZe1BP+e/Js8XKtSmc0sS8khKMvbSBwYVeonn/67/vYKiAyo6Eo0SzXd5Plw==", + "dev": true, + "requires": { + "@types/uuid": "9.0.7", + "class-transformer": "0.5.1", + "reflect-metadata": "0.2.1", + "uuid": "9.0.1" + }, + "dependencies": { + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true + } + } + }, + "@cucumber/tag-expressions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-6.1.0.tgz", + "integrity": "sha512-+3DwRumrCJG27AtzCIL37A/X+A/gSfxOPLg8pZaruh5SLumsTmpvilwroVWBT2fPzmno/tGXypeK5a7NHU4RzA==", + "dev": true + }, "@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -25664,6 +27302,26 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "@mswjs/cookies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-1.1.0.tgz", + "integrity": "sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw==", + "dev": true + }, + "@mswjs/interceptors": { + "version": "0.25.14", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.25.14.tgz", + "integrity": "sha512-2dnIxl+obqIqjoPXTFldhe6pcdOrqiz+GcLaQQ6hmL02OldAF7nIC+rUgTWm+iF6lvmyCVhFFqbgbapNhR8eag==", + "dev": true, + "requires": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.2.1", + "strict-event-emitter": "^0.5.1" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -26832,6 +28490,28 @@ "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-5.8.0.tgz", "integrity": "sha512-8adktjIb76A7viIdayQSFuBEwOzwhDC+9yxZpKNHjfzrlostHCw0/N7JWpWMObfElwvJMk2fY2l1noENCk9wmw==" }, + "@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true + }, + "@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "requires": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true + }, "@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -27016,6 +28696,12 @@ "@sinonjs/commons": "^3.0.0" } }, + "@teppeis/multimaps": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@teppeis/multimaps/-/multimaps-3.0.0.tgz", + "integrity": "sha512-ID7fosbc50TbT0MK0EG12O+gAP3W3Aa/Pz4DaTtQtEvlc9Odaqi0de+xuZ7Li2GtK4HzEX7IuRWS/JmZLksR3Q==", + "dev": true + }, "@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -27149,6 +28835,12 @@ "@types/node": "*" } }, + "@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true + }, "@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -27396,6 +29088,12 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "@types/statuses": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.4.tgz", + "integrity": "sha512-eqNDvZsCNY49OAXB0Firg/Sc2BgoWsntsLUdybGFOhAfCD6QJ2n9HXUIHGqt5qjrxmMv4wS8WLAw43ZkKcJ8Pw==", + "dev": true + }, "@types/supports-color": { "version": "8.1.3", "resolved": "https://registry.npmjs.org/@types/supports-color/-/supports-color-8.1.3.tgz", @@ -27420,6 +29118,12 @@ "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", "dev": true }, + "@types/uuid": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", + "integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==", + "dev": true + }, "@types/yargs": { "version": "17.0.24", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", @@ -27628,6 +29332,12 @@ "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", "dev": true }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, "anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -27882,6 +29592,17 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "assertion-error-formatter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/assertion-error-formatter/-/assertion-error-formatter-3.0.0.tgz", + "integrity": "sha512-6YyAVLrEze0kQ7CmJfUgrLHb+Y7XghmL2Ie7ijVa2Y9ynP3LV+VDiwFk62Dn0qtqbmY0BT0ss6p1xxpiF2PYbQ==", + "dev": true, + "requires": { + "diff": "^4.0.1", + "pad-right": "^0.2.2", + "repeat-string": "^1.6.1" + } + }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -28042,6 +29763,40 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -28308,6 +30063,17 @@ "integrity": "sha512-9aY/b05NKU4Yl2sbcJhn4A7MsGwR1EPfW/nrqsnqVA0Oq50wpmPaGI+R1Z0UKlUl96oxUkGEOILWtOHck0eCWw==", "dev": true }, + "capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, "ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", @@ -28415,6 +30181,12 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, + "class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "dev": true + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -28429,6 +30201,22 @@ "restore-cursor": "^3.1.0" } }, + "cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true + }, + "cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "dev": true, + "requires": { + "@colors/colors": "1.5.0", + "string-width": "^4.2.0" + } + }, "cli-truncate": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", @@ -28506,6 +30294,12 @@ } } }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true + }, "cluster-key-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", @@ -28766,6 +30560,15 @@ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" }, + "defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, "define-data-property": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", @@ -29060,6 +30863,15 @@ "is-arrayish": "^0.2.1" } }, + "error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dev": true, + "requires": { + "stackframe": "^1.3.4" + } + }, "es-abstract": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", @@ -30919,6 +32731,23 @@ "is-glob": "^4.0.1" } }, + "global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dev": true, + "requires": { + "ini": "2.0.0" + }, + "dependencies": { + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true + } + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -31065,6 +32894,12 @@ "function-bind": "^1.1.2" } }, + "headers-polyfill": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.2.tgz", + "integrity": "sha512-EWGTfnTqAO2L/j5HZgoM/3z82L7necsJ0pO9Tp0X1wil3PDLrkypTBRgVO2ExehEEvUycejZD3FuRaXpZZc3kw==", + "dev": true + }, "hosted-git-info": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", @@ -31544,6 +33379,22 @@ "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", "dev": true }, + "is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "requires": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, "is-lambda": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", @@ -31568,6 +33419,12 @@ "integrity": "sha512-KUwX8L3QU5YheJw8FiFwBFf+gIZ3OX2wifID5sHjn1pWrFMfIaBZLSszs7vHtiJR9y7plGM+Y4jtfwrsCI1D8Q==", "dev": true }, + "is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -31691,6 +33548,12 @@ "which-typed-array": "^1.1.11" } }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -32502,6 +34365,15 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "knuth-shuffle-seeded": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz", + "integrity": "sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg==", + "dev": true, + "requires": { + "seed-random": "~2.2.0" + } + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -33053,6 +34925,12 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true + }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -33200,6 +35078,15 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, "lru_map": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", @@ -33265,6 +35152,12 @@ } } }, + "luxon": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.2.1.tgz", + "integrity": "sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg==", + "dev": true + }, "make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -34983,6 +36876,95 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "msw": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.1.4.tgz", + "integrity": "sha512-YyIQpfLqAJf/O1kYPWBSbDqjgv71kRBmEbGLxkkai1Btcs/LcxKiAwT1My3COa9J/vTh9Ua41B/ReuiW9cXmkw==", + "dev": true, + "requires": { + "@bundled-es-modules/cookie": "^2.0.0", + "@bundled-es-modules/statuses": "^1.0.1", + "@mswjs/cookies": "^1.1.0", + "@mswjs/interceptors": "^0.25.14", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "chalk": "^4.1.2", + "chokidar": "^3.4.2", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "inquirer": "^8.2.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.2", + "path-to-regexp": "^6.2.0", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.9.0", + "yargs": "^17.7.2" + }, + "dependencies": { + "graphql": { + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", + "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", + "dev": true + }, + "inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + } + }, + "path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", + "dev": true + }, + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, + "type-fest": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.0.tgz", + "integrity": "sha512-NPaKJsb4wyJ16qc8zBQrWswLKv/YirgBFykvUQ1Iajt2wd+twC8E4hFXdlIXqiMl6kWA0zY8tUJ9ELVAdu5h7w==", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, "multimatch": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-3.0.0.tgz", @@ -35001,6 +36983,17 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "n-readlines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/n-readlines/-/n-readlines-1.0.0.tgz", @@ -35035,6 +37028,16 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "nock": { "version": "13.5.0", "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.0.tgz", @@ -35710,12 +37713,47 @@ "word-wrap": "~1.2.3" } }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + } + } + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true }, + "outvariant": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.2.tgz", + "integrity": "sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==", + "dev": true + }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -35803,6 +37841,15 @@ "tar": "^6.1.11" } }, + "pad-right": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz", + "integrity": "sha512-4cy8M95ioIGolCoMmm2cMntGR1lPLEbOMzOKu8bzjuJP6JpzEMQcDHmh7hHLYGgob+nKe1YHFMaG4V59HQa89g==", + "dev": true, + "requires": { + "repeat-string": "^1.5.2" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -36861,6 +38908,12 @@ "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", "dev": true }, + "property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", + "dev": true + }, "propose": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/propose/-/propose-0.0.5.tgz", @@ -37123,6 +39176,69 @@ } } }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, "readable-stream": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.0.tgz", @@ -37174,12 +39290,33 @@ "redis-errors": "^1.0.0" } }, + "reflect-metadata": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", + "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==", + "dev": true + }, "regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "dev": true }, + "regexp-match-indices": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz", + "integrity": "sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ==", + "dev": true, + "requires": { + "regexp-tree": "^0.1.11" + } + }, + "regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true + }, "regexp-util": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/regexp-util/-/regexp-util-1.2.2.tgz", @@ -40449,6 +42586,15 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, + "resolve-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg/-/resolve-pkg-2.0.0.tgz", + "integrity": "sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, "resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", @@ -40565,6 +42711,12 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "seed-random": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", + "integrity": "sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -40956,6 +43108,12 @@ } } }, + "stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "dev": true + }, "standard": { "version": "17.1.0", "resolved": "https://registry.npmjs.org/standard/-/standard-17.1.0.tgz", @@ -41312,6 +43470,12 @@ "internal-slot": "^1.0.4" } }, + "strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -41588,12 +43752,36 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==", + "dev": true + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -41629,6 +43817,12 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, + "toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", + "dev": true + }, "touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", @@ -42427,6 +44621,15 @@ "integrity": "sha512-3cIC18In/t0X/yH793c00qqxcKD8jVCgNOPif/fGQkFpYMGecM9YAc+kaAKXuZsM2dE9I9wFI7KvAuNX22SGMQ==", "requires": {} }, + "upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -42444,6 +44647,12 @@ } } }, + "util-arity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/util-arity/-/util-arity-1.1.0.tgz", + "integrity": "sha512-kkyIsXKwemfSy8ZEoaIz06ApApnWsk5hQO0vLjZS6UkBiGiW++Jsyb8vSBoc0WKlffGoGs5yYy/j5pp8zckrFA==", + "dev": true + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -42899,6 +45108,15 @@ "makeerror": "1.0.12" } }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -43052,6 +45270,12 @@ "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true }, + "xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -43122,6 +45346,26 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true }, + "yup": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.2.0.tgz", + "integrity": "sha512-PPqYKSAXjpRCgLgLKVGPA33v5c/WgEx3wi6NFjIiegz90zSwyMpvTFp/uGcVnnbx6to28pgnzp/q8ih3QRjLMQ==", + "dev": true, + "requires": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + }, + "dependencies": { + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true + } + } + }, "zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index bd04fbd8e6..e56a6b43d5 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,14 @@ "lint:peer": "npm ls >/dev/null", "test:unit": "jest 'test/unit/'", "test:unit:watch": "npm run test:unit -- --watch", - "test:integration": "jest 'test/integration/'", - "test:integration:debug": "LOG_LEVEL=debug DEBUG=nock.* run-s test:integration", + "test:integration": "npm-run-all --print-label --parallel test:integration:jest test:integration:cucumber", + "test:integration:jest": "jest 'test/integration/'", + "test:integration:cucumber": "run-s 'test:integration:base -- --profile noWip'", + "test:integration:base": "NODE_OPTIONS=--enable-source-maps DEBUG=any cucumber-js test/integration", + "test:integration:debug": "DEBUG=test run-s test:integration", + "test:integration:wip": "run-s 'test:integration:base -- --profile wip'", + "test:integration:wip:debug": "DEBUG=test run-s 'test:integration:wip'", + "test:integration:focus": "run-s 'test:integration:base -- --profile focus'", "generate:md": "remark . --output" }, "author": "Brandon Keepers", @@ -27,6 +33,7 @@ "probot": "12.3.3" }, "devDependencies": { + "@cucumber/cucumber": "10.3.1", "@form8ion/remark-preset": "1.0.0", "@travi/any": "3.0.2", "http-status-codes": "2.3.0", @@ -34,6 +41,7 @@ "jest-when": "3.6.0", "lockfile-lint": "4.12.1", "ls-engines": "0.9.1", + "msw": "2.1.4", "nock": "13.5.0", "nodemon": "3.0.3", "npm-run-all2": "6.1.1", diff --git a/test/integration/features/step_definitions/common-steps.mjs b/test/integration/features/step_definitions/common-steps.mjs new file mode 100644 index 0000000000..dd081c4852 --- /dev/null +++ b/test/integration/features/step_definitions/common-steps.mjs @@ -0,0 +1,73 @@ +import any from '@travi/any' +import { Before, When } from '@cucumber/cucumber' + +import settings from '../../../../lib/settings.js' +import { Probot, ProbotOctokit } from 'probot' +import settingsBot from '../../../../index.js' + +let probot + +export const repository = { + default_branch: 'master', + name: 'botland', + owner: { + name: 'bkeepers-inc', + login: 'bkeepers-inc', + email: null + } +} + +async function loadInstance () { + const probot = new Probot({ + appId: 1, + privateKey: 'test', + githubToken: 'test', + Octokit: ProbotOctokit.defaults({ + retry: { enabled: false }, + throttle: { enabled: false } + }) + }) + await probot.load(settingsBot) + + return probot +} + +function buildPushEvent () { + return { + name: 'push', + payload: { + ref: 'refs/heads/master', + repository, + commits: [{ modified: [settings.FILE_NAME], added: [] }] + } + } +} + +function buildRepositoryEditedEvent () { + return { + name: 'repository.edited', + payload: { + changes: { default_branch: { from: any.word() } }, + repository + } + } +} + +function buildRepositoryCreatedEvent () { + return { + name: 'repository.created', + payload: { repository } + } +} + +function buildTriggerEvent () { + return any.fromList([buildPushEvent(), buildRepositoryCreatedEvent(), buildRepositoryEditedEvent()]) +} + +Before(async function () { + probot = await loadInstance() +}) + +When('a settings sync is triggered', async function () { + await probot.receive(buildTriggerEvent()) +}) diff --git a/test/integration/features/step_definitions/github-api-steps.mjs b/test/integration/features/step_definitions/github-api-steps.mjs new file mode 100644 index 0000000000..a20d66e113 --- /dev/null +++ b/test/integration/features/step_definitions/github-api-steps.mjs @@ -0,0 +1,17 @@ +import { AfterAll, Before, BeforeAll } from '@cucumber/cucumber' +import { setupServer } from 'msw/node' +import any from '@travi/any' + +const server = setupServer() +export const githubToken = any.word() +BeforeAll(async function () { + server.listen() +}) + +Before(function () { + this.server = server +}) + +AfterAll(function () { + server.close() +}) diff --git a/test/integration/features/step_definitions/team-steps.mjs b/test/integration/features/step_definitions/team-steps.mjs new file mode 100644 index 0000000000..42be136577 --- /dev/null +++ b/test/integration/features/step_definitions/team-steps.mjs @@ -0,0 +1,49 @@ +import { dump } from 'js-yaml' +import { StatusCodes } from 'http-status-codes' +import assert from 'node:assert' + +import { Given, Then } from '@cucumber/cucumber' +import { http, HttpResponse } from 'msw' +import any from '@travi/any' + +import { repository } from './common-steps.mjs' +import settings from '../../../../lib/settings.js' + +Given('no team has been granted access to the repository', async function () { + this.server.use( + http.get(`https://api.github.com/repos/${repository.owner.name}/${repository.name}/teams`, ({ request }) => { + return HttpResponse.json([]) + }) + ) +}) + +Given('a team is granted {string} privileges in the config', async function (accessLevel) { + const teamName = any.word() + const teamId = any.integer() + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer(Buffer.from(dump({ teams: [{ name: teamName, permission: accessLevel }] }))) + } + ), + http.get(`https://api.github.com/orgs/${repository.owner.name}/teams/${teamName}`, ({ request }) => { + return HttpResponse.json({ id: teamId }) + }), + http.put( + `https://api.github.com/teams/${teamId}/repos/${repository.owner.name}/${repository.name}`, + async ({ request }) => { + this.teamPermissionLevel = (await request.json()).permission + + return new HttpResponse(null, { status: StatusCodes.CREATED }) + } + ) + ) +}) + +Then('the team has {string} access granted to it', async function (accessLevel) { + assert.equal(this.teamPermissionLevel, accessLevel) +}) diff --git a/test/integration/features/teams.feature b/test/integration/features/teams.feature new file mode 100644 index 0000000000..a68a0b5a1a --- /dev/null +++ b/test/integration/features/teams.feature @@ -0,0 +1,7 @@ +Feature: Teams + + Scenario: Grant Team Access + Given no team has been granted access to the repository + And a team is granted "push" privileges in the config + When a settings sync is triggered + Then the team has "push" access granted to it From 14ba5f75c713bf8e872dc89601bcb71d21c74de8 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 23 Jan 2024 21:23:52 -0600 Subject: [PATCH 02/43] test(integration): added a cucumber test for updating team access --- .../features/step_definitions/team-steps.mjs | 38 +++++++++++++++++-- test/integration/features/teams.feature | 10 +++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/test/integration/features/step_definitions/team-steps.mjs b/test/integration/features/step_definitions/team-steps.mjs index 42be136577..abfe5d6f82 100644 --- a/test/integration/features/step_definitions/team-steps.mjs +++ b/test/integration/features/step_definitions/team-steps.mjs @@ -9,6 +9,9 @@ import any from '@travi/any' import { repository } from './common-steps.mjs' import settings from '../../../../lib/settings.js' +const teamName = any.word() +const teamId = any.integer() + Given('no team has been granted access to the repository', async function () { this.server.use( http.get(`https://api.github.com/repos/${repository.owner.name}/${repository.name}/teams`, ({ request }) => { @@ -17,10 +20,15 @@ Given('no team has been granted access to the repository', async function () { ) }) -Given('a team is granted {string} privileges in the config', async function (accessLevel) { - const teamName = any.word() - const teamId = any.integer() +Given('a team has been granted {string} privileges to the repository', async function (accessLevel) { + this.server.use( + http.get(`https://api.github.com/repos/${repository.owner.name}/${repository.name}/teams`, ({ request }) => { + return HttpResponse.json([{ slug: teamName, id: teamId, permission: accessLevel }]) + }) + ) +}) +Given('a team is granted {string} privileges in the config', async function (accessLevel) { this.server.use( http.get( `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( @@ -44,6 +52,30 @@ Given('a team is granted {string} privileges in the config', async function (acc ) }) +Given('the team privileges are updated to {string} in the config', async function (accessLevel) { + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer(Buffer.from(dump({ teams: [{ name: teamName, permission: accessLevel }] }))) + } + ), + http.get(`https://api.github.com/orgs/${repository.owner.name}/teams/${teamName}`, ({ request }) => { + return HttpResponse.json({ id: teamId }) + }), + http.put( + `https://api.github.com/teams/${teamId}/repos/${repository.owner.name}/${repository.name}`, + async ({ request }) => { + this.teamPermissionLevel = (await request.json()).permission + + return new HttpResponse(null, { status: StatusCodes.OK }) + } + ) + ) +}) + Then('the team has {string} access granted to it', async function (accessLevel) { assert.equal(this.teamPermissionLevel, accessLevel) }) diff --git a/test/integration/features/teams.feature b/test/integration/features/teams.feature index a68a0b5a1a..7e3068ed75 100644 --- a/test/integration/features/teams.feature +++ b/test/integration/features/teams.feature @@ -5,3 +5,13 @@ Feature: Teams And a team is granted "push" privileges in the config When a settings sync is triggered Then the team has "push" access granted to it + + Scenario: Update Team Access + Given a team has been granted "push" privileges to the repository + And the team privileges are updated to "admin" in the config + When a settings sync is triggered + Then the team has "admin" access granted to it + + @wip + Scenario: Remove Team Access + When a settings sync is triggered From 0e0655aac47964ce2bda67054da0a14136fdc009 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 23 Jan 2024 21:54:10 -0600 Subject: [PATCH 03/43] test(integration): added a cucumber test for revoking team access --- .../features/step_definitions/team-steps.mjs | 28 +++++++++++++++++-- test/integration/features/teams.feature | 4 ++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/test/integration/features/step_definitions/team-steps.mjs b/test/integration/features/step_definitions/team-steps.mjs index abfe5d6f82..dfa06e58ae 100644 --- a/test/integration/features/step_definitions/team-steps.mjs +++ b/test/integration/features/step_definitions/team-steps.mjs @@ -62,9 +62,6 @@ Given('the team privileges are updated to {string} in the config', async functio return HttpResponse.arrayBuffer(Buffer.from(dump({ teams: [{ name: teamName, permission: accessLevel }] }))) } ), - http.get(`https://api.github.com/orgs/${repository.owner.name}/teams/${teamName}`, ({ request }) => { - return HttpResponse.json({ id: teamId }) - }), http.put( `https://api.github.com/teams/${teamId}/repos/${repository.owner.name}/${repository.name}`, async ({ request }) => { @@ -76,6 +73,31 @@ Given('the team privileges are updated to {string} in the config', async functio ) }) +Given('the team privileges are removed in the config', async function () { + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer(Buffer.from(dump({ teams: [] }))) + } + ), + http.delete( + `https://api.github.com/teams/:teamId/repos/${repository.owner.name}/${repository.name}`, + async ({ params }) => { + this.removedTeamId = params.teamId + + return new HttpResponse(null, { status: StatusCodes.NO_CONTENT }) + } + ) + ) +}) + Then('the team has {string} access granted to it', async function (accessLevel) { assert.equal(this.teamPermissionLevel, accessLevel) }) + +Then('the team has privileges to the repo revoked', async function () { + assert.equal(this.removedTeamId, teamId) +}) diff --git a/test/integration/features/teams.feature b/test/integration/features/teams.feature index 7e3068ed75..81a277a8cc 100644 --- a/test/integration/features/teams.feature +++ b/test/integration/features/teams.feature @@ -12,6 +12,8 @@ Feature: Teams When a settings sync is triggered Then the team has "admin" access granted to it - @wip Scenario: Remove Team Access + Given a team has been granted "push" privileges to the repository + And the team privileges are removed in the config When a settings sync is triggered + Then the team has privileges to the repo revoked From 1f1632e795ecccbb37697241b879465bc7a6a737 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 23 Jan 2024 22:20:42 -0600 Subject: [PATCH 04/43] test(integration): removed jest integration test for teams now that cucumber replacement exists --- test/fixtures/teams-config.yml | 7 ---- test/integration/plugins/teams.test.js | 58 -------------------------- 2 files changed, 65 deletions(-) delete mode 100644 test/fixtures/teams-config.yml delete mode 100644 test/integration/plugins/teams.test.js diff --git a/test/fixtures/teams-config.yml b/test/fixtures/teams-config.yml deleted file mode 100644 index dd30209c1b..0000000000 --- a/test/fixtures/teams-config.yml +++ /dev/null @@ -1,7 +0,0 @@ -teams: - - name: probot - permission: admin - - name: github - permission: maintain - - name: greenkeeper-keeper - permission: push diff --git a/test/integration/plugins/teams.test.js b/test/integration/plugins/teams.test.js deleted file mode 100644 index 8e124ece17..0000000000 --- a/test/integration/plugins/teams.test.js +++ /dev/null @@ -1,58 +0,0 @@ -const { CREATED, NO_CONTENT, OK } = require('http-status-codes') -const any = require('@travi/any') -const { - buildTriggerEvent, - initializeNock, - loadInstance, - repository, - teardownNock, - defineSettingsFileForScenario -} = require('../common') - -describe('teams plugin', function () { - let probot, githubScope - - beforeEach(async () => { - githubScope = initializeNock() - probot = await loadInstance() - }) - - afterEach(() => { - teardownNock(githubScope) - }) - - it('syncs teams', async () => { - await defineSettingsFileForScenario('teams-config.yml', githubScope) - const probotTeamId = any.integer() - const githubTeamId = any.integer() - const greenkeeperKeeperTeamId = any.integer() - const formationTeamId = any.integer() - githubScope.get(`/repos/${repository.owner.name}/${repository.name}/teams`).reply(OK, [ - { slug: 'greenkeeper-keeper', id: greenkeeperKeeperTeamId, permission: 'pull' }, - { slug: 'form8ion', id: formationTeamId, permission: 'push' } - ]) - githubScope.get(`/orgs/${repository.owner.name}/teams/probot`).reply(OK, { id: probotTeamId }) - githubScope.get(`/orgs/${repository.owner.name}/teams/github`).reply(OK, { id: githubTeamId }) - githubScope - .put(`/teams/${probotTeamId}/repos/${repository.owner.name}/${repository.name}`, body => { - expect(body).toMatchObject({ permission: 'admin' }) - return true - }) - .reply(CREATED) - githubScope - .put(`/teams/${githubTeamId}/repos/${repository.owner.name}/${repository.name}`, body => { - expect(body).toMatchObject({ permission: 'maintain' }) - return true - }) - .reply(CREATED) - githubScope - .put(`/teams/${greenkeeperKeeperTeamId}/repos/${repository.owner.name}/${repository.name}`, body => { - expect(body).toMatchObject({ permission: 'push' }) - return true - }) - .reply(OK) - githubScope.delete(`/teams/${formationTeamId}/repos/${repository.owner.name}/${repository.name}`).reply(NO_CONTENT) - - await probot.receive(buildTriggerEvent()) - }) -}) From 61949701b75df0bc846774d1642b7cef98954032 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 23 Jan 2024 23:35:50 -0600 Subject: [PATCH 05/43] test(integration): migrated the integration tests of collaborators to cucumber --- test/fixtures/collaborators-config.yml | 9 -- .../features/collaborators.feature | 19 +++ .../step_definitions/collaborators-steps.mjs | 118 ++++++++++++++++++ .../integration/plugins/collaborators.test.js | 45 ------- 4 files changed, 137 insertions(+), 54 deletions(-) delete mode 100644 test/fixtures/collaborators-config.yml create mode 100644 test/integration/features/collaborators.feature create mode 100644 test/integration/features/step_definitions/collaborators-steps.mjs delete mode 100644 test/integration/plugins/collaborators.test.js diff --git a/test/fixtures/collaborators-config.yml b/test/fixtures/collaborators-config.yml deleted file mode 100644 index 60b8bea519..0000000000 --- a/test/fixtures/collaborators-config.yml +++ /dev/null @@ -1,9 +0,0 @@ -collaborators: - - username: bkeepers - permission: push - - - username: octokit-bot - permission: triage - - - username: hubot - permission: pull diff --git a/test/integration/features/collaborators.feature b/test/integration/features/collaborators.feature new file mode 100644 index 0000000000..1f97ddb1a7 --- /dev/null +++ b/test/integration/features/collaborators.feature @@ -0,0 +1,19 @@ +Feature: Collaborators + + Scenario: Grant Collaborator Access + Given no collaborator has been granted access to the repository + And a collaborator is granted "push" privileges in the config + When a settings sync is triggered + Then the collaborator has "push" access granted to it + + Scenario: Update Collaborator Access + Given a collaborator has been granted "push" privileges to the repository + And the collaborator privileges are updated to "admin" in the config + When a settings sync is triggered + Then the collaborator has "admin" access granted to it + + Scenario: Remove Collaborator Access + Given a collaborator has been granted "push" privileges to the repository + And the collaborator privileges are removed in the config + When a settings sync is triggered + Then the collaborator has privileges to the repo revoked diff --git a/test/integration/features/step_definitions/collaborators-steps.mjs b/test/integration/features/step_definitions/collaborators-steps.mjs new file mode 100644 index 0000000000..8c2316645d --- /dev/null +++ b/test/integration/features/step_definitions/collaborators-steps.mjs @@ -0,0 +1,118 @@ +import { Given, Then } from '@cucumber/cucumber' +import assert from 'node:assert' +import { http, HttpResponse } from 'msw' +import any from '@travi/any' + +import { repository } from './common-steps.mjs' +import settings from '../../../../lib/settings.js' +import { dump } from 'js-yaml' +import { StatusCodes } from 'http-status-codes' + +const collaboratorLogin = any.word() + +Given('no collaborator has been granted access to the repository', async function () { + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/collaborators`, + ({ request }) => { + const url = new URL(request.url) + const collaboratorAffiliation = url.searchParams.get('affiliation') + + if (collaboratorAffiliation === 'direct') { + return HttpResponse.json([]) + } + } + ) + ) +}) + +Given('a collaborator has been granted {string} privileges to the repository', async function (accessLevel) { + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/collaborators`, + ({ request }) => { + const url = new URL(request.url) + const collaboratorAffiliation = url.searchParams.get('affiliation') + + if (collaboratorAffiliation === 'direct') { + return HttpResponse.json([{ login: collaboratorLogin, permissions: { [accessLevel]: true } }]) + } + } + ) + ) +}) + +Given('a collaborator is granted {string} privileges in the config', async function (accessLevel) { + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from(dump({ collaborators: [{ username: collaboratorLogin, permission: accessLevel }] })) + ) + } + ), + http.put( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/collaborators/${collaboratorLogin}`, + async ({ request }) => { + this.collaboratorPermissionLevel = (await request.json()).permission + + return new HttpResponse(null, { status: StatusCodes.CREATED }) + } + ) + ) +}) + +Given('the collaborator privileges are updated to {string} in the config', async function (accessLevel) { + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from(dump({ collaborators: [{ username: collaboratorLogin, permission: accessLevel }] })) + ) + } + ), + http.put( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/collaborators/${collaboratorLogin}`, + async ({ request }) => { + this.collaboratorPermissionLevel = (await request.json()).permission + + return new HttpResponse(null, { status: StatusCodes.OK }) + } + ) + ) +}) + +Given('the collaborator privileges are removed in the config', async function () { + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer(Buffer.from(dump({ collaborators: [] }))) + } + ), + http.delete( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/collaborators/:collaboratorLogin`, + async ({ params }) => { + this.removedCollaboratorLogin = params.collaboratorLogin + + return new HttpResponse(null, { status: StatusCodes.NO_CONTENT }) + } + ) + ) +}) + +Then('the collaborator has {string} access granted to it', async function (accessLevel) { + assert.equal(this.collaboratorPermissionLevel, accessLevel) +}) + +Then('the collaborator has privileges to the repo revoked', async function () { + assert.equal(this.removedCollaboratorLogin, collaboratorLogin) +}) diff --git a/test/integration/plugins/collaborators.test.js b/test/integration/plugins/collaborators.test.js deleted file mode 100644 index cfb9e96e6c..0000000000 --- a/test/integration/plugins/collaborators.test.js +++ /dev/null @@ -1,45 +0,0 @@ -const { CREATED, NO_CONTENT, OK } = require('http-status-codes') -const { - buildTriggerEvent, - initializeNock, - loadInstance, - repository, - teardownNock, - defineSettingsFileForScenario -} = require('../common') - -describe('collaborators plugin', function () { - let probot, githubScope - - beforeEach(async () => { - githubScope = initializeNock() - probot = await loadInstance() - }) - - afterEach(() => { - teardownNock(githubScope) - }) - - it('syncs collaborators', async () => { - await defineSettingsFileForScenario('collaborators-config.yml', githubScope) - githubScope.get(`/repos/${repository.owner.name}/${repository.name}/collaborators?affiliation=direct`).reply(OK, [ - { login: 'travi', permissions: { admin: true } }, - { login: 'bkeepers', permissions: { push: true } } - ]) - githubScope - .put(`/repos/${repository.owner.name}/${repository.name}/collaborators/hubot`, body => { - expect(body).toMatchObject({ permission: 'pull' }) - return true - }) - .reply(CREATED) - githubScope - .put(`/repos/${repository.owner.name}/${repository.name}/collaborators/octokit-bot`, body => { - expect(body).toMatchObject({ permission: 'triage' }) - return true - }) - .reply(CREATED) - githubScope.delete(`/repos/${repository.owner.name}/${repository.name}/collaborators/travi`).reply(NO_CONTENT) - - await probot.receive(buildTriggerEvent()) - }) -}) From 0079094ede5ff6d12587cef31cddcc7cc625ae75 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 26 Jan 2024 00:39:20 -0600 Subject: [PATCH 06/43] test(integration): added a cucumber test for adding a label and stubbed additional label scenarios --- test/integration/features/labels.feature | 19 ++++++++ .../step_definitions/labels-steps.mjs | 44 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 test/integration/features/labels.feature create mode 100644 test/integration/features/step_definitions/labels-steps.mjs diff --git a/test/integration/features/labels.feature b/test/integration/features/labels.feature new file mode 100644 index 0000000000..0317f231b7 --- /dev/null +++ b/test/integration/features/labels.feature @@ -0,0 +1,19 @@ +Feature: Labels + + Scenario: Create Label + Given no labels exist + And a label is added + When a settings sync is triggered + Then the label is available + + @wip + Scenario: Create Label with leading `#` + When a settings sync is triggered + + @wip + Scenario: Update Label + When a settings sync is triggered + + @wip + Scenario: Remove Label + When a settings sync is triggered diff --git a/test/integration/features/step_definitions/labels-steps.mjs b/test/integration/features/step_definitions/labels-steps.mjs new file mode 100644 index 0000000000..fafe5e7668 --- /dev/null +++ b/test/integration/features/step_definitions/labels-steps.mjs @@ -0,0 +1,44 @@ +import { dump } from 'js-yaml' +import { StatusCodes } from 'http-status-codes' + +import any from '@travi/any' +import { Given, Then } from '@cucumber/cucumber' +import { http, HttpResponse } from 'msw' +import assert from 'node:assert' + +import { repository } from './common-steps.mjs' +import settings from '../../../../lib/settings.js' + +Given('no labels exist', async function () { + this.server.use( + http.get(`https://api.github.com/repos/${repository.owner.name}/${repository.name}/labels`, ({ request }) => { + return HttpResponse.json([]) + }) + ) +}); + +Given('a label is added', async function () { + this.label = { name: any.word(), color: any.word() } + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer(Buffer.from(dump({labels: [this.label]}))) + } + ), + http.post( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/labels`, + async ({ request }) => { + this.savedLabel = (await request.json()) + + return new HttpResponse(null, { status: StatusCodes.CREATED }) + } + ) + ) +}); + +Then('the label is available', async function () { + assert.deepEqual(this.savedLabel, this.label) +}); From d6c6d36965cf9f8e718e36cd1b89ca623feb2219 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 26 Jan 2024 13:38:58 -0600 Subject: [PATCH 07/43] test(integration): added a cucumber test for added a label with color led by `#` --- test/integration/features/labels.feature | 4 ++- .../step_definitions/labels-steps.mjs | 36 ++++++++++++++++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/test/integration/features/labels.feature b/test/integration/features/labels.feature index 0317f231b7..7f5ac53db5 100644 --- a/test/integration/features/labels.feature +++ b/test/integration/features/labels.feature @@ -6,9 +6,11 @@ Feature: Labels When a settings sync is triggered Then the label is available - @wip Scenario: Create Label with leading `#` + Given no labels exist + And a label is added with a leading `#` When a settings sync is triggered + Then the label is available @wip Scenario: Update Label diff --git a/test/integration/features/step_definitions/labels-steps.mjs b/test/integration/features/step_definitions/labels-steps.mjs index fafe5e7668..5cad487e9c 100644 --- a/test/integration/features/step_definitions/labels-steps.mjs +++ b/test/integration/features/step_definitions/labels-steps.mjs @@ -15,30 +15,56 @@ Given('no labels exist', async function () { return HttpResponse.json([]) }) ) -}); +}) Given('a label is added', async function () { this.label = { name: any.word(), color: any.word() } + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer(Buffer.from(dump({ labels: [this.label] }))) + } + ), + http.post( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/labels`, + async ({ request }) => { + this.savedLabel = await request.json() + + return new HttpResponse(null, { status: StatusCodes.CREATED }) + } + ) + ) +}) + +Given('a label is added with a leading `#`', async function () { + this.label = { name: any.word(), color: any.word() } + this.server.use( http.get( `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( settings.FILE_NAME )}`, ({ request }) => { - return HttpResponse.arrayBuffer(Buffer.from(dump({labels: [this.label]}))) + return HttpResponse.arrayBuffer( + Buffer.from(dump({ labels: [{ ...this.label, color: `#${this.label.color}` }] })) + ) } ), http.post( `https://api.github.com/repos/${repository.owner.name}/${repository.name}/labels`, async ({ request }) => { - this.savedLabel = (await request.json()) + this.savedLabel = await request.json() return new HttpResponse(null, { status: StatusCodes.CREATED }) } ) ) -}); +}) Then('the label is available', async function () { assert.deepEqual(this.savedLabel, this.label) -}); +}) From 8501159ce6df463bd40629e4adedfa9f8d2984bb Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 26 Jan 2024 16:43:01 -0600 Subject: [PATCH 08/43] test(integration): added a cucumber test for updating a label color --- test/integration/features/labels.feature | 4 +- .../step_definitions/labels-steps.mjs | 39 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/test/integration/features/labels.feature b/test/integration/features/labels.feature index 7f5ac53db5..6ee3f9ff8b 100644 --- a/test/integration/features/labels.feature +++ b/test/integration/features/labels.feature @@ -12,9 +12,11 @@ Feature: Labels When a settings sync is triggered Then the label is available - @wip Scenario: Update Label + Given a label exists + And the color is updated on the existing label When a settings sync is triggered + Then the label has the updated color @wip Scenario: Remove Label diff --git a/test/integration/features/step_definitions/labels-steps.mjs b/test/integration/features/step_definitions/labels-steps.mjs index 5cad487e9c..f35210d14c 100644 --- a/test/integration/features/step_definitions/labels-steps.mjs +++ b/test/integration/features/step_definitions/labels-steps.mjs @@ -17,6 +17,16 @@ Given('no labels exist', async function () { ) }) +Given('a label exists', async function () { + this.label = { name: any.word(), color: any.word() } + + this.server.use( + http.get(`https://api.github.com/repos/${repository.owner.name}/${repository.name}/labels`, ({ request }) => { + return HttpResponse.json([this.label]) + }) + ) +}) + Given('a label is added', async function () { this.label = { name: any.word(), color: any.word() } @@ -65,6 +75,35 @@ Given('a label is added with a leading `#`', async function () { ) }) +Given('the color is updated on the existing label', async function () { + this.newColor = any.word() + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from(dump({ labels: [{ name: this.label.name, color: this.newColor }] })) + ) + } + ), + http.patch( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/labels/${this.label.name}`, + async ({ request }) => { + this.updatedColor = (await request.json()).color + + return new HttpResponse(null, { status: StatusCodes.OK }) + } + ) + ) +}) + Then('the label is available', async function () { assert.deepEqual(this.savedLabel, this.label) }) + +Then('the label has the updated color', async function () { + assert.equal(this.updatedColor, this.newColor) +}) From 0d42bfd27513c657a23ddb51629be7b3bb99a505 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 26 Jan 2024 16:50:58 -0600 Subject: [PATCH 09/43] test(integration): added a cucumber test for removing a label --- test/integration/features/labels.feature | 4 ++- .../step_definitions/labels-steps.mjs | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/test/integration/features/labels.feature b/test/integration/features/labels.feature index 6ee3f9ff8b..357a152711 100644 --- a/test/integration/features/labels.feature +++ b/test/integration/features/labels.feature @@ -18,6 +18,8 @@ Feature: Labels When a settings sync is triggered Then the label has the updated color - @wip Scenario: Remove Label + Given a label exists + And the label is removed from the config When a settings sync is triggered + Then the label is no longer available diff --git a/test/integration/features/step_definitions/labels-steps.mjs b/test/integration/features/step_definitions/labels-steps.mjs index f35210d14c..eb66cd1ede 100644 --- a/test/integration/features/step_definitions/labels-steps.mjs +++ b/test/integration/features/step_definitions/labels-steps.mjs @@ -100,6 +100,27 @@ Given('the color is updated on the existing label', async function () { ) }) +Given('the label is removed from the config', async function () { + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer(Buffer.from(dump({ labels: [] }))) + } + ), + http.delete( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/labels/:labelName`, + async ({ params }) => { + this.removedLabel = params.labelName + + return new HttpResponse(null, { status: StatusCodes.NO_CONTENT }) + } + ) + ) +}) + Then('the label is available', async function () { assert.deepEqual(this.savedLabel, this.label) }) @@ -107,3 +128,7 @@ Then('the label is available', async function () { Then('the label has the updated color', async function () { assert.equal(this.updatedColor, this.newColor) }) + +Then('the label is no longer available', async function () { + assert.deepEqual(this.removedLabel, this.label.name) +}) From e60a151e12e022c485d1b60c99b9efe3e3638c5a Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 26 Jan 2024 17:00:21 -0600 Subject: [PATCH 10/43] test(integration): captured additional scenarios for labels --- test/integration/features/labels.feature | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/integration/features/labels.feature b/test/integration/features/labels.feature index 357a152711..2ecba3b7b3 100644 --- a/test/integration/features/labels.feature +++ b/test/integration/features/labels.feature @@ -23,3 +23,20 @@ Feature: Labels And the label is removed from the config When a settings sync is triggered Then the label is no longer available + + @wip + Scenario: Rename a Label + + @wip + Scenario: Label with color matching config if `#` were stripped + Then no call to update the color is performed + + @wip + Scenario: + Then no call to update the name is performed + + @wip + Scenario: Label with a short color code + + @wip + Scenario: Label with numerical color code From 73c07fa8ae87d5065faf3c4816835cb70ea76322 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 26 Jan 2024 17:01:39 -0600 Subject: [PATCH 11/43] test(integration): removed jest integration test for labels since it has been replaced by cucumber tests --- test/fixtures/labels-config.yml | 7 ---- test/integration/plugins/labels.test.js | 45 ------------------------- 2 files changed, 52 deletions(-) delete mode 100644 test/fixtures/labels-config.yml delete mode 100644 test/integration/plugins/labels.test.js diff --git a/test/fixtures/labels-config.yml b/test/fixtures/labels-config.yml deleted file mode 100644 index 5c01bd413a..0000000000 --- a/test/fixtures/labels-config.yml +++ /dev/null @@ -1,7 +0,0 @@ -labels: - - name: bug - color: ee0701 - - name: enhancement - color: 008672 - - name: help wanted - color: d876e3 diff --git a/test/integration/plugins/labels.test.js b/test/integration/plugins/labels.test.js deleted file mode 100644 index b05f0a9be9..0000000000 --- a/test/integration/plugins/labels.test.js +++ /dev/null @@ -1,45 +0,0 @@ -const { - initializeNock, - loadInstance, - teardownNock, - repository, - buildTriggerEvent, - defineSettingsFileForScenario -} = require('../common') -const { OK, CREATED, NO_CONTENT } = require('http-status-codes') -describe('branches plugin', function () { - let probot, githubScope - - beforeEach(async () => { - githubScope = initializeNock() - probot = await loadInstance() - }) - - afterEach(() => { - teardownNock(githubScope) - }) - - it('configures labels', async () => { - await defineSettingsFileForScenario('labels-config.yml', githubScope) - githubScope.get(`/repos/${repository.owner.name}/${repository.name}/labels?per_page=100`).reply(OK, [ - { name: 'bug', color: 'ee0701' }, - { name: 'duplicate', color: 'cccccc' }, - { name: 'help wanted', color: '128A0C' } - ]) - githubScope - .post(`/repos/${repository.owner.name}/${repository.name}/labels`, body => { - expect(body).toMatchObject({ name: 'enhancement', color: '008672' }) - return true - }) - .reply(CREATED) - githubScope - .patch(`/repos/${repository.owner.name}/${repository.name}/labels/help%20wanted`, body => { - expect(body).toMatchObject({ color: 'd876e3' }) - return true - }) - .reply(OK) - githubScope.delete(`/repos/${repository.owner.name}/${repository.name}/labels/duplicate`).reply(NO_CONTENT) - - await probot.receive(buildTriggerEvent()) - }) -}) From fcd5ca29d1aa1eb6ccbd629cf713d05f4d6f5d8e Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 26 Jan 2024 17:15:09 -0600 Subject: [PATCH 12/43] test(cucumber): downgraded to v9 to bring back v16 support for now --- package-lock.json | 632 ++++++++++++++++++++-------------------------- package.json | 2 +- 2 files changed, 274 insertions(+), 360 deletions(-) diff --git a/package-lock.json b/package-lock.json index af31c37c4f..93d30651ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "probot": "12.3.3" }, "devDependencies": { - "@cucumber/cucumber": "10.3.1", + "@cucumber/cucumber": "9.6.0", "@form8ion/remark-preset": "1.0.0", "@travi/any": "3.0.2", "http-status-codes": "2.3.0", @@ -740,26 +740,26 @@ } }, "node_modules/@cucumber/ci-environment": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-10.0.0.tgz", - "integrity": "sha512-lRkiehckosIOdc7p1L44nZsttO5dVHFjpwKKWZ07x8SeoAdV/sPuGe1PISe0AmAowFGza62nMOgG4KaroGzwFQ==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-9.2.0.tgz", + "integrity": "sha512-jLzRtVwdtNt+uAmTwvXwW9iGYLEOJFpDSmnx/dgoMGKXUWRx1UHT86Q696CLdgXO8kyTwsgJY0c6n5SW9VitAA==", "dev": true }, "node_modules/@cucumber/cucumber": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-10.3.1.tgz", - "integrity": "sha512-0H0NkOXcYTCG1qCh3o0p1HPSMODGJmlHi1rm5yfoiMx5tJbBjxVNI2VVD2xtPWA+D6ehHQD9asewuzjMXrbPIw==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-9.6.0.tgz", + "integrity": "sha512-bCw2uJdGHHLg4B3RoZpLzx0RXyXURmPe+swtdK1cGoA8rs+vv+/6osifcNwvFM2sv0nQ91+gDACSrXK7AHCylg==", "dev": true, "dependencies": { - "@cucumber/ci-environment": "10.0.0", - "@cucumber/cucumber-expressions": "17.0.1", - "@cucumber/gherkin": "27.0.0", + "@cucumber/ci-environment": "9.2.0", + "@cucumber/cucumber-expressions": "16.1.2", + "@cucumber/gherkin": "26.2.0", "@cucumber/gherkin-streams": "5.0.1", - "@cucumber/gherkin-utils": "8.0.5", - "@cucumber/html-formatter": "21.2.0", + "@cucumber/gherkin-utils": "8.0.2", + "@cucumber/html-formatter": "20.4.0", "@cucumber/message-streams": "4.0.1", - "@cucumber/messages": "24.0.1", - "@cucumber/tag-expressions": "6.1.0", + "@cucumber/messages": "22.0.0", + "@cucumber/tag-expressions": "5.0.1", "assertion-error-formatter": "^3.0.0", "capital-case": "^1.0.4", "chalk": "^4.1.2", @@ -768,7 +768,7 @@ "debug": "^4.3.4", "error-stack-parser": "^2.1.4", "figures": "^3.2.0", - "glob": "^10.3.10", + "glob": "^7.1.6", "has-ansi": "^4.0.1", "indent-string": "^4.0.0", "is-installed-globally": "^0.4.0", @@ -780,15 +780,14 @@ "mkdirp": "^2.1.5", "mz": "^2.7.0", "progress": "^2.0.3", - "read-pkg-up": "^7.0.1", "resolve-pkg": "^2.0.0", "semver": "7.5.3", - "string-argv": "0.3.1", + "string-argv": "^0.3.1", "strip-ansi": "6.0.1", "supports-color": "^8.1.1", "tmp": "^0.2.1", - "type-fest": "^4.8.3", "util-arity": "^1.1.0", + "verror": "^1.10.0", "xmlbuilder": "^15.1.1", "yaml": "^2.2.2", "yup": "1.2.0" @@ -797,18 +796,45 @@ "cucumber-js": "bin/cucumber.js" }, "engines": { - "node": "18 || >=20" + "node": "14 || 16 || >=18" } }, "node_modules/@cucumber/cucumber-expressions": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-17.0.1.tgz", - "integrity": "sha512-reR7/sNRmDWgdz8BtFuHEwpksPnAkHty7gxUC2n0iaUPmckv9G5I5i+Vonc6xwUHDb/hmHPz/DyUL+Iv4Ao96w==", + "version": "16.1.2", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-16.1.2.tgz", + "integrity": "sha512-CfHEbxJ5FqBwF6mJyLLz4B353gyHkoi6cCL4J0lfDZ+GorpcWw4n2OUAdxJmP7ZlREANWoTFlp4FhmkLKrCfUA==", "dev": true, "dependencies": { "regexp-match-indices": "1.0.2" } }, + "node_modules/@cucumber/cucumber/node_modules/@cucumber/gherkin": { + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-26.2.0.tgz", + "integrity": "sha512-iRSiK8YAIHAmLrn/mUfpAx7OXZ7LyNlh1zT89RoziSVCbqSVDxJS6ckEzW8loxs+EEXl0dKPQOXiDmbHV+C/fA==", + "dev": true, + "dependencies": { + "@cucumber/messages": ">=19.1.4 <=22" + } + }, + "node_modules/@cucumber/cucumber/node_modules/@cucumber/messages": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-22.0.0.tgz", + "integrity": "sha512-EuaUtYte9ilkxcKmfqGF9pJsHRUU0jwie5ukuZ/1NPTuHS1LxHPsGEODK17RPRbZHOFhqybNzG2rHAwThxEymg==", + "dev": true, + "dependencies": { + "@types/uuid": "9.0.1", + "class-transformer": "0.5.1", + "reflect-metadata": "0.1.13", + "uuid": "9.0.0" + } + }, + "node_modules/@cucumber/cucumber/node_modules/@types/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==", + "dev": true + }, "node_modules/@cucumber/cucumber/node_modules/ansi-regex": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", @@ -818,15 +844,6 @@ "node": ">=6" } }, - "node_modules/@cucumber/cucumber/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/@cucumber/cucumber/node_modules/commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", @@ -836,28 +853,6 @@ "node": ">=14" } }, - "node_modules/@cucumber/cucumber/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@cucumber/cucumber/node_modules/has-ansi": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-4.0.1.tgz", @@ -882,21 +877,6 @@ "node": ">=10" } }, - "node_modules/@cucumber/cucumber/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@cucumber/cucumber/node_modules/mkdirp": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", @@ -912,6 +892,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@cucumber/cucumber/node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, "node_modules/@cucumber/cucumber/node_modules/semver": { "version": "7.5.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", @@ -927,15 +913,6 @@ "node": ">=10" } }, - "node_modules/@cucumber/cucumber/node_modules/string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true, - "engines": { - "node": ">=0.6.19" - } - }, "node_modules/@cucumber/cucumber/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -963,16 +940,13 @@ "node": ">=8.17.0" } }, - "node_modules/@cucumber/cucumber/node_modules/type-fest": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.0.tgz", - "integrity": "sha512-NPaKJsb4wyJ16qc8zBQrWswLKv/YirgBFykvUQ1Iajt2wd+twC8E4hFXdlIXqiMl6kWA0zY8tUJ9ELVAdu5h7w==", + "node_modules/@cucumber/cucumber/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/@cucumber/cucumber/node_modules/yallist": { @@ -995,6 +969,7 @@ "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-27.0.0.tgz", "integrity": "sha512-j5rCsjqzRiC3iVTier3sa0kzyNbkcAmF7xr7jKnyO7qDeK3Z8Ye1P3KSVpeQRMY+KCDJ3WbTDdyxH0FwfA/fIw==", "dev": true, + "peer": true, "dependencies": { "@cucumber/messages": ">=19.1.4 <=22" } @@ -1037,15 +1012,15 @@ } }, "node_modules/@cucumber/gherkin-utils": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-8.0.5.tgz", - "integrity": "sha512-kxM1OCDjYddF26VKc892PF0GokW4wUIl1PUz3TIXsPZgS39ExM1pF8oww8mlGFD2B0+4op/cSE3SSIME5H3aNw==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-8.0.2.tgz", + "integrity": "sha512-aQlziN3r3cTwprEDbLEcFoMRQajb9DTOu2OZZp5xkuNz6bjSTowSY90lHUD2pWT7jhEEckZRIREnk7MAwC2d1A==", "dev": true, "dependencies": { - "@cucumber/gherkin": "^26.0.0", - "@cucumber/messages": "^22.0.0", - "@teppeis/multimaps": "3.0.0", - "commander": "10.0.1", + "@cucumber/gherkin": "^25.0.0", + "@cucumber/messages": "^19.1.4", + "@teppeis/multimaps": "2.0.0", + "commander": "9.4.1", "source-map-support": "^0.5.21" }, "bin": { @@ -1053,39 +1028,39 @@ } }, "node_modules/@cucumber/gherkin-utils/node_modules/@cucumber/gherkin": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-26.2.0.tgz", - "integrity": "sha512-iRSiK8YAIHAmLrn/mUfpAx7OXZ7LyNlh1zT89RoziSVCbqSVDxJS6ckEzW8loxs+EEXl0dKPQOXiDmbHV+C/fA==", + "version": "25.0.2", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-25.0.2.tgz", + "integrity": "sha512-EdsrR33Y5GjuOoe2Kq5Y9DYwgNRtUD32H4y2hCrT6+AWo7ibUQu7H+oiWTgfVhwbkHsZmksxHSxXz/AwqqyCRQ==", "dev": true, "dependencies": { - "@cucumber/messages": ">=19.1.4 <=22" + "@cucumber/messages": "^19.1.4" } }, "node_modules/@cucumber/gherkin-utils/node_modules/@cucumber/messages": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-22.0.0.tgz", - "integrity": "sha512-EuaUtYte9ilkxcKmfqGF9pJsHRUU0jwie5ukuZ/1NPTuHS1LxHPsGEODK17RPRbZHOFhqybNzG2rHAwThxEymg==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-19.1.4.tgz", + "integrity": "sha512-Pksl0pnDz2l1+L5Ug85NlG6LWrrklN9qkMxN5Mv+1XZ3T6u580dnE6mVaxjJRdcOq4tR17Pc0RqIDZMyVY1FlA==", "dev": true, "dependencies": { - "@types/uuid": "9.0.1", + "@types/uuid": "8.3.4", "class-transformer": "0.5.1", "reflect-metadata": "0.1.13", "uuid": "9.0.0" } }, "node_modules/@cucumber/gherkin-utils/node_modules/@types/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", "dev": true }, "node_modules/@cucumber/gherkin-utils/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", "dev": true, "engines": { - "node": ">=14" + "node": "^12.20.0 || >=14" } }, "node_modules/@cucumber/gherkin-utils/node_modules/reflect-metadata": { @@ -1118,6 +1093,7 @@ "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-22.0.0.tgz", "integrity": "sha512-EuaUtYte9ilkxcKmfqGF9pJsHRUU0jwie5ukuZ/1NPTuHS1LxHPsGEODK17RPRbZHOFhqybNzG2rHAwThxEymg==", "dev": true, + "peer": true, "dependencies": { "@types/uuid": "9.0.1", "class-transformer": "0.5.1", @@ -1129,27 +1105,30 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@cucumber/gherkin/node_modules/reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@cucumber/gherkin/node_modules/uuid": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "dev": true, + "peer": true, "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/@cucumber/html-formatter": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-21.2.0.tgz", - "integrity": "sha512-4OcSa12Y0v5e4ySDl67+QFTxCG/Y9fxGSkFqvm98ggpTvS7b75whwzupu+lM2lMBw+h3H6P8ZURQr0xQIAwE2A==", + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-20.4.0.tgz", + "integrity": "sha512-TnLSXC5eJd8AXHENo69f5z+SixEVtQIf7Q2dZuTpT/Y8AOkilGpGl1MQR1Vp59JIw+fF3EQSUKdf+DAThCxUNg==", "dev": true, "peerDependencies": { "@cucumber/messages": ">=18" @@ -1169,6 +1148,7 @@ "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-24.0.1.tgz", "integrity": "sha512-dKfNkvgc6stSQIyeHk7p/221iqEZe1BP+e/Js8XKtSmc0sS8khKMvbSBwYVeonn/67/vYKiAyo6Eo0SzXd5Plw==", "dev": true, + "peer": true, "dependencies": { "@types/uuid": "9.0.7", "class-transformer": "0.5.1", @@ -1185,14 +1165,15 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "peer": true, "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/@cucumber/tag-expressions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-6.1.0.tgz", - "integrity": "sha512-+3DwRumrCJG27AtzCIL37A/X+A/gSfxOPLg8pZaruh5SLumsTmpvilwroVWBT2fPzmno/tGXypeK5a7NHU4RzA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-5.0.1.tgz", + "integrity": "sha512-N43uWud8ZXuVjza423T9ZCIJsaZhFekmakt7S9bvogTxqdVGbRobjR663s0+uW0Rz9e+Pa8I6jUuWtoBLQD2Mw==", "dev": true }, "node_modules/@eslint-community/eslint-utils": { @@ -3567,12 +3548,12 @@ } }, "node_modules/@teppeis/multimaps": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@teppeis/multimaps/-/multimaps-3.0.0.tgz", - "integrity": "sha512-ID7fosbc50TbT0MK0EG12O+gAP3W3Aa/Pz4DaTtQtEvlc9Odaqi0de+xuZ7Li2GtK4HzEX7IuRWS/JmZLksR3Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@teppeis/multimaps/-/multimaps-2.0.0.tgz", + "integrity": "sha512-TL1adzq1HdxUf9WYduLcQ/DNGYiz71U31QRgbnr0Ef1cPyOUOsBojxHVWpFeOSUucB6Lrs0LxFRA14ntgtkc9w==", "dev": true, "engines": { - "node": ">=14" + "node": ">=10.17" } }, "node_modules/@tootallnate/once": { @@ -4016,7 +3997,8 @@ "version": "9.0.7", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", "integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/yargs": { "version": "17.0.24", @@ -4630,6 +4612,15 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, "node_modules/assertion-error-formatter": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/assertion-error-formatter/-/assertion-error-formatter-3.0.0.tgz", @@ -5808,6 +5799,12 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true + }, "node_modules/cosmiconfig": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", @@ -7368,6 +7365,15 @@ "node": ">=0.10.0" } }, + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, "node_modules/fast_array_intersect": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fast_array_intersect/-/fast_array_intersect-1.1.0.tgz", @@ -17293,83 +17299,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/read-pkg-up/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/read-pkg-up/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/read-pkg/node_modules/hosted-git-info": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.0.tgz", @@ -17559,7 +17488,8 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/regenerator-runtime": { "version": "0.13.11", @@ -25019,6 +24949,20 @@ "node": ">= 0.8" } }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/version-guard": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/version-guard/-/version-guard-1.1.1.tgz", @@ -26384,26 +26328,26 @@ "optional": true }, "@cucumber/ci-environment": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-10.0.0.tgz", - "integrity": "sha512-lRkiehckosIOdc7p1L44nZsttO5dVHFjpwKKWZ07x8SeoAdV/sPuGe1PISe0AmAowFGza62nMOgG4KaroGzwFQ==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-9.2.0.tgz", + "integrity": "sha512-jLzRtVwdtNt+uAmTwvXwW9iGYLEOJFpDSmnx/dgoMGKXUWRx1UHT86Q696CLdgXO8kyTwsgJY0c6n5SW9VitAA==", "dev": true }, "@cucumber/cucumber": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-10.3.1.tgz", - "integrity": "sha512-0H0NkOXcYTCG1qCh3o0p1HPSMODGJmlHi1rm5yfoiMx5tJbBjxVNI2VVD2xtPWA+D6ehHQD9asewuzjMXrbPIw==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-9.6.0.tgz", + "integrity": "sha512-bCw2uJdGHHLg4B3RoZpLzx0RXyXURmPe+swtdK1cGoA8rs+vv+/6osifcNwvFM2sv0nQ91+gDACSrXK7AHCylg==", "dev": true, "requires": { - "@cucumber/ci-environment": "10.0.0", - "@cucumber/cucumber-expressions": "17.0.1", - "@cucumber/gherkin": "27.0.0", + "@cucumber/ci-environment": "9.2.0", + "@cucumber/cucumber-expressions": "16.1.2", + "@cucumber/gherkin": "26.2.0", "@cucumber/gherkin-streams": "5.0.1", - "@cucumber/gherkin-utils": "8.0.5", - "@cucumber/html-formatter": "21.2.0", + "@cucumber/gherkin-utils": "8.0.2", + "@cucumber/html-formatter": "20.4.0", "@cucumber/message-streams": "4.0.1", - "@cucumber/messages": "24.0.1", - "@cucumber/tag-expressions": "6.1.0", + "@cucumber/messages": "22.0.0", + "@cucumber/tag-expressions": "5.0.1", "assertion-error-formatter": "^3.0.0", "capital-case": "^1.0.4", "chalk": "^4.1.2", @@ -26412,7 +26356,7 @@ "debug": "^4.3.4", "error-stack-parser": "^2.1.4", "figures": "^3.2.0", - "glob": "^10.3.10", + "glob": "^7.1.6", "has-ansi": "^4.0.1", "indent-string": "^4.0.0", "is-installed-globally": "^0.4.0", @@ -26424,54 +26368,58 @@ "mkdirp": "^2.1.5", "mz": "^2.7.0", "progress": "^2.0.3", - "read-pkg-up": "^7.0.1", "resolve-pkg": "^2.0.0", "semver": "7.5.3", - "string-argv": "0.3.1", + "string-argv": "^0.3.1", "strip-ansi": "6.0.1", "supports-color": "^8.1.1", "tmp": "^0.2.1", - "type-fest": "^4.8.3", "util-arity": "^1.1.0", + "verror": "^1.10.0", "xmlbuilder": "^15.1.1", "yaml": "^2.2.2", "yup": "1.2.0" }, "dependencies": { + "@cucumber/gherkin": { + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-26.2.0.tgz", + "integrity": "sha512-iRSiK8YAIHAmLrn/mUfpAx7OXZ7LyNlh1zT89RoziSVCbqSVDxJS6ckEzW8loxs+EEXl0dKPQOXiDmbHV+C/fA==", + "dev": true, + "requires": { + "@cucumber/messages": ">=19.1.4 <=22" + } + }, + "@cucumber/messages": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-22.0.0.tgz", + "integrity": "sha512-EuaUtYte9ilkxcKmfqGF9pJsHRUU0jwie5ukuZ/1NPTuHS1LxHPsGEODK17RPRbZHOFhqybNzG2rHAwThxEymg==", + "dev": true, + "requires": { + "@types/uuid": "9.0.1", + "class-transformer": "0.5.1", + "reflect-metadata": "0.1.13", + "uuid": "9.0.0" + } + }, + "@types/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==", + "dev": true + }, "ansi-regex": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true }, - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, "commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true }, - "glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "requires": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - } - }, "has-ansi": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-4.0.1.tgz", @@ -26490,21 +26438,18 @@ "yallist": "^4.0.0" } }, - "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, "mkdirp": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", "dev": true }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, "semver": { "version": "7.5.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", @@ -26514,12 +26459,6 @@ "lru-cache": "^6.0.0" } }, - "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true - }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -26538,10 +26477,10 @@ "rimraf": "^3.0.0" } }, - "type-fest": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.0.tgz", - "integrity": "sha512-NPaKJsb4wyJ16qc8zBQrWswLKv/YirgBFykvUQ1Iajt2wd+twC8E4hFXdlIXqiMl6kWA0zY8tUJ9ELVAdu5h7w==", + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "dev": true }, "yallist": { @@ -26559,9 +26498,9 @@ } }, "@cucumber/cucumber-expressions": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-17.0.1.tgz", - "integrity": "sha512-reR7/sNRmDWgdz8BtFuHEwpksPnAkHty7gxUC2n0iaUPmckv9G5I5i+Vonc6xwUHDb/hmHPz/DyUL+Iv4Ao96w==", + "version": "16.1.2", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-16.1.2.tgz", + "integrity": "sha512-CfHEbxJ5FqBwF6mJyLLz4B353gyHkoi6cCL4J0lfDZ+GorpcWw4n2OUAdxJmP7ZlREANWoTFlp4FhmkLKrCfUA==", "dev": true, "requires": { "regexp-match-indices": "1.0.2" @@ -26572,6 +26511,7 @@ "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-27.0.0.tgz", "integrity": "sha512-j5rCsjqzRiC3iVTier3sa0kzyNbkcAmF7xr7jKnyO7qDeK3Z8Ye1P3KSVpeQRMY+KCDJ3WbTDdyxH0FwfA/fIw==", "dev": true, + "peer": true, "requires": { "@cucumber/messages": ">=19.1.4 <=22" }, @@ -26581,6 +26521,7 @@ "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-22.0.0.tgz", "integrity": "sha512-EuaUtYte9ilkxcKmfqGF9pJsHRUU0jwie5ukuZ/1NPTuHS1LxHPsGEODK17RPRbZHOFhqybNzG2rHAwThxEymg==", "dev": true, + "peer": true, "requires": { "@types/uuid": "9.0.1", "class-transformer": "0.5.1", @@ -26592,19 +26533,22 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==", - "dev": true + "dev": true, + "peer": true }, "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", - "dev": true + "dev": true, + "peer": true }, "uuid": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "dev": true + "dev": true, + "peer": true } } }, @@ -26637,49 +26581,49 @@ } }, "@cucumber/gherkin-utils": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-8.0.5.tgz", - "integrity": "sha512-kxM1OCDjYddF26VKc892PF0GokW4wUIl1PUz3TIXsPZgS39ExM1pF8oww8mlGFD2B0+4op/cSE3SSIME5H3aNw==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-8.0.2.tgz", + "integrity": "sha512-aQlziN3r3cTwprEDbLEcFoMRQajb9DTOu2OZZp5xkuNz6bjSTowSY90lHUD2pWT7jhEEckZRIREnk7MAwC2d1A==", "dev": true, "requires": { - "@cucumber/gherkin": "^26.0.0", - "@cucumber/messages": "^22.0.0", - "@teppeis/multimaps": "3.0.0", - "commander": "10.0.1", + "@cucumber/gherkin": "^25.0.0", + "@cucumber/messages": "^19.1.4", + "@teppeis/multimaps": "2.0.0", + "commander": "9.4.1", "source-map-support": "^0.5.21" }, "dependencies": { "@cucumber/gherkin": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-26.2.0.tgz", - "integrity": "sha512-iRSiK8YAIHAmLrn/mUfpAx7OXZ7LyNlh1zT89RoziSVCbqSVDxJS6ckEzW8loxs+EEXl0dKPQOXiDmbHV+C/fA==", + "version": "25.0.2", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-25.0.2.tgz", + "integrity": "sha512-EdsrR33Y5GjuOoe2Kq5Y9DYwgNRtUD32H4y2hCrT6+AWo7ibUQu7H+oiWTgfVhwbkHsZmksxHSxXz/AwqqyCRQ==", "dev": true, "requires": { - "@cucumber/messages": ">=19.1.4 <=22" + "@cucumber/messages": "^19.1.4" } }, "@cucumber/messages": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-22.0.0.tgz", - "integrity": "sha512-EuaUtYte9ilkxcKmfqGF9pJsHRUU0jwie5ukuZ/1NPTuHS1LxHPsGEODK17RPRbZHOFhqybNzG2rHAwThxEymg==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-19.1.4.tgz", + "integrity": "sha512-Pksl0pnDz2l1+L5Ug85NlG6LWrrklN9qkMxN5Mv+1XZ3T6u580dnE6mVaxjJRdcOq4tR17Pc0RqIDZMyVY1FlA==", "dev": true, "requires": { - "@types/uuid": "9.0.1", + "@types/uuid": "8.3.4", "class-transformer": "0.5.1", "reflect-metadata": "0.1.13", "uuid": "9.0.0" } }, "@types/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", "dev": true }, "commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", "dev": true }, "reflect-metadata": { @@ -26707,9 +26651,9 @@ } }, "@cucumber/html-formatter": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-21.2.0.tgz", - "integrity": "sha512-4OcSa12Y0v5e4ySDl67+QFTxCG/Y9fxGSkFqvm98ggpTvS7b75whwzupu+lM2lMBw+h3H6P8ZURQr0xQIAwE2A==", + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-20.4.0.tgz", + "integrity": "sha512-TnLSXC5eJd8AXHENo69f5z+SixEVtQIf7Q2dZuTpT/Y8AOkilGpGl1MQR1Vp59JIw+fF3EQSUKdf+DAThCxUNg==", "dev": true, "requires": {} }, @@ -26725,6 +26669,7 @@ "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-24.0.1.tgz", "integrity": "sha512-dKfNkvgc6stSQIyeHk7p/221iqEZe1BP+e/Js8XKtSmc0sS8khKMvbSBwYVeonn/67/vYKiAyo6Eo0SzXd5Plw==", "dev": true, + "peer": true, "requires": { "@types/uuid": "9.0.7", "class-transformer": "0.5.1", @@ -26736,14 +26681,15 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true + "dev": true, + "peer": true } } }, "@cucumber/tag-expressions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-6.1.0.tgz", - "integrity": "sha512-+3DwRumrCJG27AtzCIL37A/X+A/gSfxOPLg8pZaruh5SLumsTmpvilwroVWBT2fPzmno/tGXypeK5a7NHU4RzA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-5.0.1.tgz", + "integrity": "sha512-N43uWud8ZXuVjza423T9ZCIJsaZhFekmakt7S9bvogTxqdVGbRobjR663s0+uW0Rz9e+Pa8I6jUuWtoBLQD2Mw==", "dev": true }, "@eslint-community/eslint-utils": { @@ -28697,9 +28643,9 @@ } }, "@teppeis/multimaps": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@teppeis/multimaps/-/multimaps-3.0.0.tgz", - "integrity": "sha512-ID7fosbc50TbT0MK0EG12O+gAP3W3Aa/Pz4DaTtQtEvlc9Odaqi0de+xuZ7Li2GtK4HzEX7IuRWS/JmZLksR3Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@teppeis/multimaps/-/multimaps-2.0.0.tgz", + "integrity": "sha512-TL1adzq1HdxUf9WYduLcQ/DNGYiz71U31QRgbnr0Ef1cPyOUOsBojxHVWpFeOSUucB6Lrs0LxFRA14ntgtkc9w==", "dev": true }, "@tootallnate/once": { @@ -29122,7 +29068,8 @@ "version": "9.0.7", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", "integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==", - "dev": true + "dev": true, + "peer": true }, "@types/yargs": { "version": "17.0.24", @@ -29592,6 +29539,12 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true + }, "assertion-error-formatter": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/assertion-error-formatter/-/assertion-error-formatter-3.0.0.tgz", @@ -30451,6 +30404,12 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true + }, "cosmiconfig": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", @@ -31672,6 +31631,12 @@ } } }, + "extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true + }, "fast_array_intersect": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fast_array_intersect/-/fast_array_intersect-1.1.0.tgz", @@ -39176,69 +39141,6 @@ } } }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, "readable-stream": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.0.tgz", @@ -39294,7 +39196,8 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==", - "dev": true + "dev": true, + "peer": true }, "regenerator-runtime": { "version": "0.13.11", @@ -44749,6 +44652,17 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, + "verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, "version-guard": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/version-guard/-/version-guard-1.1.1.tgz", diff --git a/package.json b/package.json index e56a6b43d5..1c2e8952fc 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "probot": "12.3.3" }, "devDependencies": { - "@cucumber/cucumber": "10.3.1", + "@cucumber/cucumber": "9.6.0", "@form8ion/remark-preset": "1.0.0", "@travi/any": "3.0.2", "http-status-codes": "2.3.0", From cba9f9726db337b873ecc7de322fa3d1e420096e Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 26 Jan 2024 22:38:51 -0600 Subject: [PATCH 13/43] test(integration): captured more labels scenarios --- test/integration/features/labels.feature | 25 ++++++++++++------- .../step_definitions/labels-steps.mjs | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/test/integration/features/labels.feature b/test/integration/features/labels.feature index 2ecba3b7b3..f43dfdd943 100644 --- a/test/integration/features/labels.feature +++ b/test/integration/features/labels.feature @@ -6,35 +6,42 @@ Feature: Labels When a settings sync is triggered Then the label is available - Scenario: Create Label with leading `#` + Scenario: Create Label with leading `#` on the color code Given no labels exist - And a label is added with a leading `#` + And a label is added with a leading `#` on the color code When a settings sync is triggered Then the label is available - Scenario: Update Label + Scenario: Update Label Color Given a label exists And the color is updated on the existing label When a settings sync is triggered Then the label has the updated color - Scenario: Remove Label - Given a label exists - And the label is removed from the config - When a settings sync is triggered - Then the label is no longer available + @wip + Scenario: Update Label Color with color led by `#` @wip Scenario: Rename a Label + Given a label exists + And the name is updated on the existing label + When a settings sync is triggered + Then the label has the updated color @wip Scenario: Label with color matching config if `#` were stripped Then no call to update the color is performed @wip - Scenario: + Scenario: Config suggests a name update that has already happened Then no call to update the name is performed + Scenario: Remove Label + Given a label exists + And the label is removed from the config + When a settings sync is triggered + Then the label is no longer available + @wip Scenario: Label with a short color code diff --git a/test/integration/features/step_definitions/labels-steps.mjs b/test/integration/features/step_definitions/labels-steps.mjs index eb66cd1ede..78ddf46fd8 100644 --- a/test/integration/features/step_definitions/labels-steps.mjs +++ b/test/integration/features/step_definitions/labels-steps.mjs @@ -50,7 +50,7 @@ Given('a label is added', async function () { ) }) -Given('a label is added with a leading `#`', async function () { +Given('a label is added with a leading `#` on the color code', async function () { this.label = { name: any.word(), color: any.word() } this.server.use( From 3066f065ba65fe4ffd25f99c9a2bb6427da40752 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 26 Jan 2024 23:32:23 -0600 Subject: [PATCH 14/43] test(integration): added a cucumber test for adding a milestone --- test/integration/features/milestones.feature | 7 +++ .../step_definitions/milestones-steps.mjs | 45 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 test/integration/features/milestones.feature create mode 100644 test/integration/features/step_definitions/milestones-steps.mjs diff --git a/test/integration/features/milestones.feature b/test/integration/features/milestones.feature new file mode 100644 index 0000000000..6158125f00 --- /dev/null +++ b/test/integration/features/milestones.feature @@ -0,0 +1,7 @@ +Feature: Milestones + + Scenario: Add Milestone + Given no milestones exist + And a milestone is added + When a settings sync is triggered + Then the milestone is available diff --git a/test/integration/features/step_definitions/milestones-steps.mjs b/test/integration/features/step_definitions/milestones-steps.mjs new file mode 100644 index 0000000000..95c533b7ae --- /dev/null +++ b/test/integration/features/step_definitions/milestones-steps.mjs @@ -0,0 +1,45 @@ +import { dump } from 'js-yaml' +import { StatusCodes } from 'http-status-codes' + +import { Given, Then } from '@cucumber/cucumber' +import { http, HttpResponse } from 'msw' +import assert from 'node:assert' +import any from '@travi/any' + +import settings from '../../../../lib/settings.js' +import { repository } from './common-steps.mjs' + +Given('no milestones exist', async function () { + this.server.use( + http.get(`https://api.github.com/repos/${repository.owner.name}/${repository.name}/milestones`, ({ request }) => { + return HttpResponse.json([]) + }) + ) +}) + +Given('a milestone is added', async function () { + this.milestone = { title: any.word(), description: any.sentence(), state: any.word() } + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer(Buffer.from(dump({ milestones: [this.milestone] }))) + } + ), + http.post( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/milestones`, + async ({ request }) => { + this.savedMilestone = await request.json() + + return new HttpResponse(null, { status: StatusCodes.CREATED }) + } + ) + ) +}) + +Then('the milestone is available', async function () { + assert.deepEqual(this.savedMilestone, this.milestone) +}) From 0e78f262e1b212578f237dfa934cf2828a713d00 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Mon, 29 Jan 2024 20:36:46 -0600 Subject: [PATCH 15/43] test(integration): added a scenario for updating a milestone --- test/integration/features/milestones.feature | 6 +++ .../step_definitions/milestones-steps.mjs | 42 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/test/integration/features/milestones.feature b/test/integration/features/milestones.feature index 6158125f00..9d88a85e1c 100644 --- a/test/integration/features/milestones.feature +++ b/test/integration/features/milestones.feature @@ -5,3 +5,9 @@ Feature: Milestones And a milestone is added When a settings sync is triggered Then the milestone is available + + Scenario: Update a Milestone + Given a milestone exists + And the milestone is updated in the config + When a settings sync is triggered + Then updated milestone is available diff --git a/test/integration/features/step_definitions/milestones-steps.mjs b/test/integration/features/step_definitions/milestones-steps.mjs index 95c533b7ae..6c687daa7b 100644 --- a/test/integration/features/step_definitions/milestones-steps.mjs +++ b/test/integration/features/step_definitions/milestones-steps.mjs @@ -16,6 +16,15 @@ Given('no milestones exist', async function () { }) ) }) +Given('a milestone exists', async function () { + this.milestone = { title: any.word(), description: any.sentence(), state: any.word(), number: any.integer() } + + this.server.use( + http.get(`https://api.github.com/repos/${repository.owner.name}/${repository.name}/milestones`, ({ request }) => { + return HttpResponse.json([this.milestone]) + }) + ) +}) Given('a milestone is added', async function () { this.milestone = { title: any.word(), description: any.sentence(), state: any.word() } @@ -40,6 +49,39 @@ Given('a milestone is added', async function () { ) }) +Given('the milestone is updated in the config', async function () { + this.milestoneUpdates = { description: any.sentence(), state: any.word() } + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from(dump({ milestones: [{ ...this.milestone, ...this.milestoneUpdates }] })) + ) + } + ), + http.patch( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/milestones/${this.milestone.number}`, + async ({ request }) => { + this.updatedMilestone = await request.json() + + return new HttpResponse(null, { status: StatusCodes.OK }) + } + ) + ) +}) + Then('the milestone is available', async function () { assert.deepEqual(this.savedMilestone, this.milestone) }) + +Then('updated milestone is available', async function () { + assert.deepEqual(this.updatedMilestone, { + number: this.milestone.number, + title: this.milestone.title, + ...this.milestoneUpdates + }) +}) From 57c7f7658373170c92668a5bf262e6f1dcafb5ee Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Mon, 29 Jan 2024 21:13:43 -0600 Subject: [PATCH 16/43] test(integration): added a scenario for deleting a milestone --- test/integration/features/milestones.feature | 7 ++++++ .../step_definitions/milestones-steps.mjs | 25 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/test/integration/features/milestones.feature b/test/integration/features/milestones.feature index 9d88a85e1c..97240d73d1 100644 --- a/test/integration/features/milestones.feature +++ b/test/integration/features/milestones.feature @@ -11,3 +11,10 @@ Feature: Milestones And the milestone is updated in the config When a settings sync is triggered Then updated milestone is available + + @focus + Scenario: Delete a milestone + Given a milestone exists + And the milestone is removed from the config + When a settings sync is triggered + Then the milestone is no longer available diff --git a/test/integration/features/step_definitions/milestones-steps.mjs b/test/integration/features/step_definitions/milestones-steps.mjs index 6c687daa7b..f36a1ca2db 100644 --- a/test/integration/features/step_definitions/milestones-steps.mjs +++ b/test/integration/features/step_definitions/milestones-steps.mjs @@ -74,6 +74,27 @@ Given('the milestone is updated in the config', async function () { ) }) +Given('the milestone is removed from the config', async function () { + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer(Buffer.from(dump({ milestones: [] }))) + } + ), + http.delete( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/milestones/:milestoneNumber`, + async ({ params }) => { + this.removedMilestoneNumber = params.milestoneNumber + + return new HttpResponse(null, { status: StatusCodes.OK }) + } + ) + ) +}) + Then('the milestone is available', async function () { assert.deepEqual(this.savedMilestone, this.milestone) }) @@ -85,3 +106,7 @@ Then('updated milestone is available', async function () { ...this.milestoneUpdates }) }) + +Then('the milestone is no longer available', async function () { + assert.equal(this.removedMilestoneNumber, this.milestone.number) +}) From f7f747377d374179c98b4bb52c5c1d38b8137365 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Mon, 29 Jan 2024 21:17:53 -0600 Subject: [PATCH 17/43] test(integration): removed the jest tests for milestones now that they are migrated to cucumber --- test/fixtures/milestones-config.yml | 12 ---- test/integration/plugins/milestones.test.js | 64 --------------------- 2 files changed, 76 deletions(-) delete mode 100644 test/fixtures/milestones-config.yml delete mode 100644 test/integration/plugins/milestones.test.js diff --git a/test/fixtures/milestones-config.yml b/test/fixtures/milestones-config.yml deleted file mode 100644 index 2f14de94a7..0000000000 --- a/test/fixtures/milestones-config.yml +++ /dev/null @@ -1,12 +0,0 @@ -repository: - name: bar - delete_branch_on_merge: true - is_template: true - -milestones: - - title: new-milestone - description: this milestone should get added - state: open - - title: existing-milestone - description: this milestone should get updated - state: closed diff --git a/test/integration/plugins/milestones.test.js b/test/integration/plugins/milestones.test.js deleted file mode 100644 index b5869a619f..0000000000 --- a/test/integration/plugins/milestones.test.js +++ /dev/null @@ -1,64 +0,0 @@ -const { CREATED, NO_CONTENT, OK } = require('http-status-codes') -const { - buildTriggerEvent, - initializeNock, - loadInstance, - repository, - teardownNock, - defineSettingsFileForScenario -} = require('../common') - -describe('milestones plugin', function () { - let probot, githubScope - - beforeEach(async () => { - githubScope = initializeNock() - probot = await loadInstance() - }) - - afterEach(() => { - teardownNock(githubScope) - }) - - it('syncs milestones', async () => { - await defineSettingsFileForScenario('milestones-config.yml', githubScope) - githubScope.patch(`/repos/${repository.owner.name}/${repository.name}`).reply(200) - githubScope.get(`/repos/${repository.owner.name}/${repository.name}/milestones?per_page=100&state=all`).reply(OK, [ - { - number: 42, - title: 'existing-milestone', - description: 'this milestone should get updated', - state: 'open' - }, - { - number: 8, - title: 'old-milestone', - description: 'this milestone should get deleted', - state: 'closed' - } - ]) - githubScope - .post(`/repos/${repository.owner.name}/${repository.name}/milestones`, body => { - expect(body).toMatchObject({ - title: 'new-milestone', - description: 'this milestone should get added', - state: 'open' - }) - return true - }) - .reply(CREATED) - githubScope - .patch(`/repos/${repository.owner.name}/${repository.name}/milestones/42`, body => { - expect(body).toMatchObject({ - title: 'existing-milestone', - description: 'this milestone should get updated', - state: 'closed' - }) - return true - }) - .reply(OK) - githubScope.delete(`/repos/${repository.owner.name}/${repository.name}/milestones/8`).reply(NO_CONTENT) - - await probot.receive(buildTriggerEvent()) - }) -}) From 431de8b30d39a6766d7db79cf57c08e2a992d409 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 9 Feb 2024 15:24:28 -0600 Subject: [PATCH 18/43] test(integration): removed the wip tag from the delete-milestone scenario --- test/integration/features/milestones.feature | 1 - 1 file changed, 1 deletion(-) diff --git a/test/integration/features/milestones.feature b/test/integration/features/milestones.feature index 97240d73d1..da8c8be46d 100644 --- a/test/integration/features/milestones.feature +++ b/test/integration/features/milestones.feature @@ -12,7 +12,6 @@ Feature: Milestones When a settings sync is triggered Then updated milestone is available - @focus Scenario: Delete a milestone Given a milestone exists And the milestone is removed from the config From f039855edac2c0b0339d58c7e2819f676030b731 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 9 Feb 2024 15:37:01 -0600 Subject: [PATCH 19/43] test(msw): reset handlers between tests --- .../features/step_definitions/github-api-steps.mjs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/integration/features/step_definitions/github-api-steps.mjs b/test/integration/features/step_definitions/github-api-steps.mjs index a20d66e113..5e0c93fa41 100644 --- a/test/integration/features/step_definitions/github-api-steps.mjs +++ b/test/integration/features/step_definitions/github-api-steps.mjs @@ -1,9 +1,10 @@ -import { AfterAll, Before, BeforeAll } from '@cucumber/cucumber' +import { After, AfterAll, Before, BeforeAll } from '@cucumber/cucumber' import { setupServer } from 'msw/node' import any from '@travi/any' const server = setupServer() export const githubToken = any.word() + BeforeAll(async function () { server.listen() }) @@ -12,6 +13,10 @@ Before(function () { this.server = server }) +After(function () { + server.resetHandlers() +}) + AfterAll(function () { server.close() }) From 7b575a81ba0e51dc5c6878e89c259798a678ad9e Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 9 Feb 2024 15:51:09 -0600 Subject: [PATCH 20/43] test(integration): defined scenarios for scenarios that do not trigger syncs --- test/integration/features/events.feature | 22 ++++++++ .../step_definitions/collaborators-steps.mjs | 7 +-- .../step_definitions/common-steps.mjs | 51 +++--------------- .../step_definitions/config-steps.mjs | 53 +++++++++++++++++++ .../step_definitions/labels-steps.mjs | 2 +- .../step_definitions/milestones-steps.mjs | 2 +- .../step_definitions/repository-steps.mjs | 37 +++++++++++++ .../features/step_definitions/team-steps.mjs | 2 +- 8 files changed, 125 insertions(+), 51 deletions(-) create mode 100644 test/integration/features/events.feature create mode 100644 test/integration/features/step_definitions/config-steps.mjs create mode 100644 test/integration/features/step_definitions/repository-steps.mjs diff --git a/test/integration/features/events.feature b/test/integration/features/events.feature new file mode 100644 index 0000000000..f71f8f5348 --- /dev/null +++ b/test/integration/features/events.feature @@ -0,0 +1,22 @@ +Feature: Events that do not result in a sync + + Scenario: Push to a non-default branch + Given changes to the settings file are to be pushed to a non-default branch + When the settings file changes are pushed + Then a sync does not get triggered + + Scenario: Repository created when repository does not have a settings file + Given the repository has no settings file + When the repository is created + Then a sync does not get triggered + + @wip + Scenario: Repository edited, but default branch was not changed + When the repository is created + Then a sync does not get triggered + + @wip + Scenario: Repository edited when repository does not have a settings file + Given the repository has no settings file + When the repository is edited + Then a sync does not get triggered diff --git a/test/integration/features/step_definitions/collaborators-steps.mjs b/test/integration/features/step_definitions/collaborators-steps.mjs index 8c2316645d..46a37a8162 100644 --- a/test/integration/features/step_definitions/collaborators-steps.mjs +++ b/test/integration/features/step_definitions/collaborators-steps.mjs @@ -1,12 +1,13 @@ +import { dump } from 'js-yaml' +import { StatusCodes } from 'http-status-codes' + import { Given, Then } from '@cucumber/cucumber' import assert from 'node:assert' import { http, HttpResponse } from 'msw' import any from '@travi/any' -import { repository } from './common-steps.mjs' import settings from '../../../../lib/settings.js' -import { dump } from 'js-yaml' -import { StatusCodes } from 'http-status-codes' +import { repository } from './repository-steps.mjs' const collaboratorLogin = any.word() diff --git a/test/integration/features/step_definitions/common-steps.mjs b/test/integration/features/step_definitions/common-steps.mjs index dd081c4852..85931c11d1 100644 --- a/test/integration/features/step_definitions/common-steps.mjs +++ b/test/integration/features/step_definitions/common-steps.mjs @@ -1,21 +1,10 @@ +import { Probot, ProbotOctokit } from 'probot' + import any from '@travi/any' import { Before, When } from '@cucumber/cucumber' - -import settings from '../../../../lib/settings.js' -import { Probot, ProbotOctokit } from 'probot' import settingsBot from '../../../../index.js' - -let probot - -export const repository = { - default_branch: 'master', - name: 'botland', - owner: { - name: 'bkeepers-inc', - login: 'bkeepers-inc', - email: null - } -} +import { buildRepositoryCreatedEvent, buildRepositoryEditedEvent } from './repository-steps.mjs' +import { buildPushEvent } from './config-steps.mjs' async function loadInstance () { const probot = new Probot({ @@ -32,42 +21,14 @@ async function loadInstance () { return probot } -function buildPushEvent () { - return { - name: 'push', - payload: { - ref: 'refs/heads/master', - repository, - commits: [{ modified: [settings.FILE_NAME], added: [] }] - } - } -} - -function buildRepositoryEditedEvent () { - return { - name: 'repository.edited', - payload: { - changes: { default_branch: { from: any.word() } }, - repository - } - } -} - -function buildRepositoryCreatedEvent () { - return { - name: 'repository.created', - payload: { repository } - } -} - function buildTriggerEvent () { return any.fromList([buildPushEvent(), buildRepositoryCreatedEvent(), buildRepositoryEditedEvent()]) } Before(async function () { - probot = await loadInstance() + this.probot = await loadInstance() }) When('a settings sync is triggered', async function () { - await probot.receive(buildTriggerEvent()) + await this.probot.receive(buildTriggerEvent()) }) diff --git a/test/integration/features/step_definitions/config-steps.mjs b/test/integration/features/step_definitions/config-steps.mjs new file mode 100644 index 0000000000..69d99bbcb7 --- /dev/null +++ b/test/integration/features/step_definitions/config-steps.mjs @@ -0,0 +1,53 @@ +import { Given, Then, When } from '@cucumber/cucumber' + +import settings from '../../../../lib/settings.js' +import { repository } from './repository-steps.mjs' +import any from '@travi/any' +import { http, HttpResponse } from 'msw' +import { StatusCodes } from 'http-status-codes' + +export function buildPushEvent ({ pushBranch } = {}) { + return { + name: 'push', + payload: { + ref: `refs/heads/${pushBranch || repository.default_branch}`, + repository, + commits: [{ modified: [settings.FILE_NAME], added: [] }] + } + } +} + +Given('the repository has no settings file', async function () { + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return new HttpResponse(null, { status: StatusCodes.NOT_FOUND }) + } + ), + http.get( + `https://api.github.com/repos/${repository.owner.name}/.github/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return new HttpResponse(null, { status: StatusCodes.NOT_FOUND }) + } + ) + ) +}) + +Given('changes to the settings file are to be pushed to a non-default branch', async function () { + this.pushBranch = any.word() +}) + +When('the settings file changes are pushed', async function () { + await this.probot.receive(buildPushEvent({ pushBranch: this.pushBranch })) +}) + +Then('a sync does not get triggered', async function () { + // a call to an unexpected endpoint will error, so lack of an error satisfies this step + + return undefined +}) diff --git a/test/integration/features/step_definitions/labels-steps.mjs b/test/integration/features/step_definitions/labels-steps.mjs index 78ddf46fd8..50209325fc 100644 --- a/test/integration/features/step_definitions/labels-steps.mjs +++ b/test/integration/features/step_definitions/labels-steps.mjs @@ -6,8 +6,8 @@ import { Given, Then } from '@cucumber/cucumber' import { http, HttpResponse } from 'msw' import assert from 'node:assert' -import { repository } from './common-steps.mjs' import settings from '../../../../lib/settings.js' +import { repository } from './repository-steps.mjs' Given('no labels exist', async function () { this.server.use( diff --git a/test/integration/features/step_definitions/milestones-steps.mjs b/test/integration/features/step_definitions/milestones-steps.mjs index f36a1ca2db..668a770510 100644 --- a/test/integration/features/step_definitions/milestones-steps.mjs +++ b/test/integration/features/step_definitions/milestones-steps.mjs @@ -7,7 +7,7 @@ import assert from 'node:assert' import any from '@travi/any' import settings from '../../../../lib/settings.js' -import { repository } from './common-steps.mjs' +import { repository } from './repository-steps.mjs' Given('no milestones exist', async function () { this.server.use( diff --git a/test/integration/features/step_definitions/repository-steps.mjs b/test/integration/features/step_definitions/repository-steps.mjs new file mode 100644 index 0000000000..b965b679ff --- /dev/null +++ b/test/integration/features/step_definitions/repository-steps.mjs @@ -0,0 +1,37 @@ +import { When } from '@cucumber/cucumber' +import any from '@travi/any' + +export const repository = { + default_branch: 'master', + name: 'botland', + owner: { + name: 'bkeepers-inc', + login: 'bkeepers-inc', + email: null + } +} + +export function buildRepositoryCreatedEvent () { + return { + name: 'repository.created', + payload: { repository } + } +} + +export function buildRepositoryEditedEvent () { + return { + name: 'repository.edited', + payload: { + changes: { default_branch: { from: any.word() } }, + repository + } + } +} + +When('the repository is created', async function () { + await this.probot.receive(buildRepositoryCreatedEvent()) +}) + +When('the repository is edited', async function () { + await this.probot.receive(buildRepositoryEditedEvent()) +}) diff --git a/test/integration/features/step_definitions/team-steps.mjs b/test/integration/features/step_definitions/team-steps.mjs index dfa06e58ae..50f411b335 100644 --- a/test/integration/features/step_definitions/team-steps.mjs +++ b/test/integration/features/step_definitions/team-steps.mjs @@ -6,8 +6,8 @@ import { Given, Then } from '@cucumber/cucumber' import { http, HttpResponse } from 'msw' import any from '@travi/any' -import { repository } from './common-steps.mjs' import settings from '../../../../lib/settings.js' +import { repository } from './repository-steps.mjs' const teamName = any.word() const teamId = any.integer() From b8154c74f98575f34fe82fe5edf38f7a03452908 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 9 Feb 2024 16:06:22 -0600 Subject: [PATCH 21/43] test(integration): covered the remaining scenarios that do not trigger a sync --- test/integration/features/events.feature | 5 +-- .../step_definitions/repository-steps.mjs | 12 ++++-- test/integration/triggers/push.test.js | 26 ----------- .../triggers/repository-created.test.js | 33 -------------- .../triggers/repository-edited.test.js | 43 ------------------- 5 files changed, 10 insertions(+), 109 deletions(-) delete mode 100644 test/integration/triggers/push.test.js delete mode 100644 test/integration/triggers/repository-created.test.js delete mode 100644 test/integration/triggers/repository-edited.test.js diff --git a/test/integration/features/events.feature b/test/integration/features/events.feature index f71f8f5348..5b150c7d09 100644 --- a/test/integration/features/events.feature +++ b/test/integration/features/events.feature @@ -10,12 +10,11 @@ Feature: Events that do not result in a sync When the repository is created Then a sync does not get triggered - @wip Scenario: Repository edited, but default branch was not changed - When the repository is created + Given the default branch is not changed as part of updating the repository + When the repository is edited Then a sync does not get triggered - @wip Scenario: Repository edited when repository does not have a settings file Given the repository has no settings file When the repository is edited diff --git a/test/integration/features/step_definitions/repository-steps.mjs b/test/integration/features/step_definitions/repository-steps.mjs index b965b679ff..9156c9c80b 100644 --- a/test/integration/features/step_definitions/repository-steps.mjs +++ b/test/integration/features/step_definitions/repository-steps.mjs @@ -1,4 +1,4 @@ -import { When } from '@cucumber/cucumber' +import { Given, When } from '@cucumber/cucumber' import any from '@travi/any' export const repository = { @@ -18,20 +18,24 @@ export function buildRepositoryCreatedEvent () { } } -export function buildRepositoryEditedEvent () { +export function buildRepositoryEditedEvent ({ changes } = {}) { return { name: 'repository.edited', payload: { - changes: { default_branch: { from: any.word() } }, + changes: { ...(changes || { default_branch: { from: any.word() } }) }, repository } } } +Given('the default branch is not changed as part of updating the repository', async function () { + this.repositoryEditedChanges = any.simpleObject() +}) + When('the repository is created', async function () { await this.probot.receive(buildRepositoryCreatedEvent()) }) When('the repository is edited', async function () { - await this.probot.receive(buildRepositoryEditedEvent()) + await this.probot.receive(buildRepositoryEditedEvent({ changes: this.repositoryEditedChanges })) }) diff --git a/test/integration/triggers/push.test.js b/test/integration/triggers/push.test.js deleted file mode 100644 index f16fd73c60..0000000000 --- a/test/integration/triggers/push.test.js +++ /dev/null @@ -1,26 +0,0 @@ -const settings = require('../../../lib/settings') -const { initializeNock, loadInstance, repository, teardownNock } = require('../common') - -describe('push trigger', function () { - let probot, githubScope - - beforeEach(async () => { - githubScope = initializeNock() - probot = await loadInstance() - }) - - afterEach(() => { - teardownNock(githubScope) - }) - - it('does not apply configuration when not on the default branch', async () => { - await probot.receive({ - name: 'push', - payload: { - ref: 'refs/heads/wip', - repository, - commits: [{ modified: [settings.FILE_NAME], added: [] }] - } - }) - }) -}) diff --git a/test/integration/triggers/repository-created.test.js b/test/integration/triggers/repository-created.test.js deleted file mode 100644 index cb3427d58b..0000000000 --- a/test/integration/triggers/repository-created.test.js +++ /dev/null @@ -1,33 +0,0 @@ -const { NOT_FOUND } = require('http-status-codes') -const settings = require('../../../lib/settings') -const { buildRepositoryCreatedEvent, initializeNock, loadInstance, repository, teardownNock } = require('../common') - -describe('repository.created trigger', function () { - let probot, githubScope - - beforeEach(async () => { - githubScope = initializeNock() - probot = await loadInstance() - }) - - afterEach(() => { - teardownNock(githubScope) - }) - - it('does not apply configuration when the repository does not have a settings.yml', async () => { - githubScope - .get(`/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent(settings.FILE_NAME)}`) - .reply(NOT_FOUND, { - message: 'Not Found', - documentation_url: 'https://developer.github.com/v3/repos/contents/#get-contents' - }) - githubScope - .get(`/repos/${repository.owner.name}/.github/contents/${encodeURIComponent(settings.FILE_NAME)}`) - .reply(NOT_FOUND, { - message: 'Not Found', - documentation_url: 'https://developer.github.com/v3/repos/contents/#get-contents' - }) - - await probot.receive(buildRepositoryCreatedEvent()) - }) -}) diff --git a/test/integration/triggers/repository-edited.test.js b/test/integration/triggers/repository-edited.test.js deleted file mode 100644 index e9508c9002..0000000000 --- a/test/integration/triggers/repository-edited.test.js +++ /dev/null @@ -1,43 +0,0 @@ -const { NOT_FOUND } = require('http-status-codes') -const any = require('@travi/any') -const settings = require('../../../lib/settings') -const { buildRepositoryEditedEvent, initializeNock, loadInstance, repository, teardownNock } = require('../common') - -describe('repository.edited trigger', function () { - let probot, githubScope - - beforeEach(async () => { - githubScope = initializeNock() - probot = await loadInstance() - }) - - afterEach(() => { - teardownNock(githubScope) - }) - - it('does not apply configuration when the default branch was not changed', async () => { - await probot.receive({ - name: 'repository.edited', - payload: { - changes: any.simpleObject() - } - }) - }) - - it('does not apply configuration when the repository does not have a settings.yml', async () => { - githubScope - .get(`/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent(settings.FILE_NAME)}`) - .reply(NOT_FOUND, { - message: 'Not Found', - documentation_url: 'https://developer.github.com/v3/repos/contents/#get-contents' - }) - githubScope - .get(`/repos/${repository.owner.name}/.github/contents/${encodeURIComponent(settings.FILE_NAME)}`) - .reply(NOT_FOUND, { - message: 'Not Found', - documentation_url: 'https://developer.github.com/v3/repos/contents/#get-contents' - }) - - await probot.receive(buildRepositoryEditedEvent()) - }) -}) From 59fe45649cd8378a9da6e837c3d45c195282ea44 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 9 Feb 2024 16:10:44 -0600 Subject: [PATCH 22/43] test(integration): named the repository-events steps file more clearly to avoid collision with the upcoming steps file for repository settings rather than events --- .../step_definitions/collaborators-steps.mjs | 3 ++- .../features/step_definitions/common-steps.mjs | 12 +++++++++++- .../features/step_definitions/config-steps.mjs | 2 +- .../features/step_definitions/labels-steps.mjs | 3 ++- .../features/step_definitions/milestones-steps.mjs | 3 ++- ...ository-steps.mjs => repository-events-steps.mjs} | 10 +--------- .../features/step_definitions/team-steps.mjs | 3 ++- 7 files changed, 21 insertions(+), 15 deletions(-) rename test/integration/features/step_definitions/{repository-steps.mjs => repository-events-steps.mjs} (84%) diff --git a/test/integration/features/step_definitions/collaborators-steps.mjs b/test/integration/features/step_definitions/collaborators-steps.mjs index 46a37a8162..6559525fbb 100644 --- a/test/integration/features/step_definitions/collaborators-steps.mjs +++ b/test/integration/features/step_definitions/collaborators-steps.mjs @@ -7,7 +7,8 @@ import { http, HttpResponse } from 'msw' import any from '@travi/any' import settings from '../../../../lib/settings.js' -import { repository } from './repository-steps.mjs' + +import { repository } from './common-steps.mjs' const collaboratorLogin = any.word() diff --git a/test/integration/features/step_definitions/common-steps.mjs b/test/integration/features/step_definitions/common-steps.mjs index 85931c11d1..da533078d5 100644 --- a/test/integration/features/step_definitions/common-steps.mjs +++ b/test/integration/features/step_definitions/common-steps.mjs @@ -3,9 +3,19 @@ import { Probot, ProbotOctokit } from 'probot' import any from '@travi/any' import { Before, When } from '@cucumber/cucumber' import settingsBot from '../../../../index.js' -import { buildRepositoryCreatedEvent, buildRepositoryEditedEvent } from './repository-steps.mjs' +import { buildRepositoryCreatedEvent, buildRepositoryEditedEvent } from './repository-events-steps.mjs' import { buildPushEvent } from './config-steps.mjs' +export const repository = { + default_branch: 'master', + name: 'botland', + owner: { + name: 'bkeepers-inc', + login: 'bkeepers-inc', + email: null + } +} + async function loadInstance () { const probot = new Probot({ appId: 1, diff --git a/test/integration/features/step_definitions/config-steps.mjs b/test/integration/features/step_definitions/config-steps.mjs index 69d99bbcb7..04288a0996 100644 --- a/test/integration/features/step_definitions/config-steps.mjs +++ b/test/integration/features/step_definitions/config-steps.mjs @@ -1,10 +1,10 @@ import { Given, Then, When } from '@cucumber/cucumber' import settings from '../../../../lib/settings.js' -import { repository } from './repository-steps.mjs' import any from '@travi/any' import { http, HttpResponse } from 'msw' import { StatusCodes } from 'http-status-codes' +import { repository } from './common-steps.mjs' export function buildPushEvent ({ pushBranch } = {}) { return { diff --git a/test/integration/features/step_definitions/labels-steps.mjs b/test/integration/features/step_definitions/labels-steps.mjs index 50209325fc..acfe9e0c70 100644 --- a/test/integration/features/step_definitions/labels-steps.mjs +++ b/test/integration/features/step_definitions/labels-steps.mjs @@ -7,7 +7,8 @@ import { http, HttpResponse } from 'msw' import assert from 'node:assert' import settings from '../../../../lib/settings.js' -import { repository } from './repository-steps.mjs' + +import { repository } from './common-steps.mjs' Given('no labels exist', async function () { this.server.use( diff --git a/test/integration/features/step_definitions/milestones-steps.mjs b/test/integration/features/step_definitions/milestones-steps.mjs index 668a770510..4e7b474f41 100644 --- a/test/integration/features/step_definitions/milestones-steps.mjs +++ b/test/integration/features/step_definitions/milestones-steps.mjs @@ -7,7 +7,8 @@ import assert from 'node:assert' import any from '@travi/any' import settings from '../../../../lib/settings.js' -import { repository } from './repository-steps.mjs' + +import { repository } from './common-steps.mjs' Given('no milestones exist', async function () { this.server.use( diff --git a/test/integration/features/step_definitions/repository-steps.mjs b/test/integration/features/step_definitions/repository-events-steps.mjs similarity index 84% rename from test/integration/features/step_definitions/repository-steps.mjs rename to test/integration/features/step_definitions/repository-events-steps.mjs index 9156c9c80b..abebe4612f 100644 --- a/test/integration/features/step_definitions/repository-steps.mjs +++ b/test/integration/features/step_definitions/repository-events-steps.mjs @@ -1,15 +1,7 @@ import { Given, When } from '@cucumber/cucumber' import any from '@travi/any' -export const repository = { - default_branch: 'master', - name: 'botland', - owner: { - name: 'bkeepers-inc', - login: 'bkeepers-inc', - email: null - } -} +import { repository } from './common-steps.mjs' export function buildRepositoryCreatedEvent () { return { diff --git a/test/integration/features/step_definitions/team-steps.mjs b/test/integration/features/step_definitions/team-steps.mjs index 50f411b335..4bf8701b28 100644 --- a/test/integration/features/step_definitions/team-steps.mjs +++ b/test/integration/features/step_definitions/team-steps.mjs @@ -7,7 +7,8 @@ import { http, HttpResponse } from 'msw' import any from '@travi/any' import settings from '../../../../lib/settings.js' -import { repository } from './repository-steps.mjs' + +import { repository } from './common-steps.mjs' const teamName = any.word() const teamId = any.integer() From cbde318e3ec396c93d2ff68210598074e2094094 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 9 Feb 2024 17:03:01 -0600 Subject: [PATCH 23/43] test(integration): defined basic scenarios for environments --- .../integration/features/environments.feature | 21 +++++++++ .../step_definitions/environments-steps.mjs | 45 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 test/integration/features/environments.feature create mode 100644 test/integration/features/step_definitions/environments-steps.mjs diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature new file mode 100644 index 0000000000..b3dd46dbc4 --- /dev/null +++ b/test/integration/features/environments.feature @@ -0,0 +1,21 @@ +Feature: Environments + + Scenario: Define an Environment + Given no environments are defined + And an environment is defined in the config + When a settings sync is triggered + Then the environment is available + + @wip + Scenario: Update an Environment + Given an environment exists + And the environment is modified in the config + When a settings sync is triggered + Then the environment is update + + @wip + Scenario: Delete an Environment + Given an environment exists + And the environment is removed from the config + When a settings sync is triggered + Then the environment is no longer available diff --git a/test/integration/features/step_definitions/environments-steps.mjs b/test/integration/features/step_definitions/environments-steps.mjs new file mode 100644 index 0000000000..05a05c0ad2 --- /dev/null +++ b/test/integration/features/step_definitions/environments-steps.mjs @@ -0,0 +1,45 @@ +import { dump } from 'js-yaml' +import { StatusCodes } from 'http-status-codes' + +import { Given, Then } from '@cucumber/cucumber' +import { http, HttpResponse } from 'msw' +import any from '@travi/any' +import assert from 'node:assert' + +import { repository } from './common-steps.mjs' +import settings from '../../../../lib/settings.js' + +Given('no environments are defined', async function () { + this.server.use( + http.get(`https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments`, () => { + return HttpResponse.json({ environments: [] }) + }) + ) +}) + +Given('an environment is defined in the config', async function () { + this.environmentName = any.word() + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer(Buffer.from(dump({ environments: [{ name: this.environmentName }] }))) + } + ), + http.put( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/:environmentName`, + async ({ params }) => { + this.createdEnvironmentName = params.environmentName + + return new HttpResponse(null, { status: StatusCodes.CREATED }) + } + ) + ) +}) + +Then('the environment is available', async function () { + assert.equal(this.createdEnvironmentName, this.environmentName) +}) From cd98c1f14484f2ef696f58384ce494f295e5c03e Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 9 Feb 2024 17:06:45 -0600 Subject: [PATCH 24/43] docs(environments): improved the wrapping of the environments config description --- docs/configuration.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 91a1531d7e..ac245696fe 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -103,7 +103,11 @@ collaborators: # * `triage` - Recommended for contributors who need to proactively manage issues and pull requests without write access. # See https://docs.github.com/en/rest/deployments/environments#create-or-update-an-environment for available options -# Note: deployment_branch_policy differs from the API for ease of use. Either protected_branches (boolean) OR custom_branches (array of strings) can be provided; this will manage the API requirements under the hood. See https://docs.github.com/en/rest/deployments/branch-policies for documentation of custom_branches. If both are provided in an unexpected manner, protected_branches will be used. +# Note: deployment_branch_policy differs from the API for ease of use. +# Either protected_branches (boolean) OR custom_branches (array of strings) can be provided; +# this will manage the API requirements under the hood. +# See https://docs.github.com/en/rest/deployments/branch-policies for documentation of custom_branches. +# If both are provided in an unexpected manner, protected_branches will be used. # Either removing or simply not setting deployment_branch_policy will restore the default 'All branches' setting. environments: - name: production From eef6dee04b28aa92abd26e7768f49c336d610e77 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 16 Feb 2024 14:46:01 -0600 Subject: [PATCH 25/43] test(integration): pinned the behavior of updating an environment resource --- lib/plugins/environments.js | 117 +++++++++++------- .../integration/features/environments.feature | 3 +- .../step_definitions/environments-steps.mjs | 51 +++++++- 3 files changed, 117 insertions(+), 54 deletions(-) diff --git a/lib/plugins/environments.js b/lib/plugins/environments.js index 9e2f59eaf8..aecb7dac43 100644 --- a/lib/plugins/environments.js +++ b/lib/plugins/environments.js @@ -2,6 +2,62 @@ const Diffable = require('./diffable') const environmentRepoEndpoint = '/repos/:org/:repo/environments/:environment_name' +function shouldUseProtectedBranches (protectedBranches, customBranchPolicies) { + return !!(protectedBranches || customBranchPolicies === undefined || customBranchPolicies === null) +} + +function attributeSorter (a, b) { + if (a.id < b.id) return -1 + if (a.id > b.id) return 1 + if (a.type < b.type) return -1 + if (a.type > b.type) return 1 + return 0 +} + +function reviewersToString (attrs) { + if (attrs === null || attrs === undefined) { + return '' + } else { + attrs.sort(attributeSorter) + + return JSON.stringify( + attrs.map(reviewer => { + return { + id: reviewer.id, + type: reviewer.type + } + }) + ) + } +} + +function deploymentBranchPolicyToString (attrs) { + if (attrs === null || attrs === undefined) { + return '' + } else { + return JSON.stringify( + shouldUseProtectedBranches(attrs.protected_branches, attrs.custom_branches) + ? { protected_branches: true } + : { custom_branches: attrs.custom_branches.sort() } + ) + } +} + +function waitTimerHasChanged (existing, attrs) { + return (existing.wait_timer || 0) !== attrs.wait_timer +} + +function reviewersHasChanged (existing, attrs) { + return reviewersToString(existing.reviewers) !== reviewersToString(attrs.reviewers) +} + +function deploymentBranchPolicyHasChanged (existing, attrs) { + return ( + deploymentBranchPolicyToString(existing.deployment_branch_policy) !== + deploymentBranchPolicyToString(attrs.deployment_branch_policy) + ) +} + module.exports = class Environments extends Diffable { constructor (...args) { super(...args) @@ -21,6 +77,7 @@ module.exports = class Environments extends Diffable { org: this.repo.owner, repo: this.repo.repo }) + return Promise.all( environments.map(async environment => { if (environment.deployment_branch_policy) { @@ -39,6 +96,7 @@ module.exports = class Environments extends Diffable { } } } + return { ...environment, // Force all names to lowercase to avoid comparison issues. @@ -54,17 +112,18 @@ module.exports = class Environments extends Diffable { changed (existing, attrs) { if (!attrs.wait_timer) attrs.wait_timer = 0 + return ( - (existing.wait_timer || 0) !== attrs.wait_timer || - this.reviewersToString(existing.reviewers) !== this.reviewersToString(attrs.reviewers) || - this.deploymentBranchPolicyToString(existing.deployment_branch_policy) !== - this.deploymentBranchPolicyToString(attrs.deployment_branch_policy) + waitTimerHasChanged(existing, attrs) || + reviewersHasChanged(existing, attrs) || + deploymentBranchPolicyHasChanged(existing, attrs) ) } async update (existing, attrs) { if (existing.deployment_branch_policy && existing.deployment_branch_policy.custom_branches) { const branchPolicies = await this.getDeploymentBranchPolicies(this.repo.owner, this.repo.repo, existing.name) + await Promise.all( branchPolicies.map(branchPolicy => this.github.request( @@ -79,15 +138,17 @@ module.exports = class Environments extends Diffable { ) ) } + return this.add(attrs) } async add (attrs) { await this.github.request(`PUT ${environmentRepoEndpoint}`, this.toParams({ name: attrs.name }, attrs)) + if (attrs.deployment_branch_policy && attrs.deployment_branch_policy.custom_branches) { await Promise.all( attrs.deployment_branch_policy.custom_branches.map(name => - this.github.request(`POST /repos/:org/:repo/environments/:environment_name/deployment-branch-policies`, { + this.github.request('POST /repos/:org/:repo/environments/:environment_name/deployment-branch-policies', { org: this.repo.owner, repo: this.repo.repo, environment_name: attrs.name, @@ -106,40 +167,6 @@ module.exports = class Environments extends Diffable { }) } - reviewersToString (attrs) { - if (attrs === null || attrs === undefined) { - return '' - } else { - attrs.sort((a, b) => { - if (a.id < b.id) return -1 - if (a.id > b.id) return 1 - if (a.type < b.type) return -1 - if (a.type > b.type) return 1 - return 0 - }) - return JSON.stringify( - attrs.map(reviewer => { - return { - id: reviewer.id, - type: reviewer.type - } - }) - ) - } - } - - deploymentBranchPolicyToString (attrs) { - if (attrs === null || attrs === undefined) { - return '' - } else { - return JSON.stringify( - this.shouldUseProtectedBranches(attrs.protected_branches, attrs.custom_branches) - ? { protected_branches: true } - : { custom_branches: attrs.custom_branches.sort() } - ) - } - } - async getDeploymentBranchPolicies (owner, repo, environmentName) { const { data: { branch_policies: branchPolicies } @@ -148,18 +175,20 @@ module.exports = class Environments extends Diffable { repo, environment_name: environmentName }) + return branchPolicies } toParams (existing, attrs) { const deploymentBranchPolicy = attrs.deployment_branch_policy - ? this.shouldUseProtectedBranches( + ? shouldUseProtectedBranches( attrs.deployment_branch_policy.protected_branches, attrs.deployment_branch_policy.custom_branches ) ? { protected_branches: true, custom_branch_policies: false } : { protected_branches: false, custom_branch_policies: true } : null + return { environment_name: existing.name, repo: this.repo.repo, @@ -169,12 +198,4 @@ module.exports = class Environments extends Diffable { deployment_branch_policy: deploymentBranchPolicy } } - - shouldUseProtectedBranches (protectedBranches, customBranchPolicies) { - if (protectedBranches || customBranchPolicies === undefined || customBranchPolicies === null) { - return true // Returning booleans like this to avoid unexpected datatypes that result in truthy values - } else { - return false - } - } } diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature index b3dd46dbc4..6c5b2ae6a2 100644 --- a/test/integration/features/environments.feature +++ b/test/integration/features/environments.feature @@ -6,12 +6,11 @@ Feature: Environments When a settings sync is triggered Then the environment is available - @wip Scenario: Update an Environment Given an environment exists And the environment is modified in the config When a settings sync is triggered - Then the environment is update + Then the environment is updated @wip Scenario: Delete an Environment diff --git a/test/integration/features/step_definitions/environments-steps.mjs b/test/integration/features/step_definitions/environments-steps.mjs index 05a05c0ad2..4dcb23e01b 100644 --- a/test/integration/features/step_definitions/environments-steps.mjs +++ b/test/integration/features/step_definitions/environments-steps.mjs @@ -17,6 +17,16 @@ Given('no environments are defined', async function () { ) }) +Given('an environment exists', async function () { + this.environment = { name: any.word(), wait_timer: any.integer(), deployment_branch_policy: null } + + this.server.use( + http.get(`https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments`, () => { + return HttpResponse.json({ environments: [this.environment] }) + }) + ) +}) + Given('an environment is defined in the config', async function () { this.environmentName = any.word() @@ -30,9 +40,38 @@ Given('an environment is defined in the config', async function () { } ), http.put( - `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/:environmentName`, - async ({ params }) => { - this.createdEnvironmentName = params.environmentName + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environmentName}`, + async ({ params, request }) => { + this.createdEnvironment = await request.json() + + return new HttpResponse(null, { status: StatusCodes.CREATED }) + } + ) + ) +}) + +Given('the environment is modified in the config', async function () { + this.environmentUpdates = { wait_timer: any.integer(), deployment_branch_policy: null } + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from( + dump({ + environments: [{ name: this.environment.name, ...this.environmentUpdates }] + }) + ) + ) + } + ), + http.put( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environment.name}`, + async ({ request }) => { + this.updatedEnvironment = await request.json() return new HttpResponse(null, { status: StatusCodes.CREATED }) } @@ -41,5 +80,9 @@ Given('an environment is defined in the config', async function () { }) Then('the environment is available', async function () { - assert.equal(this.createdEnvironmentName, this.environmentName) + assert.deepEqual(this.createdEnvironment, { deployment_branch_policy: null }) +}) + +Then('the environment is updated', async function () { + assert.deepEqual(this.updatedEnvironment, this.environmentUpdates) }) From 349e7851ee7ea79313d30923bbcd4cda21ddcaa5 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 16 Feb 2024 14:58:13 -0600 Subject: [PATCH 26/43] test(integration): pinned the behavior of deleting a environment --- .../integration/features/environments.feature | 1 - .../step_definitions/environments-steps.mjs | 25 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature index 6c5b2ae6a2..1bd0ed9957 100644 --- a/test/integration/features/environments.feature +++ b/test/integration/features/environments.feature @@ -12,7 +12,6 @@ Feature: Environments When a settings sync is triggered Then the environment is updated - @wip Scenario: Delete an Environment Given an environment exists And the environment is removed from the config diff --git a/test/integration/features/step_definitions/environments-steps.mjs b/test/integration/features/step_definitions/environments-steps.mjs index 4dcb23e01b..1cbc1cf802 100644 --- a/test/integration/features/step_definitions/environments-steps.mjs +++ b/test/integration/features/step_definitions/environments-steps.mjs @@ -79,6 +79,27 @@ Given('the environment is modified in the config', async function () { ) }) +Given('the environment is removed from the config', async function () { + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer(Buffer.from(dump({ environments: [] }))) + } + ), + http.delete( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/:environmentName`, + async ({ params }) => { + this.removedEnvironment = params.environmentName + + return new HttpResponse(null, { status: StatusCodes.NO_CONTENT }) + } + ) + ) +}) + Then('the environment is available', async function () { assert.deepEqual(this.createdEnvironment, { deployment_branch_policy: null }) }) @@ -86,3 +107,7 @@ Then('the environment is available', async function () { Then('the environment is updated', async function () { assert.deepEqual(this.updatedEnvironment, this.environmentUpdates) }) + +Then('the environment is no longer available', async function () { + assert.equal(this.removedEnvironment, this.environment.name) +}) From fb164a580a642e634c8e548a59dc7189efa89b01 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 16 Feb 2024 15:15:55 -0600 Subject: [PATCH 27/43] test(integration): defined remaining scenarios for pinning environments behaviors --- .../integration/features/environments.feature | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature index 1bd0ed9957..02052ee84e 100644 --- a/test/integration/features/environments.feature +++ b/test/integration/features/environments.feature @@ -17,3 +17,33 @@ Feature: Environments And the environment is removed from the config When a settings sync is triggered Then the environment is no longer available + + @wip + Scenario: Define an Environment with Reviewers + + @wip + Scenario: Update the reviewer type for an environment + + @wip + Scenario: Update the id of a reviewer for an environment + + @wip + Scenario: Add a reviewer to an environment + + @wip + Scenario: Remove a reviewer from an environment + + @wip + Scenario: Define an Environment with a Deployment Branch Policy + + @wip + Scenario: Define a Deployment Branch Policy for an exiting environment + + @wip + Scenario: Update the Deployment Branch Policy for an environment + + @wip + Scenario: Reviewers are unchanged, but are sorted differently than the api + + @wip + Scenario: Unchanged wait-timer considered equivalent to default From d6a259e35e6c3fe5c2b5bdbbfeede2d85ddfe936 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 16 Feb 2024 15:43:20 -0600 Subject: [PATCH 28/43] test(integration): pinned the behavior for defining an environment with reviewers --- .../integration/features/environments.feature | 17 ++++++- .../step_definitions/environments-steps.mjs | 49 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature index 02052ee84e..14345d07c7 100644 --- a/test/integration/features/environments.feature +++ b/test/integration/features/environments.feature @@ -18,32 +18,47 @@ Feature: Environments When a settings sync is triggered Then the environment is no longer available - @wip Scenario: Define an Environment with Reviewers + Given no environments are defined + And an environment is defined in the config with reviewers + When a settings sync is triggered + Then the environment is available with reviewers @wip Scenario: Update the reviewer type for an environment + Given an environment exists with reviewers defined + When a settings sync is triggered @wip Scenario: Update the id of a reviewer for an environment + Given an environment exists with reviewers defined + When a settings sync is triggered @wip Scenario: Add a reviewer to an environment + When a settings sync is triggered @wip Scenario: Remove a reviewer from an environment + Given an environment exists with reviewers defined + When a settings sync is triggered @wip Scenario: Define an Environment with a Deployment Branch Policy + When a settings sync is triggered @wip Scenario: Define a Deployment Branch Policy for an exiting environment + When a settings sync is triggered @wip Scenario: Update the Deployment Branch Policy for an environment + When a settings sync is triggered @wip Scenario: Reviewers are unchanged, but are sorted differently than the api + When a settings sync is triggered @wip Scenario: Unchanged wait-timer considered equivalent to default + When a settings sync is triggered diff --git a/test/integration/features/step_definitions/environments-steps.mjs b/test/integration/features/step_definitions/environments-steps.mjs index 1cbc1cf802..f4aaa04f37 100644 --- a/test/integration/features/step_definitions/environments-steps.mjs +++ b/test/integration/features/step_definitions/environments-steps.mjs @@ -9,6 +9,10 @@ import assert from 'node:assert' import { repository } from './common-steps.mjs' import settings from '../../../../lib/settings.js' +function anyReviewer () { + return { id: any.integer(), type: any.fromList(['User', 'Team']) } +} + Given('no environments are defined', async function () { this.server.use( http.get(`https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments`, () => { @@ -27,6 +31,21 @@ Given('an environment exists', async function () { ) }) +Given('an environment exists with reviewers defined', async function () { + this.environment = { + name: any.word(), + wait_timer: any.integer(), + deployment_branch_policy: null, + reviewers: any.listOf(anyReviewer) + } + + this.server.use( + http.get(`https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments`, () => { + return HttpResponse.json({ environments: [this.environment] }) + }) + ) +}) + Given('an environment is defined in the config', async function () { this.environmentName = any.word() @@ -50,6 +69,32 @@ Given('an environment is defined in the config', async function () { ) }) +Given('an environment is defined in the config with reviewers', async function () { + this.environmentName = any.word() + this.reviewers = any.listOf(anyReviewer) + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from(dump({ environments: [{ name: this.environmentName, reviewers: this.reviewers }] })) + ) + } + ), + http.put( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environmentName}`, + async ({ params, request }) => { + this.createdEnvironment = await request.json() + + return new HttpResponse(null, { status: StatusCodes.CREATED }) + } + ) + ) +}) + Given('the environment is modified in the config', async function () { this.environmentUpdates = { wait_timer: any.integer(), deployment_branch_policy: null } @@ -104,6 +149,10 @@ Then('the environment is available', async function () { assert.deepEqual(this.createdEnvironment, { deployment_branch_policy: null }) }) +Then('the environment is available with reviewers', async function () { + assert.deepEqual(this.createdEnvironment, { deployment_branch_policy: null, reviewers: this.reviewers }) +}) + Then('the environment is updated', async function () { assert.deepEqual(this.updatedEnvironment, this.environmentUpdates) }) From 0f8b83b1766ff51dbf103e94d19b4cf647862fed Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 16 Feb 2024 16:28:43 -0600 Subject: [PATCH 29/43] test(integration): pinned the behavior of updating an environment reviewer type --- .../integration/features/environments.feature | 3 +- .../step_definitions/environments-steps.mjs | 44 ++++++++++++++++++- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature index 14345d07c7..8da2b96d35 100644 --- a/test/integration/features/environments.feature +++ b/test/integration/features/environments.feature @@ -24,10 +24,11 @@ Feature: Environments When a settings sync is triggered Then the environment is available with reviewers - @wip Scenario: Update the reviewer type for an environment Given an environment exists with reviewers defined + And a reviewer has its type changed When a settings sync is triggered + Then the reviewer type is updated @wip Scenario: Update the id of a reviewer for an environment diff --git a/test/integration/features/step_definitions/environments-steps.mjs b/test/integration/features/step_definitions/environments-steps.mjs index f4aaa04f37..9654a90acf 100644 --- a/test/integration/features/step_definitions/environments-steps.mjs +++ b/test/integration/features/step_definitions/environments-steps.mjs @@ -9,8 +9,10 @@ import assert from 'node:assert' import { repository } from './common-steps.mjs' import settings from '../../../../lib/settings.js' +const possibleReviewerTypes = ['User', 'Team'] + function anyReviewer () { - return { id: any.integer(), type: any.fromList(['User', 'Team']) } + return { id: any.integer(), type: any.fromList(possibleReviewerTypes) } } Given('no environments are defined', async function () { @@ -118,7 +120,7 @@ Given('the environment is modified in the config', async function () { async ({ request }) => { this.updatedEnvironment = await request.json() - return new HttpResponse(null, { status: StatusCodes.CREATED }) + return new HttpResponse(null, { status: StatusCodes.OK }) } ) ) @@ -145,6 +147,37 @@ Given('the environment is removed from the config', async function () { ) }) +Given('a reviewer has its type changed', async function () { + const [reviewerToBeUpdated, ...unchangedReviewers] = this.environment.reviewers + const alternativeType = possibleReviewerTypes.find(type => type !== reviewerToBeUpdated.type) + this.updatedReviewer = { ...reviewerToBeUpdated, type: alternativeType } + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from( + dump({ + environments: [{ ...this.environment, reviewers: [...unchangedReviewers, this.updatedReviewer] }] + }) + ) + ) + } + ), + http.put( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environment.name}`, + async ({ request }) => { + this.updatedEnvironment = await request.json() + + return new HttpResponse(null, { status: StatusCodes.OK }) + } + ) + ) +}) + Then('the environment is available', async function () { assert.deepEqual(this.createdEnvironment, { deployment_branch_policy: null }) }) @@ -160,3 +193,10 @@ Then('the environment is updated', async function () { Then('the environment is no longer available', async function () { assert.equal(this.removedEnvironment, this.environment.name) }) + +Then('the reviewer type is updated', async function () { + assert.deepEqual( + this.updatedEnvironment.reviewers.find(reviewer => reviewer.id === this.updatedReviewer.id), + this.updatedReviewer + ) +}) From 1fa1656241b5fbb68875f96413f78601b9a3e33f Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 16 Feb 2024 16:34:47 -0600 Subject: [PATCH 30/43] test(integration): pinned the behavior of updating the id of an environment reviewer --- .../integration/features/environments.feature | 3 +- .../step_definitions/environments-steps.mjs | 39 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature index 8da2b96d35..eac4e943b6 100644 --- a/test/integration/features/environments.feature +++ b/test/integration/features/environments.feature @@ -30,10 +30,11 @@ Feature: Environments When a settings sync is triggered Then the reviewer type is updated - @wip Scenario: Update the id of a reviewer for an environment Given an environment exists with reviewers defined + And a reviewer has its id changed When a settings sync is triggered + Then the reviewer id is updated @wip Scenario: Add a reviewer to an environment diff --git a/test/integration/features/step_definitions/environments-steps.mjs b/test/integration/features/step_definitions/environments-steps.mjs index 9654a90acf..b015950c23 100644 --- a/test/integration/features/step_definitions/environments-steps.mjs +++ b/test/integration/features/step_definitions/environments-steps.mjs @@ -178,6 +178,36 @@ Given('a reviewer has its type changed', async function () { ) }) +Given('a reviewer has its id changed', async function () { + const [reviewerToBeUpdated, ...unchangedReviewers] = this.environment.reviewers + this.updatedReviewer = { ...reviewerToBeUpdated, id: any.integer() } + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from( + dump({ + environments: [{ ...this.environment, reviewers: [...unchangedReviewers, this.updatedReviewer] }] + }) + ) + ) + } + ), + http.put( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environment.name}`, + async ({ request }) => { + this.updatedEnvironment = await request.json() + + return new HttpResponse(null, { status: StatusCodes.OK }) + } + ) + ) +}) + Then('the environment is available', async function () { assert.deepEqual(this.createdEnvironment, { deployment_branch_policy: null }) }) @@ -195,6 +225,15 @@ Then('the environment is no longer available', async function () { }) Then('the reviewer type is updated', async function () { + assert.equal(this.updatedEnvironment.reviewers.length, this.environment.reviewers.length) + assert.deepEqual( + this.updatedEnvironment.reviewers.find(reviewer => reviewer.id === this.updatedReviewer.id).type, + this.updatedReviewer.type + ) +}) + +Then('the reviewer id is updated', async function () { + assert.equal(this.updatedEnvironment.reviewers.length, this.environment.reviewers.length) assert.deepEqual( this.updatedEnvironment.reviewers.find(reviewer => reviewer.id === this.updatedReviewer.id), this.updatedReviewer From 6e7ecfeeacdbe4fb6e127e0f6f8c49a1ecb3a536 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 16 Feb 2024 16:58:38 -0600 Subject: [PATCH 31/43] test(integration): pinned the behavior of defining basic repository configuration --- test/integration/features/repository.feature | 19 ++++++++++ .../step_definitions/repository-steps.mjs | 38 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 test/integration/features/repository.feature create mode 100644 test/integration/features/step_definitions/repository-steps.mjs diff --git a/test/integration/features/repository.feature b/test/integration/features/repository.feature new file mode 100644 index 0000000000..c6605b156d --- /dev/null +++ b/test/integration/features/repository.feature @@ -0,0 +1,19 @@ +Feature: Repository + + @focus + Scenario: Basic repository settings + Given basic repository config is defined + When a settings sync is triggered + Then the repository will be configured + + @wip + Scenario: Repository with topics defined + When a settings sync is triggered + + @wip + Scenario: Repository with vulnerability alerts enabled + When a settings sync is triggered + + @wip + Scenario: Repository with security fixes enabled + When a settings sync is triggered diff --git a/test/integration/features/step_definitions/repository-steps.mjs b/test/integration/features/step_definitions/repository-steps.mjs new file mode 100644 index 0000000000..df42b390b8 --- /dev/null +++ b/test/integration/features/step_definitions/repository-steps.mjs @@ -0,0 +1,38 @@ +import { dump } from 'js-yaml' +import { StatusCodes } from 'http-status-codes' + +import { Given, Then } from '@cucumber/cucumber' +import { http, HttpResponse } from 'msw' +import any from '@travi/any' +import assert from 'node:assert' + +import { repository } from './common-steps.mjs' +import settings from '../../../../lib/settings.js' + +Given('basic repository config is defined', async function () { + this.repository = { + name: repository.name, + description: any.sentence(), + default_branch: 'main', + visibility: any.fromList(['public', 'private', 'internal']), + homepage: any.url() + } + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => HttpResponse.arrayBuffer(Buffer.from(dump({ repository: this.repository }))) + ), + http.patch(`https://api.github.com/repos/${repository.owner.name}/${repository.name}`, async ({ request }) => { + this.repositoryDetails = await request.json() + + return new HttpResponse(null, { status: StatusCodes.OK }) + }) + ) +}) + +Then('the repository will be configured', async function () { + assert.deepEqual(this.repositoryDetails, this.repository) +}) From 7006e29fe0ff91e342f4f8c95f5c93a11cd5b416 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 16 Feb 2024 18:17:48 -0600 Subject: [PATCH 32/43] test(integration): pinned behavior of updating repository topics --- test/integration/features/repository.feature | 4 +-- .../step_definitions/repository-steps.mjs | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/test/integration/features/repository.feature b/test/integration/features/repository.feature index c6605b156d..663feecfe6 100644 --- a/test/integration/features/repository.feature +++ b/test/integration/features/repository.feature @@ -1,14 +1,14 @@ Feature: Repository - @focus Scenario: Basic repository settings Given basic repository config is defined When a settings sync is triggered Then the repository will be configured - @wip Scenario: Repository with topics defined + Given topics are defined in the repository config When a settings sync is triggered + Then topics are updated @wip Scenario: Repository with vulnerability alerts enabled diff --git a/test/integration/features/step_definitions/repository-steps.mjs b/test/integration/features/step_definitions/repository-steps.mjs index df42b390b8..e1574f1c03 100644 --- a/test/integration/features/step_definitions/repository-steps.mjs +++ b/test/integration/features/step_definitions/repository-steps.mjs @@ -33,6 +33,34 @@ Given('basic repository config is defined', async function () { ) }) +Given('topics are defined in the repository config', async function () { + this.repository = { + name: repository.name, + topics: any.listOf(any.word).join(', ') + } + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => HttpResponse.arrayBuffer(Buffer.from(dump({ repository: this.repository }))) + ), + http.patch(`https://api.github.com/repos/${repository.owner.name}/${repository.name}`, async ({ request }) => { + return new HttpResponse(null, { status: StatusCodes.OK }) + }), + http.put(`https://api.github.com/repos/${repository.owner.name}/${repository.name}/topics`, async ({ request }) => { + this.updatedTopics = (await request.json()).names + + return new HttpResponse(null, { status: StatusCodes.OK }) + }) + ) +}) + Then('the repository will be configured', async function () { assert.deepEqual(this.repositoryDetails, this.repository) }) + +Then('topics are updated', async function () { + assert.deepEqual(this.updatedTopics, this.repository.topics.split(', ')) +}) From b6c971c0201cd7c865afe420ed451e6d430d8bf5 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 16 Feb 2024 23:23:54 -0600 Subject: [PATCH 33/43] test(integration): pinned the behavior of enabling/disabling vulnerability alerts --- test/integration/features/repository.feature | 12 ++++++- .../step_definitions/repository-steps.mjs | 31 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/test/integration/features/repository.feature b/test/integration/features/repository.feature index 663feecfe6..530041ae38 100644 --- a/test/integration/features/repository.feature +++ b/test/integration/features/repository.feature @@ -10,10 +10,20 @@ Feature: Repository When a settings sync is triggered Then topics are updated - @wip Scenario: Repository with vulnerability alerts enabled + Given vulnerability alerts are "enabled" in the config + When a settings sync is triggered + Then vulnerability alerts are "enabled" + + Scenario: Repository with vulnerability alerts disabled + Given vulnerability alerts are "disabled" in the config When a settings sync is triggered + Then vulnerability alerts are "disabled" @wip Scenario: Repository with security fixes enabled When a settings sync is triggered + + @wip + Scenario: Repository with security fixes disabled + When a settings sync is triggered diff --git a/test/integration/features/step_definitions/repository-steps.mjs b/test/integration/features/step_definitions/repository-steps.mjs index e1574f1c03..a9eec3bc83 100644 --- a/test/integration/features/step_definitions/repository-steps.mjs +++ b/test/integration/features/step_definitions/repository-steps.mjs @@ -57,6 +57,33 @@ Given('topics are defined in the repository config', async function () { ) }) +Given('vulnerability alerts are {string} in the config', async function (enablement) { + this.repository = { + name: repository.name, + enable_vulnerability_alerts: enablement === 'enabled' + } + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => HttpResponse.arrayBuffer(Buffer.from(dump({ repository: this.repository }))) + ), + http.patch(`https://api.github.com/repos/${repository.owner.name}/${repository.name}`, async ({ request }) => { + return new HttpResponse(null, { status: StatusCodes.OK }) + }), + http[this.repository.enable_vulnerability_alerts ? 'put' : 'delete']( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/vulnerability-alerts`, + async ({ request }) => { + this.vulnerabilityAlertEnablement = enablement + + return new HttpResponse(null, { status: StatusCodes.OK }) + } + ) + ) +}) + Then('the repository will be configured', async function () { assert.deepEqual(this.repositoryDetails, this.repository) }) @@ -64,3 +91,7 @@ Then('the repository will be configured', async function () { Then('topics are updated', async function () { assert.deepEqual(this.updatedTopics, this.repository.topics.split(', ')) }) + +Then('vulnerability alerts are {string}', async function (enablement) { + assert.equal(this.vulnerabilityAlertEnablement, enablement) +}) From a829cc9a5730c4220d144b90bc0a8a1234b5aa3d Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 16 Feb 2024 23:39:20 -0600 Subject: [PATCH 34/43] test(integration): pinned the behaviors of enabling/disabling security fixes --- .../basic-config-with-security-fixes.yml | 5 - .../repository/basic-config-with-topics.yml | 5 - ...basic-config-with-vulnerability-alerts.yml | 5 - test/fixtures/repository/basic-config.yml | 4 - test/integration/features/repository.feature | 6 +- .../step_definitions/repository-steps.mjs | 37 ++++++- test/integration/plugins/repository.test.js | 102 ------------------ 7 files changed, 40 insertions(+), 124 deletions(-) delete mode 100644 test/fixtures/repository/basic-config-with-security-fixes.yml delete mode 100644 test/fixtures/repository/basic-config-with-topics.yml delete mode 100644 test/fixtures/repository/basic-config-with-vulnerability-alerts.yml delete mode 100644 test/fixtures/repository/basic-config.yml delete mode 100644 test/integration/plugins/repository.test.js diff --git a/test/fixtures/repository/basic-config-with-security-fixes.yml b/test/fixtures/repository/basic-config-with-security-fixes.yml deleted file mode 100644 index d4f828002f..0000000000 --- a/test/fixtures/repository/basic-config-with-security-fixes.yml +++ /dev/null @@ -1,5 +0,0 @@ -repository: - name: bar - delete_branch_on_merge: true - is_template: true - enable_automated_security_fixes: true diff --git a/test/fixtures/repository/basic-config-with-topics.yml b/test/fixtures/repository/basic-config-with-topics.yml deleted file mode 100644 index 0b01ed17d4..0000000000 --- a/test/fixtures/repository/basic-config-with-topics.yml +++ /dev/null @@ -1,5 +0,0 @@ -repository: - name: bar - topics: github, probot - delete_branch_on_merge: true - is_template: true diff --git a/test/fixtures/repository/basic-config-with-vulnerability-alerts.yml b/test/fixtures/repository/basic-config-with-vulnerability-alerts.yml deleted file mode 100644 index a698bf4a60..0000000000 --- a/test/fixtures/repository/basic-config-with-vulnerability-alerts.yml +++ /dev/null @@ -1,5 +0,0 @@ -repository: - name: bar - delete_branch_on_merge: true - is_template: true - enable_vulnerability_alerts: true diff --git a/test/fixtures/repository/basic-config.yml b/test/fixtures/repository/basic-config.yml deleted file mode 100644 index 43c3626cc2..0000000000 --- a/test/fixtures/repository/basic-config.yml +++ /dev/null @@ -1,4 +0,0 @@ -repository: - name: bar - delete_branch_on_merge: true - is_template: true diff --git a/test/integration/features/repository.feature b/test/integration/features/repository.feature index 530041ae38..4a1df89b95 100644 --- a/test/integration/features/repository.feature +++ b/test/integration/features/repository.feature @@ -20,10 +20,12 @@ Feature: Repository When a settings sync is triggered Then vulnerability alerts are "disabled" - @wip Scenario: Repository with security fixes enabled + Given security fixes are "enabled" in the config When a settings sync is triggered + Then security fixes are "enabled" - @wip Scenario: Repository with security fixes disabled + Given security fixes are "disabled" in the config When a settings sync is triggered + Then security fixes are "disabled" diff --git a/test/integration/features/step_definitions/repository-steps.mjs b/test/integration/features/step_definitions/repository-steps.mjs index a9eec3bc83..f8554dd854 100644 --- a/test/integration/features/step_definitions/repository-steps.mjs +++ b/test/integration/features/step_definitions/repository-steps.mjs @@ -78,7 +78,38 @@ Given('vulnerability alerts are {string} in the config', async function (enablem async ({ request }) => { this.vulnerabilityAlertEnablement = enablement - return new HttpResponse(null, { status: StatusCodes.OK }) + return new HttpResponse(null, { + status: this.repository.enable_vulnerability_alerts ? StatusCodes.OK : StatusCodes.NO_CONTENT + }) + } + ) + ) +}) + +Given('security fixes are {string} in the config', async function (enablement) { + this.repository = { + name: repository.name, + enable_automated_security_fixes: enablement === 'enabled' + } + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => HttpResponse.arrayBuffer(Buffer.from(dump({ repository: this.repository }))) + ), + http.patch(`https://api.github.com/repos/${repository.owner.name}/${repository.name}`, async ({ request }) => { + return new HttpResponse(null, { status: StatusCodes.OK }) + }), + http[this.repository.enable_automated_security_fixes ? 'put' : 'delete']( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/automated-security-fixes`, + async ({ request }) => { + this.securityFixesEnablement = enablement + + return new HttpResponse(null, { + status: this.repository.enable_automated_security_fixes ? StatusCodes.OK : StatusCodes.NO_CONTENT + }) } ) ) @@ -95,3 +126,7 @@ Then('topics are updated', async function () { Then('vulnerability alerts are {string}', async function (enablement) { assert.equal(this.vulnerabilityAlertEnablement, enablement) }) + +Then('security fixes are {string}', async function (enablement) { + assert.equal(this.securityFixesEnablement, enablement) +}) diff --git a/test/integration/plugins/repository.test.js b/test/integration/plugins/repository.test.js deleted file mode 100644 index 757cfc8b41..0000000000 --- a/test/integration/plugins/repository.test.js +++ /dev/null @@ -1,102 +0,0 @@ -const { OK } = require('http-status-codes') -const { - buildTriggerEvent, - initializeNock, - loadInstance, - repository, - teardownNock, - defineSettingsFileForScenario -} = require('../common') - -describe('repository plugin', function () { - let probot, githubScope - - beforeEach(async () => { - githubScope = initializeNock() - probot = await loadInstance() - }) - - afterEach(() => { - teardownNock(githubScope) - }) - - it('syncs repo with basic settings', async () => { - const config = await defineSettingsFileForScenario('repository/basic-config.yml', githubScope) - const repoSettings = Object.assign({}, config.repository) - - githubScope - .patch(`/repos/${repository.owner.name}/${repository.name}`, body => { - expect(body).toMatchObject(repoSettings) - return true - }) - .matchHeader('accept', ['application/vnd.github.baptiste-preview+json']) - .reply(OK) - - await probot.receive(buildTriggerEvent()) - }) - - it('replaces topics, when provided', async () => { - const config = await defineSettingsFileForScenario('repository/basic-config-with-topics.yml', githubScope) - const repoSettings = Object.assign({}, config.repository) - - githubScope - .patch(`/repos/${repository.owner.name}/${repository.name}`, body => { - expect(body).toMatchObject(repoSettings) - return true - }) - .matchHeader('accept', ['application/vnd.github.baptiste-preview+json']) - .reply(OK) - githubScope - .put(`/repos/${repository.owner.name}/${repository.name}/topics`, body => { - expect(body).toMatchObject({ names: ['github', 'probot'] }) - return true - }) - .matchHeader('accept', ['application/vnd.github.mercy-preview+json']) - .reply(OK) - - await probot.receive(buildTriggerEvent()) - }) - - it('syncs repo with basic settings and vulnerability alerts enabled', async () => { - const config = await defineSettingsFileForScenario( - 'repository/basic-config-with-vulnerability-alerts.yml', - githubScope - ) - const repoSettings = Object.assign({}, config.repository) - delete repoSettings.enable_vulnerability_alerts - - githubScope - .patch(`/repos/${repository.owner.name}/${repository.name}`, body => { - expect(body).toMatchObject(repoSettings) - return true - }) - .matchHeader('accept', ['application/vnd.github.baptiste-preview+json']) - .reply(OK) - githubScope - .put(`/repos/${repository.owner.name}/${repository.name}/vulnerability-alerts`, body => true) - .matchHeader('accept', ['application/vnd.github.dorian-preview+json']) - .reply(OK) - - await probot.receive(buildTriggerEvent()) - }) - - it('syncs repo with basic settings and security fixes enabled', async () => { - const config = await defineSettingsFileForScenario('repository/basic-config-with-security-fixes.yml', githubScope) - const repoSettings = Object.assign({}, config.repository) - delete repoSettings.enable_automated_security_fixes - - githubScope - .patch(`/repos/${repository.owner.name}/${repository.name}`, body => { - expect(body).toMatchObject(repoSettings) - return true - }) - .matchHeader('accept', ['application/vnd.github.baptiste-preview+json']) - .reply(OK) - githubScope - .put(`/repos/${repository.owner.name}/${repository.name}/automated-security-fixes`, body => true) - .matchHeader('accept', ['application/vnd.github.london-preview+json']) - .reply(OK) - - await probot.receive(buildTriggerEvent()) - }) -}) From d5898ee71fbf1d70f8037b028a7c0d4f43269005 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 16 Feb 2024 23:58:28 -0600 Subject: [PATCH 35/43] test(integration): pinned the behavior of adding a reviewer to an environment --- .../integration/features/environments.feature | 4 ++- .../step_definitions/environments-steps.mjs | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature index eac4e943b6..bf75b2c083 100644 --- a/test/integration/features/environments.feature +++ b/test/integration/features/environments.feature @@ -36,9 +36,11 @@ Feature: Environments When a settings sync is triggered Then the reviewer id is updated - @wip Scenario: Add a reviewer to an environment + Given an environment exists + And a reviewer is added to the environment When a settings sync is triggered + Then the reviewer is defined for the environment @wip Scenario: Remove a reviewer from an environment diff --git a/test/integration/features/step_definitions/environments-steps.mjs b/test/integration/features/step_definitions/environments-steps.mjs index b015950c23..f4c69c62d6 100644 --- a/test/integration/features/step_definitions/environments-steps.mjs +++ b/test/integration/features/step_definitions/environments-steps.mjs @@ -208,6 +208,35 @@ Given('a reviewer has its id changed', async function () { ) }) +Given('a reviewer is added to the environment', async function () { + this.addedReviewer = anyReviewer() + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from( + dump({ + environments: [{ ...this.environment, reviewers: [this.addedReviewer] }] + }) + ) + ) + } + ), + http.put( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environment.name}`, + async ({ request }) => { + this.updatedEnvironment = await request.json() + + return new HttpResponse(null, { status: StatusCodes.OK }) + } + ) + ) +}) + Then('the environment is available', async function () { assert.deepEqual(this.createdEnvironment, { deployment_branch_policy: null }) }) @@ -239,3 +268,7 @@ Then('the reviewer id is updated', async function () { this.updatedReviewer ) }) + +Then('the reviewer is defined for the environment', async function () { + assert.deepEqual(this.updatedEnvironment.reviewers, [this.addedReviewer]) +}) From 405083ee8e95eb5de458775c07c5e4118b7bcbdf Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Mon, 19 Feb 2024 11:50:47 -0600 Subject: [PATCH 36/43] test(integration): pinned behavior of removing an environment reviewer Co-authored-by: Julie Van Kirk Co-authored-by: May Liang --- .../integration/features/environments.feature | 3 +- .../step_definitions/environments-steps.mjs | 38 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature index bf75b2c083..92318f34cf 100644 --- a/test/integration/features/environments.feature +++ b/test/integration/features/environments.feature @@ -42,10 +42,11 @@ Feature: Environments When a settings sync is triggered Then the reviewer is defined for the environment - @wip Scenario: Remove a reviewer from an environment Given an environment exists with reviewers defined + And a reviewer is removed from the environment in the config When a settings sync is triggered + Then the reviewer is removed from the environment @wip Scenario: Define an Environment with a Deployment Branch Policy diff --git a/test/integration/features/step_definitions/environments-steps.mjs b/test/integration/features/step_definitions/environments-steps.mjs index f4c69c62d6..6a8265c6a4 100644 --- a/test/integration/features/step_definitions/environments-steps.mjs +++ b/test/integration/features/step_definitions/environments-steps.mjs @@ -237,6 +237,36 @@ Given('a reviewer is added to the environment', async function () { ) }) +Given('a reviewer is removed from the environment in the config', async function () { + const [removedReviewer, ...remainingReviewers] = this.environment.reviewers + this.removedReviewer = removedReviewer + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from( + dump({ + environments: [{ ...this.environment, reviewers: remainingReviewers }] + }) + ) + ) + } + ), + http.put( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environment.name}`, + async ({ request }) => { + this.updatedEnvironment = await request.json() + + return new HttpResponse(null, { status: StatusCodes.OK }) + } + ) + ) +}) + Then('the environment is available', async function () { assert.deepEqual(this.createdEnvironment, { deployment_branch_policy: null }) }) @@ -272,3 +302,11 @@ Then('the reviewer id is updated', async function () { Then('the reviewer is defined for the environment', async function () { assert.deepEqual(this.updatedEnvironment.reviewers, [this.addedReviewer]) }) + +Then('the reviewer is removed from the environment', async function () { + assert.equal(this.updatedEnvironment.reviewers.length, this.environment.reviewers.length - 1) + assert.equal( + this.updatedEnvironment.reviewers.find(reviewer => reviewer.id === this.removedReviewer.id), + undefined + ) +}) From fb8f322ac2ad563b6d0331fb7296da9b85357e5d Mon Sep 17 00:00:00 2001 From: Julie Van Kirk Date: Mon, 19 Feb 2024 14:15:27 -0600 Subject: [PATCH 37/43] test(integration): pinned creating env w/ branch policies Co-authored-by: May Liang Co-authored-by: Matthew Travi --- .../integration/features/environments.feature | 12 ++- .../step_definitions/environments-steps.mjs | 92 +++++++++++++++++++ 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature index 92318f34cf..f75fd050b2 100644 --- a/test/integration/features/environments.feature +++ b/test/integration/features/environments.feature @@ -48,9 +48,17 @@ Feature: Environments When a settings sync is triggered Then the reviewer is removed from the environment - @wip - Scenario: Define an Environment with a Deployment Branch Policy + Scenario: Define an Environment with a protected branches Deployment Branch Policy + Given no environments are defined + And an environment is defined in the config with a protected branches deployment branch policy + When a settings sync is triggered + Then the environment is available with a protected branches deployment branch policy + + Scenario: Define an Environment with a custom branches Deployment Branch Policy + Given no environments are defined + And an environment is defined in the config with a custom branches deployment branch policy When a settings sync is triggered + Then the environment is available with a custom branches deployment branch policy @wip Scenario: Define a Deployment Branch Policy for an exiting environment diff --git a/test/integration/features/step_definitions/environments-steps.mjs b/test/integration/features/step_definitions/environments-steps.mjs index 6a8265c6a4..13684ac20c 100644 --- a/test/integration/features/step_definitions/environments-steps.mjs +++ b/test/integration/features/step_definitions/environments-steps.mjs @@ -267,6 +267,85 @@ Given('a reviewer is removed from the environment in the config', async function ) }) +Given('an environment is defined in the config with a protected branches deployment branch policy', async function () { + this.environmentName = any.word() + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from( + dump({ + environments: [ + { + name: this.environmentName, + deployment_branch_policy: { protected_branches: true } + } + ] + }) + ) + ) + } + ), + http.put( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environmentName}`, + async ({ request }) => { + this.createdEnvironment = await request.json() + + return new HttpResponse(null, { status: StatusCodes.CREATED }) + } + ) + ) +}) + +Given('an environment is defined in the config with a custom branches deployment branch policy', async function () { + this.environmentName = any.word() + this.customBranchNames = any.listOf(any.word) + this.createdDeploymentBranchPolicyNames = {} + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from( + dump({ + environments: [ + { + name: this.environmentName, + deployment_branch_policy: { protected_branches: false, custom_branches: this.customBranchNames } + } + ] + }) + ) + ) + } + ), + http.put( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environmentName}`, + async ({ request }) => { + this.createdEnvironment = await request.json() + + return new HttpResponse(null, { status: StatusCodes.CREATED }) + } + ), + http.post( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environmentName}/deployment-branch-policies`, + async ({ request }) => { + const policyName = (await request.json()).name + this.createdDeploymentBranchPolicyNames[policyName] = true + + return new HttpResponse(null, { status: StatusCodes.OK }) + } + ) + ) +}) + Then('the environment is available', async function () { assert.deepEqual(this.createdEnvironment, { deployment_branch_policy: null }) }) @@ -310,3 +389,16 @@ Then('the reviewer is removed from the environment', async function () { undefined ) }) + +Then('the environment is available with a protected branches deployment branch policy', async function () { + assert.deepEqual(this.createdEnvironment, { + deployment_branch_policy: { protected_branches: true, custom_branch_policies: false } + }) +}) + +Then('the environment is available with a custom branches deployment branch policy', async function () { + assert.deepEqual(this.createdEnvironment, { + deployment_branch_policy: { protected_branches: false, custom_branch_policies: true } + }) + assert.deepEqual(this.customBranchNames, Object.keys(this.createdDeploymentBranchPolicyNames)) +}) From 352f16392ebc0b7c76251c8566cf0dffc078993d Mon Sep 17 00:00:00 2001 From: Julie Van Kirk Date: Mon, 19 Feb 2024 15:20:12 -0600 Subject: [PATCH 38/43] test(integration): pinned add branch policy to existing env Co-authored-by: May Liang Co-authored-by: Matthew Travi --- .../integration/features/environments.feature | 16 ++- .../step_definitions/environments-steps.mjs | 103 +++++++++++++++++- 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature index f75fd050b2..3d310e1ffa 100644 --- a/test/integration/features/environments.feature +++ b/test/integration/features/environments.feature @@ -60,12 +60,26 @@ Feature: Environments When a settings sync is triggered Then the environment is available with a custom branches deployment branch policy + Scenario: Define a protected deployment Branch Policy for an exiting environment + Given an environment exists + And a protected deployment branch policy is defined for the environment + When a settings sync is triggered + Then the protected branches deployment branch policy is available for the environment + + Scenario: Define a custom branches Deployment Branch Policy for an exiting environment + Given an environment exists + And a custom deployment branch policy is defined for the environment + When a settings sync is triggered + Then the custom branches deployment branch policy is available for the environment + @wip - Scenario: Define a Deployment Branch Policy for an exiting environment + Scenario: Update the Deployment Branch Policy for an environment + Given an environment exists with a protected branches deployment branch policy When a settings sync is triggered @wip Scenario: Update the Deployment Branch Policy for an environment + Given an environment exists with a custom branches deployment branch policy When a settings sync is triggered @wip diff --git a/test/integration/features/step_definitions/environments-steps.mjs b/test/integration/features/step_definitions/environments-steps.mjs index 13684ac20c..9c6f2d3746 100644 --- a/test/integration/features/step_definitions/environments-steps.mjs +++ b/test/integration/features/step_definitions/environments-steps.mjs @@ -293,7 +293,7 @@ Given('an environment is defined in the config with a protected branches deploym http.put( `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environmentName}`, async ({ request }) => { - this.createdEnvironment = await request.json() + this.savedEnvironment = await request.json() return new HttpResponse(null, { status: StatusCodes.CREATED }) } @@ -329,7 +329,7 @@ Given('an environment is defined in the config with a custom branches deployment http.put( `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environmentName}`, async ({ request }) => { - this.createdEnvironment = await request.json() + this.savedEnvironment = await request.json() return new HttpResponse(null, { status: StatusCodes.CREATED }) } @@ -346,6 +346,82 @@ Given('an environment is defined in the config with a custom branches deployment ) }) +Given('a protected deployment branch policy is defined for the environment', async function () { + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from( + dump({ + environments: [ + { + ...this.environment, + deployment_branch_policy: { protected_branches: true } + } + ] + }) + ) + ) + } + ), + http.put( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environment.name}`, + async ({ request }) => { + this.savedEnvironment = await request.json() + + return new HttpResponse(null, { status: StatusCodes.CREATED }) + } + ) + ) +}) + +Given('a custom deployment branch policy is defined for the environment', async function () { + this.customBranchNames = any.listOf(any.word) + this.createdDeploymentBranchPolicyNames = {} + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from( + dump({ + environments: [ + { + ...this.environment, + deployment_branch_policy: { protected_branches: false, custom_branches: this.customBranchNames } + } + ] + }) + ) + ) + } + ), + http.put( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environment.name}`, + async ({ request }) => { + this.savedEnvironment = await request.json() + + return new HttpResponse(null, { status: StatusCodes.CREATED }) + } + ), + http.post( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environment.name}/deployment-branch-policies`, + async ({ request }) => { + const policyName = (await request.json()).name + this.createdDeploymentBranchPolicyNames[policyName] = true + + return new HttpResponse(null, { status: StatusCodes.OK }) + } + ) + ) +}) + Then('the environment is available', async function () { assert.deepEqual(this.createdEnvironment, { deployment_branch_policy: null }) }) @@ -391,14 +467,33 @@ Then('the reviewer is removed from the environment', async function () { }) Then('the environment is available with a protected branches deployment branch policy', async function () { - assert.deepEqual(this.createdEnvironment, { + assert.deepEqual(this.savedEnvironment, { + deployment_branch_policy: { protected_branches: true, custom_branch_policies: false } + }) +}) + +Then('the protected branches deployment branch policy is available for the environment', async function () { + const { name, deployment_branch_policy: policy, ...existingEnvironment } = this.environment + + assert.deepEqual(this.savedEnvironment, { + ...existingEnvironment, deployment_branch_policy: { protected_branches: true, custom_branch_policies: false } }) }) Then('the environment is available with a custom branches deployment branch policy', async function () { - assert.deepEqual(this.createdEnvironment, { + assert.deepEqual(this.savedEnvironment, { deployment_branch_policy: { protected_branches: false, custom_branch_policies: true } }) assert.deepEqual(this.customBranchNames, Object.keys(this.createdDeploymentBranchPolicyNames)) }) + +Then('the custom branches deployment branch policy is available for the environment', async function () { + const { name, deployment_branch_policy: policy, ...existingEnvironment } = this.environment + + assert.deepEqual(this.savedEnvironment, { + ...existingEnvironment, + deployment_branch_policy: { protected_branches: false, custom_branch_policies: true } + }) + assert.deepEqual(this.customBranchNames.sort(), Object.keys(this.createdDeploymentBranchPolicyNames)) +}) From a3d1ffc01e583264793649de444e16d7c88ed2e0 Mon Sep 17 00:00:00 2001 From: Julie Van Kirk Date: Mon, 19 Feb 2024 16:04:13 -0600 Subject: [PATCH 39/43] test(integration): pinned switching deployment branch policy type Co-authored-by: May Liang Co-authored-by: Matthew Travi --- .../integration/features/environments.feature | 19 +++++--- .../step_definitions/environments-steps.mjs | 46 +++++++++++++++++++ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature index 3d310e1ffa..c9f3d775bf 100644 --- a/test/integration/features/environments.feature +++ b/test/integration/features/environments.feature @@ -72,15 +72,18 @@ Feature: Environments When a settings sync is triggered Then the custom branches deployment branch policy is available for the environment - @wip - Scenario: Update the Deployment Branch Policy for an environment - Given an environment exists with a protected branches deployment branch policy + Scenario: Update the protected branches Deployment Branch Policy for an environment + Given an environment exists with a "protected" branches deployment branch policy + And a custom deployment branch policy is defined for the environment When a settings sync is triggered + Then the custom branches deployment branch policy is available for the environment - @wip - Scenario: Update the Deployment Branch Policy for an environment - Given an environment exists with a custom branches deployment branch policy + Scenario: Update the custom branches Deployment Branch Policy for an environment + Given an environment exists with a "custom" branches deployment branch policy + And a protected deployment branch policy is defined for the environment When a settings sync is triggered + Then custom deployment branch policies are removed + And the protected branches deployment branch policy is available for the environment @wip Scenario: Reviewers are unchanged, but are sorted differently than the api @@ -89,3 +92,7 @@ Feature: Environments @wip Scenario: Unchanged wait-timer considered equivalent to default When a settings sync is triggered + + @wip + Scenario: Unchanged deployment branch policy + When a settings sync is triggered diff --git a/test/integration/features/step_definitions/environments-steps.mjs b/test/integration/features/step_definitions/environments-steps.mjs index 9c6f2d3746..ee93fc187a 100644 --- a/test/integration/features/step_definitions/environments-steps.mjs +++ b/test/integration/features/step_definitions/environments-steps.mjs @@ -48,6 +48,45 @@ Given('an environment exists with reviewers defined', async function () { ) }) +Given('an environment exists with a {string} branches deployment branch policy', async function (policyType) { + this.environment = { + name: any.word(), + wait_timer: any.integer(), + deployment_branch_policy: { + protected_branches: policyType === 'protected', + custom_branch_policies: policyType === 'custom' + } + } + + this.server.use( + http.get(`https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments`, () => { + return HttpResponse.json({ environments: [this.environment] }) + }) + ) + + if (policyType === 'custom') { + this.customBranches = any.listOf(() => ({ name: any.word(), id: any.integer() })) + this.removedDeploymentBranchPolicyIds = {} + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environment.name}/deployment-branch-policies`, + () => { + return HttpResponse.json({ branch_policies: this.customBranches }) + } + ), + http.delete( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments/${this.environment.name}/deployment-branch-policies/:id`, + ({ params }) => { + this.removedDeploymentBranchPolicyIds[params.id] = true + + return new HttpResponse(null, { status: StatusCodes.NO_CONTENT }) + } + ) + ) + } +}) + Given('an environment is defined in the config', async function () { this.environmentName = any.word() @@ -497,3 +536,10 @@ Then('the custom branches deployment branch policy is available for the environm }) assert.deepEqual(this.customBranchNames.sort(), Object.keys(this.createdDeploymentBranchPolicyNames)) }) + +Then('custom deployment branch policies are removed', async function () { + assert.deepEqual( + Object.keys(this.removedDeploymentBranchPolicyIds), + this.customBranches.map(branch => branch.id) + ) +}) From 117188eebc7e3052886147885341b78e71e1c0fc Mon Sep 17 00:00:00 2001 From: Julie Van Kirk Date: Tue, 20 Feb 2024 13:32:33 -0600 Subject: [PATCH 40/43] test(integration): pinned no sync when reviewers are already in sync Co-authored-by: May Liang Co-authored-by: Matthew Travi --- lib/plugins/environments.js | 8 +++---- .../integration/features/environments.feature | 6 ++++- .../step_definitions/environments-steps.mjs | 22 +++++++++++++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/lib/plugins/environments.js b/lib/plugins/environments.js index aecb7dac43..b57de88c7c 100644 --- a/lib/plugins/environments.js +++ b/lib/plugins/environments.js @@ -14,14 +14,14 @@ function attributeSorter (a, b) { return 0 } -function reviewersToString (attrs) { - if (attrs === null || attrs === undefined) { +function reviewersToString (reviewers) { + if (reviewers === null || reviewers === undefined) { return '' } else { - attrs.sort(attributeSorter) + reviewers.sort(attributeSorter) return JSON.stringify( - attrs.map(reviewer => { + reviewers.map(reviewer => { return { id: reviewer.id, type: reviewer.type diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature index c9f3d775bf..95303503bd 100644 --- a/test/integration/features/environments.feature +++ b/test/integration/features/environments.feature @@ -85,14 +85,18 @@ Feature: Environments Then custom deployment branch policies are removed And the protected branches deployment branch policy is available for the environment - @wip Scenario: Reviewers are unchanged, but are sorted differently than the api + Given an environment exists with reviewers defined + And an environment is defined in the config with the same reviewers but sorted differently When a settings sync is triggered + Then no update will happen @wip Scenario: Unchanged wait-timer considered equivalent to default When a settings sync is triggered + Then no update will happen @wip Scenario: Unchanged deployment branch policy When a settings sync is triggered + Then no update will happen diff --git a/test/integration/features/step_definitions/environments-steps.mjs b/test/integration/features/step_definitions/environments-steps.mjs index ee93fc187a..59a63d8a72 100644 --- a/test/integration/features/step_definitions/environments-steps.mjs +++ b/test/integration/features/step_definitions/environments-steps.mjs @@ -461,6 +461,23 @@ Given('a custom deployment branch policy is defined for the environment', async ) }) +Given('an environment is defined in the config with the same reviewers but sorted differently', async function () { + const ascendingIdSortedReviewers = this.environment.reviewers.sort((a, b) => a.id - b.id) + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from(dump({ environments: [{ ...this.environment, reviewers: ascendingIdSortedReviewers }] })) + ) + } + ) + ) +}) + Then('the environment is available', async function () { assert.deepEqual(this.createdEnvironment, { deployment_branch_policy: null }) }) @@ -543,3 +560,8 @@ Then('custom deployment branch policies are removed', async function () { this.customBranches.map(branch => branch.id) ) }) + +Then('no update will happen', async function () { + // absence of an error means no update calls were made + return undefined +}) From 1b02660442149de7961bb30dd7728338d89f02a6 Mon Sep 17 00:00:00 2001 From: Julie Van Kirk Date: Tue, 20 Feb 2024 14:49:39 -0600 Subject: [PATCH 41/43] test(integration): pinning deployment branch policy update avoidance Co-authored-by: May Liang Co-authored-by: Matthew Travi --- .../integration/features/environments.feature | 5 ++-- .../step_definitions/environments-steps.mjs | 30 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature index 95303503bd..6af6cb010c 100644 --- a/test/integration/features/environments.feature +++ b/test/integration/features/environments.feature @@ -95,8 +95,9 @@ Feature: Environments Scenario: Unchanged wait-timer considered equivalent to default When a settings sync is triggered Then no update will happen - - @wip + Scenario: Unchanged deployment branch policy + Given an environment exists with a "custom" branches deployment branch policy + And an environment is defined in the config with the same custom branches deployment branch policy but sorted differently When a settings sync is triggered Then no update will happen diff --git a/test/integration/features/step_definitions/environments-steps.mjs b/test/integration/features/step_definitions/environments-steps.mjs index 59a63d8a72..2f121121da 100644 --- a/test/integration/features/step_definitions/environments-steps.mjs +++ b/test/integration/features/step_definitions/environments-steps.mjs @@ -478,6 +478,36 @@ Given('an environment is defined in the config with the same reviewers but sorte ) }) +Given( + 'an environment is defined in the config with the same custom branches deployment branch policy but sorted differently', + async function () { + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from( + dump({ + environments: [ + { + ...this.environment, + deployment_branch_policy: { + protected_branches: false, + custom_branches: this.customBranches.map(branch => branch.name).reverse() + } + } + ] + }) + ) + ) + } + ) + ) + } +) + Then('the environment is available', async function () { assert.deepEqual(this.createdEnvironment, { deployment_branch_policy: null }) }) From 39dae4f06ba392f4f8ca05e66ac379cb6451e99a Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 20 Feb 2024 16:46:43 -0600 Subject: [PATCH 42/43] test(integration): pinned updated avoidance for undefined wait_timer for an environment --- .../integration/features/environments.feature | 5 +-- .../step_definitions/environments-steps.mjs | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/test/integration/features/environments.feature b/test/integration/features/environments.feature index 6af6cb010c..11eb110260 100644 --- a/test/integration/features/environments.feature +++ b/test/integration/features/environments.feature @@ -91,11 +91,12 @@ Feature: Environments When a settings sync is triggered Then no update will happen - @wip Scenario: Unchanged wait-timer considered equivalent to default + Given an environment exists without wait-timer defined + And wait-timer is not defined for the environment in the config When a settings sync is triggered Then no update will happen - + Scenario: Unchanged deployment branch policy Given an environment exists with a "custom" branches deployment branch policy And an environment is defined in the config with the same custom branches deployment branch policy but sorted differently diff --git a/test/integration/features/step_definitions/environments-steps.mjs b/test/integration/features/step_definitions/environments-steps.mjs index 2f121121da..f09d820a26 100644 --- a/test/integration/features/step_definitions/environments-steps.mjs +++ b/test/integration/features/step_definitions/environments-steps.mjs @@ -33,6 +33,16 @@ Given('an environment exists', async function () { ) }) +Given('an environment exists without wait-timer defined', async function () { + this.environment = { name: any.word(), deployment_branch_policy: null } + + this.server.use( + http.get(`https://api.github.com/repos/${repository.owner.name}/${repository.name}/environments`, () => { + return HttpResponse.json({ environments: [this.environment] }) + }) + ) +}) + Given('an environment exists with reviewers defined', async function () { this.environment = { name: any.word(), @@ -136,6 +146,27 @@ Given('an environment is defined in the config with reviewers', async function ( ) }) +Given('wait-timer is not defined for the environment in the config', async function () { + const { wait_timer: waitTimer, ...environmentWithoutWaitTimer } = this.environment + + this.server.use( + http.get( + `https://api.github.com/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent( + settings.FILE_NAME + )}`, + ({ request }) => { + return HttpResponse.arrayBuffer( + Buffer.from( + dump({ + environments: [environmentWithoutWaitTimer] + }) + ) + ) + } + ) + ) +}) + Given('the environment is modified in the config', async function () { this.environmentUpdates = { wait_timer: any.integer(), deployment_branch_policy: null } From d7bc85312d878ee579ccdd04a7c6601789b4a73a Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 20 Feb 2024 17:08:33 -0600 Subject: [PATCH 43/43] test(integration): cleaned up remaining jest details after finishing transition to cucumber --- package.json | 4 +- test/fixtures/environments-config.yml | 81 ------- test/integration/common.js | 101 --------- test/integration/plugins/environments.test.js | 210 ------------------ 4 files changed, 1 insertion(+), 395 deletions(-) delete mode 100644 test/fixtures/environments-config.yml delete mode 100644 test/integration/common.js delete mode 100644 test/integration/plugins/environments.test.js diff --git a/package.json b/package.json index 0f8c02e994..a4c15e4ccb 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,7 @@ "lint:peer": "npm ls >/dev/null", "test:unit": "jest 'test/unit/'", "test:unit:watch": "npm run test:unit -- --watch", - "test:integration": "npm-run-all --print-label --parallel test:integration:jest test:integration:cucumber", - "test:integration:jest": "jest 'test/integration/'", - "test:integration:cucumber": "run-s 'test:integration:base -- --profile noWip'", + "test:integration": "run-s 'test:integration:base -- --profile noWip'", "test:integration:base": "NODE_OPTIONS=--enable-source-maps DEBUG=any cucumber-js test/integration", "test:integration:debug": "DEBUG=test run-s test:integration", "test:integration:wip": "run-s 'test:integration:base -- --profile wip'", diff --git a/test/fixtures/environments-config.yml b/test/fixtures/environments-config.yml deleted file mode 100644 index b9bd55058e..0000000000 --- a/test/fixtures/environments-config.yml +++ /dev/null @@ -1,81 +0,0 @@ -environments: - - name: changed-wait-timer - wait_timer: 10 - - - name: changed-reviewers-type - reviewers: - - id: 1 - type: User - - id: 2 - type: User - - - name: changed-reviewers-id - reviewers: - - id: 1 - type: Team - - id: 3 - type: User - - - name: changed-reviewers-remove - reviewers: - - id: 1 - type: Team - - - name: changed-reviewers-add - reviewers: - - id: 1 - type: Team - - id: 2 - type: User - - id: 3 - type: User - - - name: changed-deployment-branch-policy - deployment_branch_policy: - custom_branches: - - stage/* - - uat/* - - - name: changed-all - wait_timer: 10 - reviewers: - - id: 2 - type: User - deployment_branch_policy: - custom_branches: - - dev/* - - - name: new-environment - wait_timer: 1 - reviewers: - - id: 1 - type: Team - - id: 2 - type: User - deployment_branch_policy: - protected_branches: true - - - name: unchanged-default-wait-timer - - - name: unchanged-wait-timer - wait_timer: 1 - - - name: unchanged-reviewers-unsorted - reviewers: - - id: 2 - type: User - - id: 1 - type: Team - - - name: unchanged-reviewers-sorted - reviewers: - - id: 1 - type: Team - - id: 2 - type: User - - - name: unchanged-deployment-branch-policy - deployment_branch_policy: - custom_branches: - - dev/* - - dev-* diff --git a/test/integration/common.js b/test/integration/common.js deleted file mode 100644 index 00c40d8002..0000000000 --- a/test/integration/common.js +++ /dev/null @@ -1,101 +0,0 @@ -const path = require('node:path') -const { promises: fs } = require('node:fs') -const { OK } = require('http-status-codes') -const { Probot, ProbotOctokit } = require('probot') -const nock = require('nock') -const any = require('@travi/any') - -const settingsBot = require('../../index') -const settings = require('../../lib/settings') - -nock.disableNetConnect() - -const repository = { - default_branch: 'master', - name: 'botland', - owner: { - name: 'bkeepers-inc', - login: 'bkeepers-inc', - email: null - } -} - -async function loadInstance () { - const probot = new Probot({ - appId: 1, - privateKey: 'test', - githubToken: 'test', - Octokit: ProbotOctokit.defaults({ - retry: { enabled: false }, - throttle: { enabled: false } - }) - }) - await probot.load(settingsBot) - - return probot -} - -function initializeNock () { - return nock('https://api.github.com') -} - -function teardownNock (githubScope) { - expect(githubScope.pendingMocks()).toStrictEqual([]) - - nock.cleanAll() -} - -function buildPushEvent () { - return { - name: 'push', - payload: { - ref: 'refs/heads/master', - repository, - commits: [{ modified: [settings.FILE_NAME], added: [] }] - } - } -} - -function buildRepositoryEditedEvent () { - return { - name: 'repository.edited', - payload: { - changes: { default_branch: { from: any.word() } }, - repository - } - } -} - -function buildRepositoryCreatedEvent () { - return { - name: 'repository.created', - payload: { repository } - } -} - -function buildTriggerEvent () { - return any.fromList([buildPushEvent(), buildRepositoryCreatedEvent(), buildRepositoryEditedEvent()]) -} - -async function defineSettingsFileForScenario (settingsFileFixtureName, githubScope) { - const pathToConfig = path.resolve(__dirname, '..', 'fixtures', settingsFileFixtureName) - const configFile = Buffer.from(await fs.readFile(pathToConfig, 'utf8')) - const config = configFile.toString() - - githubScope - .get(`/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent(settings.FILE_NAME)}`) - .reply(OK, config) - - return config -} - -module.exports = { - loadInstance, - initializeNock, - teardownNock, - buildTriggerEvent, - buildRepositoryCreatedEvent, - buildRepositoryEditedEvent, - defineSettingsFileForScenario, - repository -} diff --git a/test/integration/plugins/environments.test.js b/test/integration/plugins/environments.test.js deleted file mode 100644 index 29ee7eb006..0000000000 --- a/test/integration/plugins/environments.test.js +++ /dev/null @@ -1,210 +0,0 @@ -const { CREATED, NO_CONTENT, OK } = require('http-status-codes') -const { - buildTriggerEvent, - initializeNock, - loadInstance, - repository, - teardownNock, - defineSettingsFileForScenario -} = require('../common') - -describe('environments plugin', function () { - let probot, githubScope - - beforeEach(async () => { - githubScope = initializeNock() - probot = await loadInstance() - }) - - afterEach(() => { - teardownNock(githubScope) - }) - - it('syncs environments', async () => { - await defineSettingsFileForScenario('environments-config.yml', githubScope) - githubScope.get(`/repos/${repository.owner.name}/${repository.name}/environments`).reply(OK, { - environments: [ - { name: 'changed-wait-timer', wait_timer: 1 }, - { - name: 'changed-reviewers-type', - reviewers: [ - { id: 1, type: 'Team' }, - { id: 2, type: 'User' } - ] - }, - { - name: 'changed-reviewers-id', - reviewers: [ - { id: 1, type: 'Team' }, - { id: 2, type: 'User' } - ] - }, - { - name: 'changed-reviewers-remove', - reviewers: [ - { id: 1, type: 'Team' }, - { id: 2, type: 'User' } - ] - }, - { - name: 'changed-reviewers-add', - reviewers: [ - { id: 1, type: 'Team' }, - { id: 2, type: 'User' } - ] - }, - { - name: 'changed-deployment-branch-policy', - deployment_branch_policy: { protected_branches: true, custom_branch_policies: false } - }, - { name: 'changed-all', wait_timer: 0 }, - { name: 'unchanged-default-wait-timer', wait_timer: 0 }, - { name: 'unchanged-wait-timer', wait_timer: 1 }, - { - name: 'unchanged-reviewers-unsorted', - reviewers: [ - { id: 1, type: 'Team' }, - { id: 2, type: 'User' } - ] - }, - { - name: 'unchanged-reviewers-sorted', - reviewers: [ - { id: 1, type: 'Team' }, - { id: 2, type: 'User' } - ] - }, - { - name: 'unchanged-deployment-branch-policy', - deployment_branch_policy: { protected_branches: false, custom_branch_policies: true } - }, - { name: 'deleted', wait_timer: 0 } - ] - }) - githubScope - .get( - `/repos/${repository.owner.name}/${repository.name}/environments/unchanged-deployment-branch-policy/deployment-branch-policies` - ) - .reply(OK, { - branch_policies: [ - { - id: 1, - node_id: '1', - name: 'dev/*' - }, - { - id: 2, - node_id: '2', - name: 'dev-*' - } - ] - }) - githubScope - .put(`/repos/${repository.owner.name}/${repository.name}/environments/changed-wait-timer`, body => { - expect(body).toMatchObject({ wait_timer: 10 }) - return true - }) - .reply(CREATED) - githubScope - .put(`/repos/${repository.owner.name}/${repository.name}/environments/changed-reviewers-type`, body => { - expect(body).toMatchObject({ - reviewers: [ - { id: 1, type: 'User' }, - { id: 2, type: 'User' } - ] - }) - return true - }) - .reply(CREATED) - githubScope - .put(`/repos/${repository.owner.name}/${repository.name}/environments/changed-reviewers-id`, body => { - expect(body).toMatchObject({ - reviewers: [ - { id: 1, type: 'Team' }, - { id: 3, type: 'User' } - ] - }) - return true - }) - .reply(CREATED) - githubScope - .put(`/repos/${repository.owner.name}/${repository.name}/environments/changed-reviewers-remove`, body => { - expect(body).toMatchObject({ reviewers: [{ id: 1, type: 'Team' }] }) - return true - }) - .reply(CREATED) - githubScope - .put(`/repos/${repository.owner.name}/${repository.name}/environments/changed-reviewers-add`, body => { - expect(body).toMatchObject({ - reviewers: [ - { id: 1, type: 'Team' }, - { id: 2, type: 'User' }, - { id: 3, type: 'User' } - ] - }) - return true - }) - .reply(CREATED) - githubScope - .put(`/repos/${repository.owner.name}/${repository.name}/environments/changed-deployment-branch-policy`, body => { - expect(body).toMatchObject({ - deployment_branch_policy: { protected_branches: false, custom_branch_policies: true } - }) - return true - }) - .reply(CREATED) - githubScope - .post( - `/repos/${repository.owner.name}/${repository.name}/environments/changed-deployment-branch-policy/deployment-branch-policies`, - body => { - expect(body).toMatchObject({ name: 'stage/*' }) - return true - } - ) - .reply(OK, { id: 3, node_id: '3', name: 'stage/*' }) - githubScope - .post( - `/repos/${repository.owner.name}/${repository.name}/environments/changed-deployment-branch-policy/deployment-branch-policies`, - body => { - expect(body).toMatchObject({ name: 'uat/*' }) - return true - } - ) - .reply(OK, { id: 4, node_id: '4', name: 'uat/*' }) - githubScope - .put(`/repos/${repository.owner.name}/${repository.name}/environments/changed-all`, body => { - expect(body).toMatchObject({ - wait_timer: 10, - reviewers: [{ id: 2, type: 'User' }], - deployment_branch_policy: { protected_branches: false, custom_branch_policies: true } - }) - return true - }) - .reply(CREATED) - githubScope - .post( - `/repos/${repository.owner.name}/${repository.name}/environments/changed-all/deployment-branch-policies`, - body => { - expect(body).toMatchObject({ name: 'dev/*' }) - return true - } - ) - .reply(OK, { id: 5, node_id: '5', name: 'dev/*' }) - githubScope - .put(`/repos/${repository.owner.name}/${repository.name}/environments/new-environment`, body => { - expect(body).toMatchObject({ - wait_timer: 1, - reviewers: [ - { id: 1, type: 'Team' }, - { id: 2, type: 'User' } - ], - deployment_branch_policy: { protected_branches: true, custom_branch_policies: false } - }) - return true - }) - .reply(CREATED) - githubScope.delete(`/repos/${repository.owner.name}/${repository.name}/environments/deleted`).reply(NO_CONTENT) - - await probot.receive(buildTriggerEvent()) - }) -})