diff --git a/.changeset/angry-dingos-lick.md b/.changeset/angry-dingos-lick.md new file mode 100644 index 0000000000..933f19a839 --- /dev/null +++ b/.changeset/angry-dingos-lick.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-catalog-graph': patch +'@backstage/plugin-pagerduty': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-splunk-on-call': patch +'@backstage/plugin-techdocs-react': patch +--- + +Internal refactor to improve tests diff --git a/.changeset/chilly-ads-lay.md b/.changeset/chilly-ads-lay.md new file mode 100644 index 0000000000..844eaa8dc7 --- /dev/null +++ b/.changeset/chilly-ads-lay.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-client': minor +--- + +**BREAKING PRODUCERS**: Added a new `getEntitiesByRefs` endpoint to `CatalogApi`, for efficient batch fetching of entities by ref. diff --git a/.changeset/clever-pillows-drive.md b/.changeset/clever-pillows-drive.md new file mode 100644 index 0000000000..f4e60fb869 --- /dev/null +++ b/.changeset/clever-pillows-drive.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Do not use deprecated `LocationSpec` from the `@backstage/plugin-catalog-node` package diff --git a/.changeset/fast-lies-remain.md b/.changeset/fast-lies-remain.md new file mode 100644 index 0000000000..74be117920 --- /dev/null +++ b/.changeset/fast-lies-remain.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-catalog-backend': minor +--- + +Added new `POST /entities/by-refs` endpoint, which allows you to efficiently +batch-fetch entities by their entity ref. This can be useful e.g. in graphql +resolvers or similar contexts where you need to fetch many entities at the same +time. diff --git a/.changeset/fluffy-walls-approve.md b/.changeset/fluffy-walls-approve.md new file mode 100644 index 0000000000..968f5cc402 --- /dev/null +++ b/.changeset/fluffy-walls-approve.md @@ -0,0 +1,21 @@ +--- +'@backstage/plugin-events-backend': minor +--- + +**BREAKING:** Remove required field `router` at `HttpPostIngressEventPublisher.fromConfig` +and replace it with `bind(router: Router)`. +Additionally, the path prefix `/http` will be added inside `HttpPostIngressEventPublisher`. + +```diff +// at packages/backend/src/plugins/events.ts + const eventsRouter = Router(); +- const httpRouter = Router(); +- eventsRouter.use('/http', httpRouter); + + const http = HttpPostIngressEventPublisher.fromConfig({ + config: env.config, + logger: env.logger, +- router: httpRouter, + }); ++ http.bind(eventsRouter); +``` diff --git a/.changeset/funny-numbers-compete.md b/.changeset/funny-numbers-compete.md new file mode 100644 index 0000000000..604ffe7294 --- /dev/null +++ b/.changeset/funny-numbers-compete.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Use Json types from @backstage/types diff --git a/.changeset/hip-stingrays-kneel.md b/.changeset/hip-stingrays-kneel.md new file mode 100644 index 0000000000..171e9257f2 --- /dev/null +++ b/.changeset/hip-stingrays-kneel.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Create a variable for minimum height and add a prop named 'fit' for determining if the graph height should grow or be contained. diff --git a/.changeset/new-bugs-march.md b/.changeset/new-bugs-march.md new file mode 100644 index 0000000000..346f5a151e --- /dev/null +++ b/.changeset/new-bugs-march.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-explore': patch +--- + +Adds styling to graph forcing it to always fill out the available space. diff --git a/.changeset/nine-ears-whisper.md b/.changeset/nine-ears-whisper.md new file mode 100644 index 0000000000..a1427158af --- /dev/null +++ b/.changeset/nine-ears-whisper.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-events-backend': patch +'@backstage/plugin-events-node': minor +--- + +Introduce a new interface `RequestDetails` to abstract `Request` +providing access to request body and headers. + +**BREAKING:** Replace `request: Request` with `request: RequestDetails` at `RequestValidator`. diff --git a/.changeset/old-keys-leave.md b/.changeset/old-keys-leave.md new file mode 100644 index 0000000000..d80574b2e4 --- /dev/null +++ b/.changeset/old-keys-leave.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +Added `lifecycleFactory` implementation. diff --git a/.changeset/renovate-778b2fa.md b/.changeset/renovate-778b2fa.md new file mode 100644 index 0000000000..f4f57e448b --- /dev/null +++ b/.changeset/renovate-778b2fa.md @@ -0,0 +1,7 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-codescene': patch +'@backstage/plugin-sonarqube': patch +--- + +Updated dependency `rc-progress` to `3.4.1`. diff --git a/.changeset/search-om-manniskan-ginge.md b/.changeset/search-om-manniskan-ginge.md new file mode 100644 index 0000000000..c36972f191 --- /dev/null +++ b/.changeset/search-om-manniskan-ginge.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-node': patch +--- + +Updated indexer and decorator base classes to take advantage of features introduced in Node.js v16; be sure you are running a [supported version of Node.js](https://backstage.io/docs/releases/v1.8.0#node-16-and-18). diff --git a/.changeset/silly-wolves-remember.md b/.changeset/silly-wolves-remember.md new file mode 100644 index 0000000000..f39812db88 --- /dev/null +++ b/.changeset/silly-wolves-remember.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Registered shutdown hook in experimental catalog plugin. diff --git a/.changeset/sour-plums-grow.md b/.changeset/sour-plums-grow.md new file mode 100644 index 0000000000..4070dc5e50 --- /dev/null +++ b/.changeset/sour-plums-grow.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +fixed `headerOptions` not passed to `TemplatePage` component diff --git a/.changeset/thin-donuts-join.md b/.changeset/thin-donuts-join.md new file mode 100644 index 0000000000..84705711d2 --- /dev/null +++ b/.changeset/thin-donuts-join.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Fixed Entity kind pluralisation in the `CatalogKindHeader` component. diff --git a/.changeset/twelve-meals-smell.md b/.changeset/twelve-meals-smell.md new file mode 100644 index 0000000000..a9d64823a3 --- /dev/null +++ b/.changeset/twelve-meals-smell.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-vault-backend': patch +--- + +Use `express-promise-router` to catch errors properly. +Add `403` error as a known one. It will now return a `NotAllowed` error. diff --git a/.changeset/twenty-dodos-wash.md b/.changeset/twenty-dodos-wash.md new file mode 100644 index 0000000000..d21088a953 --- /dev/null +++ b/.changeset/twenty-dodos-wash.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': patch +--- + +Added `lifecycleFactory` to default service factories. diff --git a/.changeset/young-turkeys-relax.md b/.changeset/young-turkeys-relax.md new file mode 100644 index 0000000000..c53cd984a6 --- /dev/null +++ b/.changeset/young-turkeys-relax.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-plugin-api': patch +--- + +Added initial support for registering shutdown hooks via `lifecycleServiceRef`. diff --git a/.eslintrc.js b/.eslintrc.js index 47a47daee6..6d07fab291 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -18,7 +18,7 @@ var path = require('path'); module.exports = { root: true, - plugins: ['notice'], + plugins: ['notice', 'testing-library'], rules: { 'notice/notice': [ 'error', @@ -45,5 +45,10 @@ module.exports = { "CallExpression[arguments.length=0] > MemberExpression[property.name='toUpperCase']", }, ], + 'testing-library/await-async-query': 'error', + 'testing-library/await-async-utils': 'error', + 'testing-library/no-await-sync-query': 'error', + 'testing-library/prefer-wait-for': 'error', + 'testing-library/no-dom-import': 'error', }, }; diff --git a/.github/vale/Vocab/Backstage/accept.txt b/.github/vale/Vocab/Backstage/accept.txt index 9af9ae6137..f2b3ac0b09 100644 --- a/.github/vale/Vocab/Backstage/accept.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -241,6 +241,7 @@ pageview parallelization parseable Patrik +pattison Peloton performant Performant diff --git a/.github/workflows/automate_merge_message.yml b/.github/workflows/automate_merge_message.yml new file mode 100644 index 0000000000..9d8deffd8a --- /dev/null +++ b/.github/workflows/automate_merge_message.yml @@ -0,0 +1,63 @@ +name: Automate merge message +on: + pull_request: + branches: ['master'] + types: ['closed'] + +permissions: + pull-requests: write + actions: none + checks: none + contents: none + deployments: none + issues: none + packages: none + pages: none + repository-projects: none + security-events: none + statuses: none + +jobs: + message: + # prevent running towards forks, and only run on merged PRs + if: github.repository == 'backstage/backstage' && github.event.pull_request.merged == true + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: '${{ github.event.pull_request.merge_commit_sha }}' + + - name: fetch base + run: git fetch --depth 1 origin ${{ github.event.pull_request.base.sha }} + + # We avoid using the in-source script since this workflow has elevated permissions that we don't want to expose + - name: Generate Message + id: generate-message + run: | + rm -f generate.js + wget -O generate.js https://raw.githubusercontent.com/backstage/backstage/master/scripts/generate-merge-message.js 1>&2 + node generate.js FETCH_HEAD > message.txt + + - name: Post Message + uses: actions/github-script@v6 + env: + ISSUE_NUMBER: ${{ github.event.pull_request.number }} + with: + script: | + const owner = "backstage"; + const repo = "backstage"; + const body = require('fs').readFileSync('message.txt', 'utf8').trim(); + const issue_number = Number(process.env.ISSUE_NUMBER); + + if (!body) { + console.log(`skipping comment for #${issue_number}`); + return; + } + + console.log(`creating comment for #${issue_number}`); + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body, + }); diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d826bc8bb4..eef5058690 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,7 +68,7 @@ jobs: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: verify yarn dependency duplicates - run: yarn dedupe --check + run: node scripts/verify-lockfile-duplicates.js - name: verify changesets run: node scripts/verify-changesets.js diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index 3a24befa84..4085f334f3 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -32,6 +32,9 @@ jobs: run: yarn install --immutable working-directory: microsite + - name: verify yarn dependency duplicates + run: node scripts/verify-lockfile-duplicates.js + - name: prettier run: yarn prettier:check working-directory: microsite diff --git a/docs/assets/getting-started/sidebar-submenu-example.png b/docs/assets/getting-started/sidebar-submenu-example.png new file mode 100644 index 0000000000..0b30f0ca6a Binary files /dev/null and b/docs/assets/getting-started/sidebar-submenu-example.png differ diff --git a/docs/features/search/getting-started.md b/docs/features/search/getting-started.md index 71b9c276d9..726d1557d0 100644 --- a/docs/features/search/getting-started.md +++ b/docs/features/search/getting-started.md @@ -68,7 +68,7 @@ export const searchPage = ( switch (result.type) { case 'software-catalog': return ( - { It's good practice for search results to highlight information that was used to return it in the first place! The code below highlights how you might specify a -custom result item component, using the `` component as +custom result item component, using the `` component as an example: ```tsx {7-13} @@ -265,7 +265,7 @@ an example: switch (result.type) { case 'software-catalog': return ( - ` with this: + + ```ts + + + + + + + + + + + + + + ``` + +When you startup your Backstage app and hover over the Home option on the sidebar you'll now see a nice sub-menu appear with links to the various Kinds in your Catalog. It would look like this: + +![Sidebar sub-menu example](./../assets/getting-started/sidebar-submenu-example.png) + +You can see more ways to use this in the [Storybook Sidebar examples](https://backstage.io/storybook/?path=/story/layout-sidebar--sample-scalable-sidebar) + ## Custom Homepage In addition to a custom theme, a custom logo, you can also customize the diff --git a/microsite/data/plugins/cloudsmith.yaml b/microsite/data/plugins/cloudsmith.yaml new file mode 100644 index 0000000000..5fc417eec1 --- /dev/null +++ b/microsite/data/plugins/cloudsmith.yaml @@ -0,0 +1,13 @@ +--- +title: Cloudsmith +author: Roadie +authorUrl: https://roadie.io +category: CI/CD +description: Show Cloudsmith Repository stats on your backstage homepage. +documentation: https://github.com/RoadieHQ/roadie-backstage-plugins/tree/main/plugins/frontend/backstage-plugin-cloudsmith +iconUrl: https://cloudsmith.com/img/cloudsmith-mini-dark.svg +npmPackageName: '@roadiehq/backstage-plugin-cloudsmith' +tags: + - dashboards + - monitoring +addedDate: '2022-11-18' diff --git a/microsite/data/plugins/humanitec.yaml b/microsite/data/plugins/humanitec.yaml index 5b3c459116..1b79270582 100644 --- a/microsite/data/plugins/humanitec.yaml +++ b/microsite/data/plugins/humanitec.yaml @@ -6,7 +6,7 @@ category: Deployment # A single category e.g. CI, Machine Learning, Services, Mo description: | Show workloads, environments and resources deployed by Humanitec Platform Orchestrator. Plugin includes an Entity ComponentCard, Backend API route and scaffolder actions. -documentation: https://github.com/thefrontside/backstage/tree/main/plugins/humanitec +documentation: https://github.com/thefrontside/playhouse/tree/main/plugins/humanitec iconUrl: img/humanitec-logo.png npmPackageName: '@frontside/backstage-plugin-humanitec' addedDate: '2022-06-22' diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 0a943bbe83..1fa6e492cb 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -281,14 +281,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.10.4, @babel/helper-validator-identifier@npm:^7.12.11": - version: 7.12.11 - resolution: "@babel/helper-validator-identifier@npm:7.12.11" - checksum: e604c6bf890704fc46c1ae13bf23afb242b810224ec3403bba67cdbf0d8dabfec4b82123d6dfb18135a0ee3f7f79218583c819363ebb5e04a0a49d8418db7fce - languageName: node - linkType: hard - -"@babel/helper-validator-identifier@npm:^7.18.6": +"@babel/helper-validator-identifier@npm:^7.10.4, @babel/helper-validator-identifier@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-validator-identifier@npm:7.18.6" checksum: e295254d616bbe26e48c196a198476ab4d42a73b90478c9842536cf910ead887f5af6b5c4df544d3052a25ccb3614866fa808dc1e3a5a4291acd444e243c0648 @@ -1241,18 +1234,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.10.4, @babel/types@npm:^7.10.5, @babel/types@npm:^7.12.1, @babel/types@npm:^7.12.10, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.12, @babel/types@npm:^7.12.5, @babel/types@npm:^7.12.6, @babel/types@npm:^7.12.7, @babel/types@npm:^7.4.4": - version: 7.12.12 - resolution: "@babel/types@npm:7.12.12" - dependencies: - "@babel/helper-validator-identifier": ^7.12.11 - lodash: ^4.17.19 - to-fast-properties: ^2.0.0 - checksum: 1eefe9468573a32491030f2986f820eb4289ff0b93e437b97f665e73cc8dae0ec0bdd95f37995bb3b70b5ce2a9a45a302affbbad497150021cf874c5ef10e361 - languageName: node - linkType: hard - -"@babel/types@npm:^7.8.3": +"@babel/types@npm:^7.10.4, @babel/types@npm:^7.10.5, @babel/types@npm:^7.12.1, @babel/types@npm:^7.12.10, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.12, @babel/types@npm:^7.12.5, @babel/types@npm:^7.12.6, @babel/types@npm:^7.12.7, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.18.10 resolution: "@babel/types@npm:7.18.10" dependencies: @@ -2917,7 +2899,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.3.3": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.3.3": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -2929,7 +2911,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4.3.1, debug@npm:^4.1.0": +"debug@npm:4.3.1": version: 4.3.1 resolution: "debug@npm:4.3.1" dependencies: @@ -4275,21 +4257,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.0, glob@npm:^7.0.5, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.6": - version: 7.2.0 - resolution: "glob@npm:7.2.0" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^3.0.4 - once: ^1.3.0 - path-is-absolute: ^1.0.0 - checksum: 78a8ea942331f08ed2e055cb5b9e40fe6f46f579d7fd3d694f3412fe5db23223d29b7fee1575440202e9a7ff9a72ab106a39fee39934c7bedafe5e5f8ae20134 - languageName: node - linkType: hard - -"glob@npm:^7.1.4": +"glob@npm:^7.0.0, glob@npm:^7.0.5, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -4430,14 +4398,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.10, graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0": - version: 4.2.4 - resolution: "graceful-fs@npm:4.2.4" - checksum: 9d58c444eb4f391ce30b451aae8a8af2bd675d9f6f624719e97306f571ab89b2bd2b5f9025199bc63a2edfe2e53e7701554012f32a708148d53aa689163728cc - languageName: node - linkType: hard - -"graceful-fs@npm:^4.2.6": +"graceful-fs@npm:^4.1.10, graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6": version: 4.2.10 resolution: "graceful-fs@npm:4.2.10" checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da @@ -5056,15 +5017,6 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.1.0": - version: 2.2.0 - resolution: "is-core-module@npm:2.2.0" - dependencies: - has: ^1.0.3 - checksum: 61e2aff4a7db4f8f7d5a97b484808af17290f4197b34a797cd3d3d27b6b448951064f8d3d6ceae4394fa9b7e6cf08aacd2ba7a17ef6352e922fe803580fbde56 - languageName: node - linkType: hard - "is-core-module@npm:^2.8.0": version: 2.8.1 resolution: "is-core-module@npm:2.8.1" @@ -6196,7 +6148,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:3.0.4, minimatch@npm:^3.0.4, minimatch@npm:~3.0.2": +"minimatch@npm:3.0.4, minimatch@npm:~3.0.2": version: 3.0.4 resolution: "minimatch@npm:3.0.4" dependencies: @@ -6205,7 +6157,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.1.1": +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -6941,7 +6893,7 @@ __metadata: languageName: node linkType: hard -"path-parse@npm:^1.0.6, path-parse@npm:^1.0.7": +"path-parse@npm:^1.0.7": version: 1.0.7 resolution: "path-parse@npm:1.0.7" checksum: 49abf3d81115642938a8700ec580da6e830dde670be21893c62f4e10bd7dd4c3742ddc603fe24f898cba7eb0c6bc1777f8d9ac14185d34540c6d4d80cd9cae8a @@ -7967,7 +7919,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6": +"resolve@npm:^1.1.6, resolve@npm:^1.10.0": version: 1.21.0 resolution: "resolve@npm:1.21.0" dependencies: @@ -7980,17 +7932,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.10.0": - version: 1.19.0 - resolution: "resolve@npm:1.19.0" - dependencies: - is-core-module: ^2.1.0 - path-parse: ^1.0.6 - checksum: a05b356e47b85ad3613d9e2a39a824f3c27f4fcad9c9ff6c7cc71a2e314c5904a90ab37481ad0069d03cab9eaaac6eb68aca1bc3355fdb05f1045cd50e2aacea - languageName: node - linkType: hard - -"resolve@patch:resolve@^1.1.6#~builtin": +"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.10.0#~builtin": version: 1.21.0 resolution: "resolve@patch:resolve@npm%3A1.21.0#~builtin::version=1.21.0&hash=07638b" dependencies: @@ -8003,16 +7945,6 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.10.0#~builtin": - version: 1.19.0 - resolution: "resolve@patch:resolve@npm%3A1.19.0#~builtin::version=1.19.0&hash=07638b" - dependencies: - is-core-module: ^2.1.0 - path-parse: ^1.0.6 - checksum: 2443b94d347e6946c87c85faf13071f605e609e0b54784829b0ed2b917d050bfc1cbaf4ecc6453f224cfa7d0c5dcd97cbb273454cd210bee68e4af15c1a5abc9 - languageName: node - linkType: hard - "responselike@npm:1.0.2": version: 1.0.2 resolution: "responselike@npm:1.0.2" @@ -8313,14 +8245,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2": - version: 3.0.3 - resolution: "signal-exit@npm:3.0.3" - checksum: f0169d3f1263d06df32ca072b0bf33b34c6f8f0341a7a1621558a2444dfbe8f5fec76b35537fcc6f0bc4944bdb5336fe0bdcf41a5422c4e45a1dba3f45475e6c - languageName: node - linkType: hard - -"signal-exit@npm:^3.0.7": +"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 diff --git a/package.json b/package.json index 7b0fc5cddd..2f20eeeee3 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "build-storybook": "yarn ./storybook run build-storybook", "techdocs-cli": "node scripts/techdocs-cli.js", "techdocs-cli:dev": "cross-env TECHDOCS_CLI_DEV_MODE=true node scripts/techdocs-cli.js", - "prepare": "husky install" + "prepare": "husky install", + "postinstall": "husky install" }, "workspaces": { "packages": [ @@ -70,6 +71,7 @@ "e2e-test": "workspace:*", "eslint": "^8.6.0", "eslint-plugin-notice": "^0.9.10", + "eslint-plugin-testing-library": "^5.9.1", "fs-extra": "10.1.0", "husky": "^8.0.0", "lint-staged": "^13.0.0", @@ -92,6 +94,12 @@ ], "*.md": [ "node ./scripts/check-docs-quality" + ], + "./yarn.lock": [ + "node ./scripts/verify-lockfile-duplicates --fix" + ], + "*/yarn.lock": [ + "node ./scripts/verify-lockfile-duplicates --fix" ] }, "packageManager": "yarn@3.2.3" diff --git a/packages/app/cypress/integration/app.js b/packages/app/cypress/integration/app.js index 7b874907be..6371bcc4f8 100644 --- a/packages/app/cypress/integration/app.js +++ b/packages/app/cypress/integration/app.js @@ -25,14 +25,17 @@ describe('App', () => { it('should display support info when clicking the button', () => { cy.visit('/'); + // eslint-disable-next-line testing-library/await-async-query cy.findByTestId('support-button').click({ force: true }); cy.contains('#backstage'); }); it('should display error message when triggering it', () => { cy.visit('/'); + // eslint-disable-next-line testing-library/await-async-query cy.findByTestId('error-button').click({ force: true }); cy.contains('Error: Oh no!'); + // eslint-disable-next-line testing-library/await-async-query cy.findByTestId('error-button-close').click({ force: true }); }); @@ -55,6 +58,7 @@ describe('App', () => { cy.contains('Token') .get('input[name=github-auth-tf]') .type('password', { force: true }); + // eslint-disable-next-line testing-library/await-async-query cy.findByTestId('github-auth-button').click({ force: true }); cy.contains(`Welcome, ${name}!`); cy.contains('Logout').click({ force: true }); diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 41e5b15db9..8cec54326a 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -47,12 +47,21 @@ import { SidebarSpace, Link, useSidebarOpenState, + SidebarSubmenu, + SidebarSubmenuItem, } from '@backstage/core-components'; import { MyGroupsSidebarItem } from '@backstage/plugin-org'; import GroupIcon from '@material-ui/icons/People'; import { SearchModal } from '../search/SearchModal'; import Score from '@material-ui/icons/Score'; +import ApiIcon from '@material-ui/icons/Extension'; +import ComponentIcon from '@material-ui/icons/Memory'; +import DomainIcon from '@material-ui/icons/Apartment'; +import ResourceIcon from '@material-ui/icons/Work'; +import SystemIcon from '@material-ui/icons/Category'; +import UserIcon from '@material-ui/icons/Person'; + const useSidebarLogoStyles = makeStyles({ root: { width: sidebarConfig.drawerWidthClosed, @@ -93,7 +102,47 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( }> {/* Global nav, not org-specific */} - + + + + + + + + + + + + + ServiceFactory; + // @public (undocumented) export const loggerFactory: (options?: undefined) => ServiceFactory; diff --git a/packages/backend-app-api/src/services/implementations/index.ts b/packages/backend-app-api/src/services/implementations/index.ts index 608399bad9..2c26097f48 100644 --- a/packages/backend-app-api/src/services/implementations/index.ts +++ b/packages/backend-app-api/src/services/implementations/index.ts @@ -25,4 +25,5 @@ export { schedulerFactory } from './schedulerService'; export { tokenManagerFactory } from './tokenManagerService'; export { urlReaderFactory } from './urlReaderService'; export { httpRouterFactory } from './httpRouterService'; +export { lifecycleFactory } from './lifecycleService'; export type { HttpRouterFactoryOptions } from './httpRouterService'; diff --git a/packages/backend-app-api/src/services/implementations/lifecycleService.test.ts b/packages/backend-app-api/src/services/implementations/lifecycleService.test.ts new file mode 100644 index 0000000000..c0b69a3bef --- /dev/null +++ b/packages/backend-app-api/src/services/implementations/lifecycleService.test.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getVoidLogger } from '@backstage/backend-common'; +import { BackendLifecycleImpl } from './lifecycleService'; + +describe('lifecycleService', () => { + it('should execute registered shutdown hook', async () => { + const service = new BackendLifecycleImpl(getVoidLogger()); + const hook = jest.fn(); + service.addShutdownHook({ + pluginId: 'test', + fn: async () => { + hook(); + }, + }); + // should not execute the hook more than once. + await service.shutdown(); + await service.shutdown(); + await service.shutdown(); + expect(hook).toHaveBeenCalledTimes(1); + }); + + it('should not throw errors', async () => { + const service = new BackendLifecycleImpl(getVoidLogger()); + service.addShutdownHook({ + pluginId: 'test', + fn: async () => { + throw new Error('oh no'); + }, + }); + await expect(service.shutdown()).resolves.toBeUndefined(); + }); +}); diff --git a/packages/backend-app-api/src/services/implementations/lifecycleService.ts b/packages/backend-app-api/src/services/implementations/lifecycleService.ts new file mode 100644 index 0000000000..7d41a7a276 --- /dev/null +++ b/packages/backend-app-api/src/services/implementations/lifecycleService.ts @@ -0,0 +1,96 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + BackendLifecycle, + createServiceFactory, + lifecycleServiceRef, + loggerToWinstonLogger, + pluginMetadataServiceRef, + rootLoggerServiceRef, + BackendLifecycleShutdownHook, +} from '@backstage/backend-plugin-api'; +import { Logger } from 'winston'; + +const CALLBACKS = ['SIGTERM', 'SIGINT', 'beforeExit']; +export class BackendLifecycleImpl { + constructor(private readonly logger: Logger) { + CALLBACKS.map(signal => process.on(signal, () => this.shutdown())); + } + + #isCalled = false; + #shutdownTasks: Array = + []; + + addShutdownHook( + options: BackendLifecycleShutdownHook & { pluginId: string }, + ): void { + this.#shutdownTasks.push(options); + } + + async shutdown(): Promise { + if (this.#isCalled) { + return; + } + this.#isCalled = true; + + this.logger.info(`Running ${this.#shutdownTasks.length} shutdown tasks...`); + await Promise.all( + this.#shutdownTasks.map(hook => + Promise.resolve() + .then(() => hook.fn()) + .catch(e => { + this.logger.error( + `Shutdown hook registered by plugin '${hook.pluginId}' failed with: ${e}`, + ); + }) + .then(() => + this.logger.info( + `Successfully ran shutdown hook registered by plugin ${hook.pluginId}`, + ), + ), + ), + ); + } +} + +class PluginScopedLifecycleImpl implements BackendLifecycle { + constructor( + private readonly lifecycle: BackendLifecycleImpl, + private readonly pluginId: string, + ) {} + addShutdownHook(options: BackendLifecycleShutdownHook): void { + this.lifecycle.addShutdownHook({ ...options, pluginId: this.pluginId }); + } +} + +/** + * Allows plugins to register shutdown hooks that are run when the process is about to exit. + * @public */ +export const lifecycleFactory = createServiceFactory({ + service: lifecycleServiceRef, + deps: { + logger: rootLoggerServiceRef, + plugin: pluginMetadataServiceRef, + }, + async factory({ logger }) { + const rootLifecycle = new BackendLifecycleImpl( + loggerToWinstonLogger(logger), + ); + return async ({ plugin }) => { + return new PluginScopedLifecycleImpl(rootLifecycle, plugin.getId()); + }; + }, +}); diff --git a/packages/backend-defaults/src/CreateBackend.ts b/packages/backend-defaults/src/CreateBackend.ts index b95bd6f35b..9fde31e3bd 100644 --- a/packages/backend-defaults/src/CreateBackend.ts +++ b/packages/backend-defaults/src/CreateBackend.ts @@ -22,6 +22,7 @@ import { databaseFactory, discoveryFactory, httpRouterFactory, + lifecycleFactory, loggerFactory, permissionsFactory, rootLoggerFactory, @@ -43,6 +44,7 @@ export const defaultServiceFactories = [ tokenManagerFactory, urlReaderFactory, httpRouterFactory, + lifecycleFactory, ]; /** diff --git a/packages/backend-plugin-api/api-report.md b/packages/backend-plugin-api/api-report.md index 70ab168053..b73a85c32e 100644 --- a/packages/backend-plugin-api/api-report.md +++ b/packages/backend-plugin-api/api-report.md @@ -24,6 +24,16 @@ export interface BackendFeature { register(reg: BackendRegistrationPoints): void; } +// @public (undocumented) +export interface BackendLifecycle { + addShutdownHook(options: BackendLifecycleShutdownHook): void; +} + +// @public (undocumented) +export type BackendLifecycleShutdownHook = { + fn: () => void | Promise; +}; + // @public (undocumented) export interface BackendModuleConfig { // (undocumented) @@ -158,6 +168,9 @@ export interface HttpRouterService { // @public (undocumented) export const httpRouterServiceRef: ServiceRef; +// @public (undocumented) +export const lifecycleServiceRef: ServiceRef; + // @public (undocumented) export interface Logger { // (undocumented) diff --git a/packages/backend-plugin-api/src/services/definitions/index.ts b/packages/backend-plugin-api/src/services/definitions/index.ts index e5f032ef60..cf997a344d 100644 --- a/packages/backend-plugin-api/src/services/definitions/index.ts +++ b/packages/backend-plugin-api/src/services/definitions/index.ts @@ -28,4 +28,9 @@ export { permissionsServiceRef } from './permissionsServiceRef'; export { schedulerServiceRef } from './schedulerServiceRef'; export { rootLoggerServiceRef } from './rootLoggerServiceRef'; export { pluginMetadataServiceRef } from './pluginMetadataServiceRef'; +export { lifecycleServiceRef } from './lifecycleServiceRef'; +export type { + BackendLifecycle, + BackendLifecycleShutdownHook, +} from './lifecycleServiceRef'; export type { PluginMetadata } from './pluginMetadataServiceRef'; diff --git a/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts b/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts new file mode 100644 index 0000000000..15610e2643 --- /dev/null +++ b/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createServiceRef } from '../system/types'; + +/** + * @public + **/ +export type BackendLifecycleShutdownHook = { + fn: () => void | Promise; +}; + +/** + * @public + **/ +export interface BackendLifecycle { + /** + * Register a function to be called when the backend is shutting down. + */ + addShutdownHook(options: BackendLifecycleShutdownHook): void; +} + +/** + * @public + */ +export const lifecycleServiceRef = createServiceRef({ + id: 'core.lifecycle', + scope: 'plugin', +}); diff --git a/packages/backend/src/plugins/events.ts b/packages/backend/src/plugins/events.ts index 2ada92a5b6..7427d34217 100644 --- a/packages/backend/src/plugins/events.ts +++ b/packages/backend/src/plugins/events.ts @@ -27,14 +27,12 @@ export default async function createPlugin( subscribers: EventSubscriber[], ): Promise { const eventsRouter = Router(); - const httpRouter = Router(); - eventsRouter.use('/http', httpRouter); const http = HttpPostIngressEventPublisher.fromConfig({ config: env.config, logger: env.logger, - router: httpRouter, }); + http.bind(eventsRouter); await new EventsBackend(env.logger) .addPublishers(http) diff --git a/packages/catalog-client/api-report.md b/packages/catalog-client/api-report.md index 85fc389af7..2f76be79e2 100644 --- a/packages/catalog-client/api-report.md +++ b/packages/catalog-client/api-report.md @@ -34,6 +34,10 @@ export interface CatalogApi { request?: GetEntitiesRequest, options?: CatalogRequestOptions, ): Promise; + getEntitiesByRefs( + request: GetEntitiesByRefsRequest, + options?: CatalogRequestOptions, + ): Promise; getEntityAncestors( request: GetEntityAncestorsRequest, options?: CatalogRequestOptions, @@ -91,6 +95,10 @@ export class CatalogClient implements CatalogApi { request?: GetEntitiesRequest, options?: CatalogRequestOptions, ): Promise; + getEntitiesByRefs( + request: GetEntitiesByRefsRequest, + options?: CatalogRequestOptions, + ): Promise; getEntityAncestors( request: GetEntityAncestorsRequest, options?: CatalogRequestOptions, @@ -145,14 +153,30 @@ export interface CatalogRequestOptions { export const ENTITY_STATUS_CATALOG_PROCESSING_TYPE = 'backstage.io/catalog-processing'; +// @public +export type EntityFieldsQuery = string[]; + +// @public +export type EntityFilterQuery = + | Record[] + | Record; + +// @public +export interface GetEntitiesByRefsRequest { + entityRefs: string[]; + fields?: EntityFieldsQuery | undefined; +} + +// @public +export interface GetEntitiesByRefsResponse { + items: Array; +} + // @public export interface GetEntitiesRequest { after?: string; - fields?: string[] | undefined; - filter?: - | Record[] - | Record - | undefined; + fields?: EntityFieldsQuery; + filter?: EntityFilterQuery; limit?: number; offset?: number; } diff --git a/packages/catalog-client/src/CatalogClient.test.ts b/packages/catalog-client/src/CatalogClient.test.ts index 5a344f49a3..1206d557c1 100644 --- a/packages/catalog-client/src/CatalogClient.test.ts +++ b/packages/catalog-client/src/CatalogClient.test.ts @@ -195,6 +195,35 @@ describe('CatalogClient', () => { }); }); + describe('getEntitiesByRefs', () => { + it('encodes and decodes the query correctly', async () => { + const entity = { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'Test2', + namespace: 'test1', + }, + }; + server.use( + rest.post(`${mockBaseUrl}/entities/by-refs`, async (req, res, ctx) => { + expect(req.url.searchParams.get('fields')).toBe('a,b'); + await expect(req.json()).resolves.toEqual({ + entityRefs: ['k:n/a', 'k:n/b'], + }); + return res(ctx.json({ items: [entity, null] })); + }), + ); + + const response = await client.getEntitiesByRefs( + { entityRefs: ['k:n/a', 'k:n/b'], fields: ['a', 'b'] }, + { token }, + ); + + expect(response).toEqual({ items: [entity, null] }); + }); + }); + describe('getEntityByRef', () => { const existingEntity: Entity = { apiVersion: 'v1', diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index d67392d80d..d15f382ddd 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -37,6 +37,8 @@ import { GetEntityFacetsRequest, GetEntityFacetsResponse, ValidateEntityResponse, + GetEntitiesByRefsRequest, + GetEntitiesByRefsResponse, } from './types/api'; import { DiscoveryApi } from './types/discovery'; import { FetchApi } from './types/fetch'; @@ -169,6 +171,40 @@ export class CatalogClient implements CatalogApi { return { items: entities.sort(refCompare) }; } + /** + * {@inheritdoc CatalogApi.getEntitiesByRefs} + */ + async getEntitiesByRefs( + request: GetEntitiesByRefsRequest, + options?: CatalogRequestOptions, + ): Promise { + const params: string[] = []; + if (request.fields?.length) { + params.push(`fields=${request.fields.map(encodeURIComponent).join(',')}`); + } + + const baseUrl = await this.discoveryApi.getBaseUrl('catalog'); + const query = params.length ? `?${params.join('&')}` : ''; + const url = `${baseUrl}/entities/by-refs${query}`; + + const response = await this.fetchApi.fetch(url, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'POST', + body: JSON.stringify({ entityRefs: request.entityRefs }), + }); + + if (!response.ok) { + throw await ResponseError.fromResponse(response); + } + + const { items } = await response.json(); + + return { items }; + } + /** * {@inheritdoc CatalogApi.getEntityByRef} */ diff --git a/packages/catalog-client/src/types/api.ts b/packages/catalog-client/src/types/api.ts index d3d02022f3..771adaaeee 100644 --- a/packages/catalog-client/src/types/api.ts +++ b/packages/catalog-client/src/types/api.ts @@ -29,6 +29,75 @@ export const CATALOG_FILTER_EXISTS = Symbol.for( 'CATALOG_FILTER_EXISTS_0e15b590c0b343a2bae3e787e84c2111', ); +/** + * A key-value based filter expression for entities. + * + * @remarks + * + * Each key of a record is a dot-separated path into the entity structure, e.g. + * `metadata.name`. + * + * The values are literal values to match against. As a value you can also pass + * in the symbol `CATALOG_FILTER_EXISTS` (exported from this package), which + * means that you assert on the existence of that key, no matter what its value + * is. + * + * All matching of keys and values is case insensitive. + * + * If multiple filter sets are given as an array, then there is effectively an + * OR between each filter set. + * + * Within one filter set, there is effectively an AND between the various keys. + * + * Within one key, if there are more than one value, then there is effectively + * an OR between them. + * + * Example: For an input of + * + * ``` + * [ + * { kind: ['API', 'Component'] }, + * { 'metadata.name': 'a', 'metadata.namespace': 'b' } + * ] + * ``` + * + * This effectively means + * + * ``` + * (kind = EITHER 'API' OR 'Component') + * OR + * (metadata.name = 'a' AND metadata.namespace = 'b' ) + * ``` + * + * @public + */ +export type EntityFilterQuery = + | Record[] + | Record; + +/** + * A set of dot-separated paths into an entity's keys, showing what parts of an + * entity to include in a response, and excluding all others. + * + * @remarks + * + * Example: For an input of `['kind', 'metadata.annotations']`, then response + * objects will be shaped like + * + * ``` + * { + * "kind": "Component", + * "metadata": { + * "annotations": { + * "foo": "bar" + * } + * } + * } + * ``` + * @public + */ +export type EntityFieldsQuery = string[]; + /** * The request type for {@link CatalogClient.getEntities}. * @@ -36,67 +105,14 @@ export const CATALOG_FILTER_EXISTS = Symbol.for( */ export interface GetEntitiesRequest { /** - * If given, return only entities that match the given patterns. - * - * @remarks - * - * If multiple filter sets are given as an array, then there is effectively an - * OR between each filter set. - * - * Within one filter set, there is effectively an AND between the various - * keys. - * - * Within one key, if there are more than one value, then there is effectively - * an OR between them. - * - * Example: For an input of - * - * ``` - * [ - * { kind: ['API', 'Component'] }, - * { 'metadata.name': 'a', 'metadata.namespace': 'b' } - * ] - * ``` - * - * This effectively means - * - * ``` - * (kind = EITHER 'API' OR 'Component') - * OR - * (metadata.name = 'a' AND metadata.namespace = 'b' ) - * ``` - * - * Each key is a dot separated path in each object. - * - * As a value you can also pass in the symbol `CATALOG_FILTER_EXISTS` - * (exported from this package), which means that you assert on the existence - * of that key, no matter what its value is. + * If given, return only entities that match the given filter. */ - filter?: - | Record[] - | Record - | undefined; + filter?: EntityFilterQuery; /** - * If given, return only the parts of each entity that match those dot - * separated paths in each object. - * - * @remarks - * - * Example: For an input of `['kind', 'metadata.annotations']`, then response - * objects will be shaped like - * - * ``` - * { - * "kind": "Component", - * "metadata": { - * "annotations": { - * "foo": "bar" - * } - * } - * } - * ``` + * If given, return only the parts of each entity that match the field + * declarations. */ - fields?: string[] | undefined; + fields?: EntityFieldsQuery; /** * If given, skips over the first N items in the result set. */ @@ -121,6 +137,45 @@ export interface GetEntitiesResponse { items: Entity[]; } +/** + * The request type for {@link CatalogClient.getEntitiesByRefs}. + * + * @public + */ +export interface GetEntitiesByRefsRequest { + /** + * The list of entity refs to fetch. + * + * @remarks + * + * The returned list of entities will be in the same order as the refs, and + * null will be returned in those positions that were not found. + */ + entityRefs: string[]; + /** + * If given, return only the parts of each entity that match the field + * declarations. + */ + fields?: EntityFieldsQuery | undefined; +} + +/** + * The response type for {@link CatalogClient.getEntitiesByRefs}. + * + * @public + */ +export interface GetEntitiesByRefsResponse { + /** + * The returned list of entities. + * + * @remarks + * + * The list will be in the same order as the refs given in the request, and + * null will be returned in those positions that were not found. + */ + items: Array; +} + /** * The request type for {@link CatalogClient.getEntityAncestors}. * @@ -296,6 +351,23 @@ export interface CatalogApi { options?: CatalogRequestOptions, ): Promise; + /** + * Gets a batch of entities, by their entity refs. + * + * @remarks + * + * The output list of entities is of the same size and in the same order as + * the requested list of entity refs. Entries that are not found are returned + * as null. + * + * @param request - Request parameters + * @param options - Additional options + */ + getEntitiesByRefs( + request: GetEntitiesByRefsRequest, + options?: CatalogRequestOptions, + ): Promise; + /** * Gets entity ancestor information, i.e. the hierarchy of parent entities * whose processing resulted in a given entity appearing in the catalog. diff --git a/packages/catalog-client/src/types/index.ts b/packages/catalog-client/src/types/index.ts index 86f7b18ab0..5ac058e349 100644 --- a/packages/catalog-client/src/types/index.ts +++ b/packages/catalog-client/src/types/index.ts @@ -20,13 +20,17 @@ export type { AddLocationResponse, CatalogApi, CatalogRequestOptions, + EntityFieldsQuery, + EntityFilterQuery, + GetEntitiesByRefsRequest, + GetEntitiesByRefsResponse, GetEntitiesRequest, GetEntitiesResponse, GetEntityAncestorsRequest, GetEntityAncestorsResponse, - Location, GetEntityFacetsRequest, GetEntityFacetsResponse, + Location, ValidateEntityResponse, } from './api'; export { ENTITY_STATUS_CATALOG_PROCESSING_TYPE } from './status'; diff --git a/packages/core-app-api/src/routing/FeatureFlagged.test.tsx b/packages/core-app-api/src/routing/FeatureFlagged.test.tsx index d1b45c39d1..410540f77e 100644 --- a/packages/core-app-api/src/routing/FeatureFlagged.test.tsx +++ b/packages/core-app-api/src/routing/FeatureFlagged.test.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import React from 'react'; import { FeatureFlagged } from './FeatureFlagged'; import { render } from '@testing-library/react'; @@ -44,7 +45,7 @@ describe('FeatureFlagged', () => { , ); - expect(await queryByText('BACKSTAGE!')).toBeInTheDocument(); + expect(queryByText('BACKSTAGE!')).toBeInTheDocument(); }); it('should not render contents when the feature flag is disabled', async () => { jest @@ -61,7 +62,7 @@ describe('FeatureFlagged', () => { , ); - expect(await queryByText('BACKSTAGE!')).not.toBeInTheDocument(); + expect(queryByText('BACKSTAGE!')).not.toBeInTheDocument(); }); }); describe('without', () => { @@ -80,7 +81,7 @@ describe('FeatureFlagged', () => { , ); - expect(await queryByText('BACKSTAGE!')).not.toBeInTheDocument(); + expect(queryByText('BACKSTAGE!')).not.toBeInTheDocument(); }); it('should render contents when the feature flag is disabled', async () => { jest @@ -97,7 +98,7 @@ describe('FeatureFlagged', () => { , ); - expect(await queryByText('BACKSTAGE!')).toBeInTheDocument(); + expect(queryByText('BACKSTAGE!')).toBeInTheDocument(); }); }); }); diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index fc3ab4e5fc..248b9e227c 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -250,6 +250,7 @@ export interface DependencyGraphProps edgeRanks?: number; edges: DependencyEdge[]; edgeWeight?: number; + fit?: 'grow' | 'contain'; labelOffset?: number; // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver labelPosition?: LabelPosition; diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 4a7fec9ef1..93ccbfb98a 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -56,7 +56,7 @@ "pluralize": "^8.0.0", "prop-types": "^15.7.2", "qs": "^6.9.4", - "rc-progress": "3.4.0", + "rc-progress": "3.4.1", "react-helmet": "6.1.0", "react-hook-form": "^7.12.2", "react-markdown": "^8.0.0", diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index 5159f67d9e..94d152ce1e 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -170,6 +170,14 @@ export interface DependencyGraphProps * Default: 'curveMonotoneX' */ curve?: 'curveStepBefore' | 'curveMonotoneX'; + /** + * Controls if the graph should be contained or grow + * + * @remarks + * + * Default: 'grow' + */ + fit?: 'grow' | 'contain'; } const WORKSPACE_ID = 'workspace'; @@ -203,6 +211,7 @@ export function DependencyGraph( defs, zoom = 'enabled', curve = 'curveMonotoneX', + fit = 'grow', ...svgProps } = props; const theme: BackstageTheme = useTheme(); @@ -223,6 +232,9 @@ export function DependencyGraph( const maxWidth = Math.max(graphWidth, containerWidth); const maxHeight = Math.max(graphHeight, containerHeight); + const minHeight = Math.min(graphHeight, containerHeight); + + const scalableHeight = fit === 'grow' ? maxHeight : minHeight; const containerRef = React.useMemo( () => @@ -394,7 +406,7 @@ export function DependencyGraph( ref={containerRef} {...svgProps} width="100%" - height={maxHeight} + height={scalableHeight} viewBox={`0 0 ${maxWidth} ${maxHeight}`} > diff --git a/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.test.tsx b/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.test.tsx index 35ea30ffa6..f72e33ac4b 100644 --- a/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.test.tsx +++ b/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.test.tsx @@ -70,8 +70,8 @@ describe('', () => { description="description" />, ); - const dot = await getByTestId('dot'); - const text = await getByTestId('text'); + const dot = getByTestId('dot'); + const text = getByTestId('text'); expect(dot).toBeInTheDocument(); expect(text).toBeInTheDocument(); @@ -96,8 +96,8 @@ describe('', () => { description="description" />, ); - const dot = await getByTestId('dot'); - const text = await getByTestId('text'); + const dot = getByTestId('dot'); + const text = getByTestId('text'); act(() => { Element.prototype.getBoundingClientRect = jest.fn( @@ -128,8 +128,8 @@ describe('', () => { description="description" />, ); - const dot = await getByTestId('dot'); - const text = await getByTestId('text'); + const dot = getByTestId('dot'); + const text = getByTestId('text'); act(() => { Element.prototype.getBoundingClientRect = jest.fn( diff --git a/packages/core-components/src/components/Select/Select.test.tsx b/packages/core-components/src/components/Select/Select.test.tsx index 41e787a5b4..df29983222 100644 --- a/packages/core-components/src/components/Select/Select.test.tsx +++ b/packages/core-components/src/components/Select/Select.test.tsx @@ -41,7 +41,7 @@ describe('); expect(getByText('Default')).toBeInTheDocument(); - const input = await getByTestId('select'); + const input = getByTestId('select'); expect(input.textContent).toBe('All results'); // Simulate click on input diff --git a/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx b/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx index 25cf642c28..5d00784fd1 100644 --- a/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx +++ b/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx @@ -18,7 +18,6 @@ import React from 'react'; import { fireEvent, screen } from '@testing-library/react'; import { renderInTestApp } from '@backstage/test-utils'; import Typography from '@material-ui/core/Typography'; - import { WarningPanel, WarningProps } from './WarningPanel'; const propsTitle: WarningProps = { title: 'Mock title' }; @@ -41,7 +40,7 @@ describe('', () => { it('renders title', async () => { await renderInTestApp(); - const expandIcon = await screen.getByText('Warning: Mock title'); + const expandIcon = screen.getByText('Warning: Mock title'); fireEvent.click(expandIcon); expect(screen.getByText('Warning: Mock title')).toBeInTheDocument(); expect(screen.getByText('Some more info')).toBeInTheDocument(); diff --git a/plugins/bitrise/src/components/BitriseBuildDetailsDialog/BitriseBuildDetailsDialog.test.tsx b/plugins/bitrise/src/components/BitriseBuildDetailsDialog/BitriseBuildDetailsDialog.test.tsx index 2dcffc3bb3..7992e08f37 100644 --- a/plugins/bitrise/src/components/BitriseBuildDetailsDialog/BitriseBuildDetailsDialog.test.tsx +++ b/plugins/bitrise/src/components/BitriseBuildDetailsDialog/BitriseBuildDetailsDialog.test.tsx @@ -46,7 +46,7 @@ describe('BitriseArtifactsComponent', () => { const rendered = renderComponent(); const btn = await rendered.findByTestId('btn'); - expect(await rendered.queryByText('VISIBLE')).not.toBeInTheDocument(); + expect(rendered.queryByText('VISIBLE')).not.toBeInTheDocument(); btn.click(); diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index 10b9afc291..e5d464c29a 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -37,7 +37,7 @@ import { EntityRelationSpec } from '@backstage/plugin-catalog-node'; import { GetEntitiesRequest } from '@backstage/catalog-client'; import { JsonValue } from '@backstage/types'; import { LocationEntityV1alpha1 } from '@backstage/catalog-model'; -import { LocationSpec } from '@backstage/plugin-catalog-node'; +import { LocationSpec as LocationSpec_2 } from '@backstage/plugin-catalog-common'; import { Logger } from 'winston'; import { Permission } from '@backstage/plugin-permission-common'; import { PermissionAuthorizer } from '@backstage/plugin-permission-common'; @@ -86,9 +86,9 @@ export class AnnotateLocationEntityProcessor implements CatalogProcessor { // (undocumented) preProcessEntity( entity: Entity, - location: LocationSpec, + location: LocationSpec_2, _: CatalogProcessorEmit, - originLocation: LocationSpec, + originLocation: LocationSpec_2, ): Promise; } @@ -100,7 +100,7 @@ export class AnnotateScmSlugEntityProcessor implements CatalogProcessor { // (undocumented) getProcessorName(): string; // (undocumented) - preProcessEntity(entity: Entity, location: LocationSpec): Promise; + preProcessEntity(entity: Entity, location: LocationSpec_2): Promise; } // @public (undocumented) @@ -110,7 +110,7 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor { // (undocumented) postProcessEntity( entity: Entity, - _location: LocationSpec, + _location: LocationSpec_2, emit: CatalogProcessorEmit, ): Promise; // (undocumented) @@ -284,7 +284,7 @@ export class CodeOwnersProcessor implements CatalogProcessor { // (undocumented) getProcessorName(): string; // (undocumented) - preProcessEntity(entity: Entity, location: LocationSpec): Promise; + preProcessEntity(entity: Entity, location: LocationSpec_2): Promise; } // @alpha @@ -409,7 +409,7 @@ export class FileReaderProcessor implements CatalogProcessor { getProcessorName(): string; // (undocumented) readLocation( - location: LocationSpec, + location: LocationSpec_2, optional: boolean, emit: CatalogProcessorEmit, parser: CatalogProcessorParser, @@ -431,7 +431,7 @@ export class LocationEntityProcessor implements CatalogProcessor { // (undocumented) postProcessEntity( entity: Entity, - location: LocationSpec, + location: LocationSpec_2, emit: CatalogProcessorEmit, ): Promise; } @@ -441,18 +441,19 @@ export type LocationEntityProcessorOptions = { integrations: ScmIntegrationRegistry; }; -export { LocationSpec }; +// @public @deprecated +export type LocationSpec = LocationSpec_2; // @public (undocumented) export function locationSpecToLocationEntity(opts: { - location: LocationSpec; + location: LocationSpec_2; parentEntity?: Entity; }): LocationEntityV1alpha1; // @public (undocumented) export function parseEntityYaml( data: Buffer, - location: LocationSpec, + location: LocationSpec_2, ): Iterable; // @alpha @@ -518,7 +519,7 @@ export class PlaceholderProcessor implements CatalogProcessor { // (undocumented) preProcessEntity( entity: Entity, - location: LocationSpec, + location: LocationSpec_2, emit: CatalogProcessorEmit, ): Promise; } @@ -574,7 +575,7 @@ export class UrlReaderProcessor implements CatalogProcessor { getProcessorName(): string; // (undocumented) readLocation( - location: LocationSpec, + location: LocationSpec_2, optional: boolean, emit: CatalogProcessorEmit, parser: CatalogProcessorParser, diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index 34060814aa..84673a3900 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -80,6 +80,36 @@ export type EntitiesResponse = { pageInfo: PageInfo; }; +/** + * A request for a batch of entities. + */ +export interface EntitiesBatchRequest { + /** + * The refs for which to fetch entities. + */ + entityRefs: string[]; + /** + * Any additional filters to apply in the selection of the entities. + */ + filter?: EntityFilter; + /** + * Strips out only the parts of the entity bodies to include in the response. + */ + fields?: (entity: Entity) => Entity; + /** + * The optional token that authorizes the action. + */ + authorizationToken?: string; +} + +export interface EntitiesBatchResponse { + /** + * The list of entities, in the same order as the refs in the request. Entries + * that are null signify that no entity existed with that ref. + */ + items: Array; +} + export type EntityAncestryResponse = { rootEntityRef: string; items: Array<{ @@ -130,6 +160,11 @@ export interface EntitiesCatalog { */ entities(request?: EntitiesRequest): Promise; + /** + * Fetches a batch of entities. + */ + entitiesBatch(request: EntitiesBatchRequest): Promise; + /** * Removes a single entity. * diff --git a/plugins/catalog-backend/src/index.ts b/plugins/catalog-backend/src/index.ts index 88696498d0..000dd94e8b 100644 --- a/plugins/catalog-backend/src/index.ts +++ b/plugins/catalog-backend/src/index.ts @@ -22,7 +22,6 @@ export type { DeferredEntity, - LocationSpec, EntityRelationSpec, CatalogProcessor, CatalogProcessorParser, @@ -48,3 +47,19 @@ export * from './processing'; export * from './search'; export * from './service'; export * from './util'; + +import { LocationSpec as NonDeprecatedLocationSpec } from '@backstage/plugin-catalog-common'; + +/** + * Holds the entity location information. + * + * @remarks + * + * `presence` flag: when using repo importer plugin, location is being created before the component yaml file is merged to the main branch. + * This flag is then set to indicate that the file can be not present. + * default value: 'required'. + * + * @public + * @deprecated use the same type from `@backstage/plugin-catalog-common` instead + */ +export type LocationSpec = NonDeprecatedLocationSpec; diff --git a/plugins/catalog-backend/src/ingestion/CatalogRules.ts b/plugins/catalog-backend/src/ingestion/CatalogRules.ts index ce226bd09d..548c71d833 100644 --- a/plugins/catalog-backend/src/ingestion/CatalogRules.ts +++ b/plugins/catalog-backend/src/ingestion/CatalogRules.ts @@ -17,7 +17,7 @@ import { Config } from '@backstage/config'; import { Entity } from '@backstage/catalog-model'; import path from 'path'; -import { LocationSpec } from '@backstage/plugin-catalog-node'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; /** * Rules to apply to catalog entities. diff --git a/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.ts b/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.ts index 63f3d406f5..186b732271 100644 --- a/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.ts +++ b/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.ts @@ -22,7 +22,8 @@ import { ScmIntegrations, } from '@backstage/integration'; import { Logger } from 'winston'; -import { CatalogProcessor, LocationSpec } from '@backstage/plugin-catalog-node'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; +import { CatalogProcessor } from '@backstage/plugin-catalog-node'; import { findCodeOwnerByTarget } from './lib'; const ALLOWED_KINDS = ['API', 'Component', 'Domain', 'Resource', 'System']; diff --git a/plugins/catalog-backend/src/modules/core/AnnotateLocationEntityProcessor.ts b/plugins/catalog-backend/src/modules/core/AnnotateLocationEntityProcessor.ts index a86525429e..d3329fa86c 100644 --- a/plugins/catalog-backend/src/modules/core/AnnotateLocationEntityProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/AnnotateLocationEntityProcessor.ts @@ -25,10 +25,10 @@ import { } from '@backstage/catalog-model'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { identity, merge, pickBy } from 'lodash'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { CatalogProcessor, CatalogProcessorEmit, - LocationSpec, } from '@backstage/plugin-catalog-node'; /** @public */ diff --git a/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.ts b/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.ts index 41e75e5e8e..2c18ba1d7d 100644 --- a/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Entity } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { @@ -21,7 +22,8 @@ import { } from '@backstage/integration'; import parseGitUrl from 'git-url-parse'; import { identity, merge, pickBy } from 'lodash'; -import { CatalogProcessor, LocationSpec } from '@backstage/plugin-catalog-node'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; +import { CatalogProcessor } from '@backstage/plugin-catalog-node'; const GITHUB_ACTIONS_ANNOTATION = 'github.com/project-slug'; const GITLAB_ACTIONS_ANNOTATION = 'gitlab.com/project-slug'; diff --git a/plugins/catalog-backend/src/modules/core/BuiltinKindsEntityProcessor.ts b/plugins/catalog-backend/src/modules/core/BuiltinKindsEntityProcessor.ts index 6e05daf591..4dbb23addc 100644 --- a/plugins/catalog-backend/src/modules/core/BuiltinKindsEntityProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/BuiltinKindsEntityProcessor.ts @@ -48,10 +48,10 @@ import { UserEntity, userEntityV1alpha1Validator, } from '@backstage/catalog-model'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { CatalogProcessor, CatalogProcessorEmit, - LocationSpec, processingResult, } from '@backstage/plugin-catalog-node'; diff --git a/plugins/catalog-backend/src/modules/core/FileReaderProcessor.ts b/plugins/catalog-backend/src/modules/core/FileReaderProcessor.ts index af6fba52c0..e0cd11fa3b 100644 --- a/plugins/catalog-backend/src/modules/core/FileReaderProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/FileReaderProcessor.ts @@ -18,11 +18,11 @@ import fs from 'fs-extra'; import g from 'glob'; import path from 'path'; import { promisify } from 'util'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { CatalogProcessor, CatalogProcessorEmit, CatalogProcessorParser, - LocationSpec, processingResult, } from '@backstage/plugin-catalog-node'; diff --git a/plugins/catalog-backend/src/modules/core/LocationEntityProcessor.ts b/plugins/catalog-backend/src/modules/core/LocationEntityProcessor.ts index 3676b4f727..1db74b3b56 100644 --- a/plugins/catalog-backend/src/modules/core/LocationEntityProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/LocationEntityProcessor.ts @@ -17,11 +17,11 @@ import { Entity, LocationEntity } from '@backstage/catalog-model'; import { ScmIntegrationRegistry } from '@backstage/integration'; import path from 'path'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { processingResult, CatalogProcessor, CatalogProcessorEmit, - LocationSpec, } from '@backstage/plugin-catalog-node'; export function toAbsoluteUrl( diff --git a/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts b/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts index e6b5ee279d..ec432c5fdc 100644 --- a/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts @@ -19,10 +19,10 @@ import { Entity } from '@backstage/catalog-model'; import { JsonValue } from '@backstage/types'; import { ScmIntegrationRegistry } from '@backstage/integration'; import yaml from 'yaml'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { CatalogProcessor, CatalogProcessorEmit, - LocationSpec, processingResult, } from '@backstage/plugin-catalog-node'; diff --git a/plugins/catalog-backend/src/modules/core/UrlReaderProcessor.ts b/plugins/catalog-backend/src/modules/core/UrlReaderProcessor.ts index fe9f21ebb4..5e31dcbd1a 100644 --- a/plugins/catalog-backend/src/modules/core/UrlReaderProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/UrlReaderProcessor.ts @@ -20,6 +20,7 @@ import { assertError } from '@backstage/errors'; import parseGitUrl from 'git-url-parse'; import limiterFactory from 'p-limit'; import { Logger } from 'winston'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { CatalogProcessor, CatalogProcessorCache, @@ -27,7 +28,6 @@ import { CatalogProcessorEntityResult, CatalogProcessorParser, CatalogProcessorResult, - LocationSpec, processingResult, } from '@backstage/plugin-catalog-node'; diff --git a/plugins/catalog-backend/src/modules/util/parse.ts b/plugins/catalog-backend/src/modules/util/parse.ts index d3aa915e49..778c176323 100644 --- a/plugins/catalog-backend/src/modules/util/parse.ts +++ b/plugins/catalog-backend/src/modules/util/parse.ts @@ -17,10 +17,10 @@ import { Entity, stringifyLocationRef } from '@backstage/catalog-model'; import lodash from 'lodash'; import yaml from 'yaml'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { CatalogProcessorParser, CatalogProcessorResult, - LocationSpec, processingResult, } from '@backstage/plugin-catalog-node'; diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts index 79876f70d8..e95fede8b7 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts @@ -32,10 +32,10 @@ import { JsonValue } from '@backstage/types'; import { ScmIntegrationRegistry } from '@backstage/integration'; import path from 'path'; import { Logger } from 'winston'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { CatalogProcessor, CatalogProcessorParser, - LocationSpec, processingResult, } from '@backstage/plugin-catalog-node'; import { diff --git a/plugins/catalog-backend/src/processing/util.ts b/plugins/catalog-backend/src/processing/util.ts index bd96763a54..cd93be3c8b 100644 --- a/plugins/catalog-backend/src/processing/util.ts +++ b/plugins/catalog-backend/src/processing/util.ts @@ -27,7 +27,7 @@ import { JsonObject, JsonValue } from '@backstage/types'; import { InputError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; import path from 'path'; -import { LocationSpec } from '@backstage/plugin-catalog-node'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; export function isLocationEntity(entity: Entity): entity is LocationEntity { return entity.kind === 'Location'; diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts index 66764dc89f..cbaf876071 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts @@ -24,6 +24,7 @@ import { AuthorizedEntitiesCatalog } from './AuthorizedEntitiesCatalog'; describe('AuthorizedEntitiesCatalog', () => { const fakeCatalog = { entities: jest.fn(), + entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), facets: jest.fn(), @@ -92,6 +93,67 @@ describe('AuthorizedEntitiesCatalog', () => { }); }); + describe('entitiesBatch', () => { + it('returns empty response on DENY', async () => { + fakePermissionApi.authorizeConditional.mockResolvedValue([ + { result: AuthorizeResult.DENY }, + ]); + const catalog = createCatalog(); + + await expect( + catalog.entitiesBatch({ + entityRefs: ['component:default/component-a'], + authorizationToken: 'abcd', + }), + ).resolves.toEqual({ + items: [null], + }); + + expect(fakeCatalog.entitiesBatch).not.toHaveBeenCalled(); + }); + + it('calls underlying catalog method with correct filter on CONDITIONAL', async () => { + fakePermissionApi.authorizeConditional.mockResolvedValue([ + { + result: AuthorizeResult.CONDITIONAL, + conditions: { + rule: 'IS_ENTITY_KIND', + params: { kinds: ['b'] }, + }, + }, + ]); + const catalog = createCatalog(isEntityKind); + + await catalog.entitiesBatch({ + entityRefs: ['component:default/component-a'], + authorizationToken: 'abcd', + }); + + expect(fakeCatalog.entitiesBatch).toHaveBeenCalledWith({ + entityRefs: ['component:default/component-a'], + authorizationToken: 'abcd', + filter: { key: 'kind', values: ['b'] }, + }); + }); + + it('calls underlying catalog method on ALLOW', async () => { + fakePermissionApi.authorizeConditional.mockResolvedValue([ + { result: AuthorizeResult.ALLOW }, + ]); + const catalog = createCatalog(); + + await catalog.entitiesBatch({ + entityRefs: ['component:default/component-a'], + authorizationToken: 'abcd', + }); + + expect(fakeCatalog.entitiesBatch).toHaveBeenCalledWith({ + entityRefs: ['component:default/component-a'], + authorizationToken: 'abcd', + }); + }); + }); + describe('removeEntityByUid', () => { it('throws error on DENY', async () => { fakeCatalog.entities.mockResolvedValue({ diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts index 0e861a39e1..c62e9f9a84 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts @@ -26,6 +26,8 @@ import { } from '@backstage/plugin-permission-common'; import { ConditionTransformer } from '@backstage/plugin-permission-node'; import { + EntitiesBatchRequest, + EntitiesBatchResponse, EntitiesCatalog, EntitiesRequest, EntitiesResponse, @@ -73,6 +75,37 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog { return this.entitiesCatalog.entities(request); } + async entitiesBatch( + request: EntitiesBatchRequest, + ): Promise { + const authorizeDecision = ( + await this.permissionApi.authorizeConditional( + [{ permission: catalogEntityReadPermission }], + { token: request?.authorizationToken }, + ) + )[0]; + + if (authorizeDecision.result === AuthorizeResult.DENY) { + return { + items: new Array(request.entityRefs.length).fill(null), + }; + } + + if (authorizeDecision.result === AuthorizeResult.CONDITIONAL) { + const permissionFilter: EntityFilter = this.transformConditions( + authorizeDecision.conditions, + ); + return this.entitiesCatalog.entitiesBatch({ + ...request, + filter: request?.filter + ? { allOf: [permissionFilter, request.filter] } + : permissionFilter, + }); + } + + return this.entitiesCatalog.entitiesBatch(request); + } + async removeEntityByUid( uid: string, options?: { authorizationToken?: string }, diff --git a/plugins/catalog-backend/src/service/CatalogPlugin.ts b/plugins/catalog-backend/src/service/CatalogPlugin.ts index 64e1e52494..84cad86d9a 100644 --- a/plugins/catalog-backend/src/service/CatalogPlugin.ts +++ b/plugins/catalog-backend/src/service/CatalogPlugin.ts @@ -22,6 +22,7 @@ import { permissionsServiceRef, urlReaderServiceRef, httpRouterServiceRef, + lifecycleServiceRef, } from '@backstage/backend-plugin-api'; import { CatalogBuilder } from './CatalogBuilder'; import { @@ -78,6 +79,7 @@ export const catalogPlugin = createBackendPlugin({ permissions: permissionsServiceRef, database: databaseServiceRef, httpRouter: httpRouterServiceRef, + lifecycle: lifecycleServiceRef, }, async init({ logger, @@ -86,6 +88,7 @@ export const catalogPlugin = createBackendPlugin({ database, permissions, httpRouter, + lifecycle, }) { const winstonLogger = loggerToWinstonLogger(logger); const builder = await CatalogBuilder.create({ @@ -100,7 +103,11 @@ export const catalogPlugin = createBackendPlugin({ const { processingEngine, router } = await builder.build(); await processingEngine.start(); - + lifecycle.addShutdownHook({ + fn: async () => { + await processingEngine.stop(); + }, + }); httpRouter.use(router); }, }); diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts index 49d4fe9e19..515a862515 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts @@ -534,6 +534,60 @@ describe('DefaultEntitiesCatalog', () => { ); }); + describe('entitiesBatch', () => { + it.each(databases.eachSupportedId())( + 'queries for entities by ref, including duplicates, and gracefully returns null for missing entities', + async databaseId => { + const { knex } = await createDatabase(databaseId); + + await addEntity( + knex, + { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'one' }, + spec: {}, + relations: [], + }, + [], + ); + await addEntity( + knex, + { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'two' }, + spec: {}, + relations: [], + }, + [], + ); + + const catalog = new DefaultEntitiesCatalog(knex, stitcher); + + const { items } = await catalog.entitiesBatch({ + entityRefs: [ + 'k:default/two', + 'k:default/one', + 'k:default/two', + 'not-even-a-ref', + 'k:default/does-not-exist', + 'k:default/two', + ], + }); + + expect(items.map(e => e && stringifyEntityRef(e))).toEqual([ + 'k:default/two', + 'k:default/one', + 'k:default/two', + null, + null, + 'k:default/two', + ]); + }, + ); + }); + describe('removeEntityByUid', () => { it.each(databases.eachSupportedId())( 'also clears parent hashes', diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index dc84b12484..81de6484c3 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -23,6 +23,8 @@ import { InputError, NotFoundError } from '@backstage/errors'; import { Knex } from 'knex'; import lodash from 'lodash'; import { + EntitiesBatchRequest, + EntitiesBatchResponse, EntitiesCatalog, EntitiesRequest, EntitiesResponse, @@ -237,6 +239,38 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { }; } + async entitiesBatch( + request: EntitiesBatchRequest, + ): Promise { + const lookup = new Map(); + + for (const chunk of lodash.chunk(request.entityRefs, 200)) { + let query = this.database('final_entities') + .innerJoin('refresh_state', { + 'refresh_state.entity_id': 'final_entities.entity_id', + }) + .select({ + entityRef: 'refresh_state.entity_ref', + entity: 'final_entities.final_entity', + }) + .whereIn('refresh_state.entity_ref', chunk); + if (request?.filter) { + query = parseFilter(request.filter, query, this.database); + } + for (const row of await query) { + lookup.set(row.entityRef, row.entity ? JSON.parse(row.entity) : null); + } + } + + let items = request.entityRefs.map(ref => lookup.get(ref) ?? null); + + if (request.fields) { + items = items.map(e => e && request.fields!(e)); + } + + return { items }; + } + async removeEntityByUid(uid: string): Promise { // Clear the hashed state of the immediate parents of the deleted entity. // This makes sure that when they get reprocessed, their output is written diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index f383df7364..541359feb6 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -48,6 +48,7 @@ describe('createRouter readonly disabled', () => { beforeAll(async () => { entitiesCatalog = { entities: jest.fn(), + entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), facets: jest.fn(), @@ -257,6 +258,38 @@ describe('createRouter readonly disabled', () => { }); }); + describe('POST /entities/by-refs', () => { + it.each([ + '', + 'not json', + '[', + '[]', + '{}', + '{"unknown":7}', + '{"entityRefs":7}', + '{"entityRefs":[7]}', + ])('properly rejects malformed request body, %p', async p => { + await expect( + request(app) + .post('/entities/by-refs') + .set('Content-Type', 'application/json') + .send(p), + ).resolves.toMatchObject({ status: 400 }); + }); + + it('can fetch entities by refs', async () => { + const entity: Entity = {} as any; + entitiesCatalog.entitiesBatch.mockResolvedValue({ items: [entity] }); + const response = await request(app) + .post('/entities/by-refs') + .set('Content-Type', 'application/json') + .send('{"entityRefs":["a"]}'); + expect(entitiesCatalog.entitiesBatch).toHaveBeenCalledTimes(1); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ items: [entity] }); + }); + }); + describe('GET /locations', () => { it('happy path: lists locations', async () => { const locations: Location[] = [ @@ -517,6 +550,7 @@ describe('createRouter readonly enabled', () => { beforeAll(async () => { entitiesCatalog = { entities: jest.fn(), + entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), facets: jest.fn(), @@ -706,6 +740,7 @@ describe('NextRouter permissioning', () => { beforeAll(async () => { entitiesCatalog = { entities: jest.fn(), + entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), facets: jest.fn(), diff --git a/plugins/catalog-backend/src/service/createRouter.ts b/plugins/catalog-backend/src/service/createRouter.ts index 5267519c33..4680dbb899 100644 --- a/plugins/catalog-backend/src/service/createRouter.ts +++ b/plugins/catalog-backend/src/service/createRouter.ts @@ -28,24 +28,25 @@ import express from 'express'; import Router from 'express-promise-router'; import { Logger } from 'winston'; import yn from 'yn'; +import { z } from 'zod'; import { EntitiesCatalog } from '../catalog/types'; import { LocationAnalyzer } from '../ingestion/types'; +import { CatalogProcessingOrchestrator } from '../processing/types'; +import { validateEntityEnvelope } from '../processing/util'; import { basicEntityFilter, + entitiesBatchRequest, parseEntityFilterParams, parseEntityPaginationParams, parseEntityTransformParams, } from './request'; +import { parseEntityFacetParams } from './request/parseEntityFacetParams'; +import { LocationService, RefreshOptions, RefreshService } from './types'; import { disallowReadonlyMode, locationInput, validateRequestBody, } from './util'; -import { z } from 'zod'; -import { parseEntityFacetParams } from './request/parseEntityFacetParams'; -import { RefreshOptions, LocationService, RefreshService } from './types'; -import { CatalogProcessingOrchestrator } from '../processing/types'; -import { validateEntityEnvelope } from '../processing/util'; /** * Options used by {@link createRouter}. @@ -173,6 +174,16 @@ export async function createRouter( res.status(200).json(response); }, ) + .post('/entities/by-refs', async (req, res) => { + const request = entitiesBatchRequest(req); + const token = getBearerToken(req.header('authorization')); + const response = await entitiesCatalog.entitiesBatch({ + entityRefs: request.entityRefs, + fields: parseEntityTransformParams(req.query), + authorizationToken: token, + }); + res.status(200).json(response); + }) .get('/entity-facets', async (req, res) => { const response = await entitiesCatalog.facets({ filter: parseEntityFilterParams(req.query), diff --git a/plugins/catalog-backend/src/service/request/entitiesBatchRequest.ts b/plugins/catalog-backend/src/service/request/entitiesBatchRequest.ts new file mode 100644 index 0000000000..b3a91f9491 --- /dev/null +++ b/plugins/catalog-backend/src/service/request/entitiesBatchRequest.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { InputError } from '@backstage/errors'; +import { Request } from 'express'; +import { z } from 'zod'; + +const schema = z.object({ + entityRefs: z.array(z.string()), +}); + +export function entitiesBatchRequest(req: Request) { + try { + return schema.parse(req.body); + } catch (error) { + throw new InputError( + `Malformed request body (did you remember to specify an application/json content type?), ${error.message}`, + ); + } +} diff --git a/plugins/catalog-backend/src/service/request/index.ts b/plugins/catalog-backend/src/service/request/index.ts index 4ab579fbb6..0b09355e72 100644 --- a/plugins/catalog-backend/src/service/request/index.ts +++ b/plugins/catalog-backend/src/service/request/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +export { entitiesBatchRequest } from './entitiesBatchRequest'; export { basicEntityFilter } from './basicEntityFilter'; export { parseEntityFilterParams } from './parseEntityFilterParams'; export { parseEntityPaginationParams } from './parseEntityPaginationParams'; diff --git a/plugins/catalog-backend/src/util/conversion.ts b/plugins/catalog-backend/src/util/conversion.ts index 83302722d2..0b92b88d90 100644 --- a/plugins/catalog-backend/src/util/conversion.ts +++ b/plugins/catalog-backend/src/util/conversion.ts @@ -23,7 +23,7 @@ import { stringifyLocationRef, } from '@backstage/catalog-model'; import { createHash } from 'crypto'; -import { LocationSpec } from '@backstage/plugin-catalog-node'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; export function locationSpecToMetadataName(location: LocationSpec) { const hash = createHash('sha1') diff --git a/plugins/catalog-graph/.eslintrc.js b/plugins/catalog-graph/.eslintrc.js index e2a53a6ad2..45bb6db521 100644 --- a/plugins/catalog-graph/.eslintrc.js +++ b/plugins/catalog-graph/.eslintrc.js @@ -1 +1,5 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { + rules: { + 'testing-library/prefer-screen-queries': 'error', + }, +}); diff --git a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx index c5940be395..27b67102e4 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx @@ -28,6 +28,7 @@ import { TestApiProvider, TestApiRegistry, } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { catalogGraphRouteRef } from '../../routes'; @@ -79,15 +80,15 @@ describe('', () => { relations: [], })); - const { findByText, findAllByTestId } = await renderInTestApp(wrapper, { + await renderInTestApp(wrapper, { mountedRoutes: { '/entity/{kind}/{namespace}/{name}': entityRouteRef, '/catalog-graph': catalogGraphRouteRef, }, }); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(1); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(1); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(1); }); @@ -97,7 +98,7 @@ describe('', () => { relations: [], })); - const { findByText } = await renderInTestApp( + await renderInTestApp( @@ -111,7 +112,7 @@ describe('', () => { }, ); - expect(await findByText('Custom Title')).toBeInTheDocument(); + expect(await screen.findByText('Custom Title')).toBeInTheDocument(); }); test('renders link to standalone viewer', async () => { @@ -120,15 +121,15 @@ describe('', () => { relations: [], })); - const { findByText, getByText } = await renderInTestApp(wrapper, { + await renderInTestApp(wrapper, { mountedRoutes: { '/entity/{kind}/{namespace}/{name}': entityRouteRef, '/catalog-graph': catalogGraphRouteRef, }, }); - expect(await findByText('b:d/c')).toBeInTheDocument(); - const button = getByText('View graph'); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + const button = screen.getByText('View graph'); expect(button).toBeInTheDocument(); expect(button.closest('a')).toHaveAttribute( 'href', @@ -137,7 +138,7 @@ describe('', () => { }); test('renders link to standalone viewer with custom config', async () => { - const { findByText, getByText } = await renderInTestApp( + await renderInTestApp( @@ -151,8 +152,8 @@ describe('', () => { }, ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - const button = getByText('View graph'); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + const button = screen.getByText('View graph'); expect(button).toBeInTheDocument(); expect(button.closest('a')).toHaveAttribute( 'href', @@ -167,7 +168,7 @@ describe('', () => { })); const analyticsSpy = new MockAnalyticsApi(); - const { findByText } = await renderInTestApp( + await renderInTestApp( {wrapper} , @@ -179,8 +180,8 @@ describe('', () => { }, ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - await userEvent.click(await findByText('b:d/c')); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + await userEvent.click(await screen.findByText('b:d/c')); expect(analyticsSpy.getEvents()[0]).toMatchObject({ action: 'click', diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx index 9a5d1f833e..f7e1a9fbe7 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx @@ -22,6 +22,7 @@ import { renderInTestApp, TestApiProvider, } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { CatalogGraphPage } from './CatalogGraphPage'; @@ -108,19 +109,16 @@ describe('', () => { n === 'b:d/e' ? entityE : entityC, ); - const { getByText, findByText, findAllByTestId } = await renderInTestApp( - wrapper, - { - mountedRoutes: { - '/entity/{kind}/{namespace}/{name}': entityRouteRef, - }, + await renderInTestApp(wrapper, { + mountedRoutes: { + '/entity/{kind}/{namespace}/{name}': entityRouteRef, }, - ); + }); - expect(getByText('Catalog Graph')).toBeInTheDocument(); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findByText('b:d/e')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(2); + expect(screen.getByText('Catalog Graph')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findByText('b:d/e')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(2); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(2); }); @@ -129,17 +127,17 @@ describe('', () => { n === 'b:d/e' ? entityE : entityC, ); - const { getByText, queryByText } = await renderInTestApp(wrapper, { + await renderInTestApp(wrapper, { mountedRoutes: { '/entity/{kind}/{namespace}/{name}': entityRouteRef, }, }); - expect(queryByText('Max Depth')).toBeNull(); + expect(screen.queryByText('Max Depth')).toBeNull(); - await userEvent.click(getByText('Filters')); + await userEvent.click(screen.getByText('Filters')); - expect(getByText('Max Depth')).toBeInTheDocument(); + expect(screen.getByText('Max Depth')).toBeInTheDocument(); }); test('should select other entity', async () => { @@ -147,20 +145,17 @@ describe('', () => { n === 'b:d/e' ? entityE : entityC, ); - const { getByText, findByText, findAllByTestId } = await renderInTestApp( - wrapper, - { - mountedRoutes: { - '/entity/{kind}/{namespace}/{name}': entityRouteRef, - }, + await renderInTestApp(wrapper, { + mountedRoutes: { + '/entity/{kind}/{namespace}/{name}': entityRouteRef, }, - ); + }); - expect(await findAllByTestId('node')).toHaveLength(2); + expect(await screen.findAllByTestId('node')).toHaveLength(2); - await userEvent.click(getByText('b:d/e')); + await userEvent.click(screen.getByText('b:d/e')); - expect(await findByText('hasPart')).toBeInTheDocument(); + expect(await screen.findByText('hasPart')).toBeInTheDocument(); }); test('should navigate to entity', async () => { @@ -168,17 +163,17 @@ describe('', () => { n === 'b:d/e' ? entityE : entityC, ); - const { getByText, findAllByTestId } = await renderInTestApp(wrapper, { + await renderInTestApp(wrapper, { mountedRoutes: { '/entity/{kind}/{namespace}/{name}': entityRouteRef, }, }); - expect(await findAllByTestId('node')).toHaveLength(2); + expect(await screen.findAllByTestId('node')).toHaveLength(2); const user = userEvent.setup(); await user.keyboard('{Shift>}'); - await user.click(getByText('b:d/e')); + await user.click(screen.getByText('b:d/e')); expect(navigate).toHaveBeenCalledWith('/entity/{kind}/{namespace}/{name}'); }); @@ -188,7 +183,7 @@ describe('', () => { ); const analyticsSpy = new MockAnalyticsApi(); - const { getByText, findAllByTestId } = await renderInTestApp( + await renderInTestApp( {wrapper} , @@ -199,12 +194,12 @@ describe('', () => { }, ); - expect(await findAllByTestId('node')).toHaveLength(2); + expect(await screen.findAllByTestId('node')).toHaveLength(2); // We wait a bit here to reliably reproduce an issue where that requires the `baseVal` and `view` mocks await new Promise(r => setTimeout(r, 100)); - await userEvent.click(getByText('b:d/e')); + await userEvent.click(screen.getByText('b:d/e')); expect(analyticsSpy.getEvents()[0]).toMatchObject({ action: 'click', @@ -218,7 +213,7 @@ describe('', () => { ); const analyticsSpy = new MockAnalyticsApi(); - const { getByText, findAllByTestId } = await renderInTestApp( + await renderInTestApp( {wrapper} , @@ -229,11 +224,11 @@ describe('', () => { }, ); - expect(await findAllByTestId('node')).toHaveLength(2); + expect(await screen.findAllByTestId('node')).toHaveLength(2); const user = userEvent.setup(); await user.keyboard('{Shift>}'); - await user.click(getByText('b:d/e')); + await user.click(screen.getByText('b:d/e')); expect(analyticsSpy.getEvents()[0]).toMatchObject({ action: 'click', diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx index 0be0fca66b..85d9d97c7f 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { render, waitFor } from '@testing-library/react'; + +import { render, waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { CurveFilter } from './CurveFilter'; @@ -21,26 +22,22 @@ import { CurveFilter } from './CurveFilter'; describe('', () => { test('should display current curve label', () => { const onChange = jest.fn(); - const { getByText } = render( - , - ); + render(); - expect(getByText('Monotone X')).toBeInTheDocument(); + expect(screen.getByText('Monotone X')).toBeInTheDocument(); }); test('should select an alternative curve factory', async () => { const onChange = jest.fn(); - const { getByText, getByTestId } = render( - , - ); + render(); - expect(getByText('Step Before')).toBeInTheDocument(); + expect(screen.getByText('Step Before')).toBeInTheDocument(); - await userEvent.click(getByTestId('select')); - await userEvent.click(getByText('Monotone X')); + await userEvent.click(screen.getByTestId('select')); + await userEvent.click(screen.getByText('Monotone X')); await waitFor(() => { - expect(getByText('Monotone X')).toBeInTheDocument(); + expect(screen.getByText('Monotone X')).toBeInTheDocument(); expect(onChange).toHaveBeenCalledWith('curveMonotoneX'); }); }); diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/DirectionFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/DirectionFilter.test.tsx index 05c071f1f9..cb660b5a25 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/DirectionFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/DirectionFilter.test.tsx @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { render, waitFor } from '@testing-library/react'; + +import { render, waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { Direction } from '../EntityRelationsGraph'; @@ -21,26 +22,26 @@ import { DirectionFilter } from './DirectionFilter'; describe('', () => { test('should display current value', () => { - const { getByText } = render( + render( {}} />, ); - expect(getByText('Left to right')).toBeInTheDocument(); + expect(screen.getByText('Left to right')).toBeInTheDocument(); }); test('should select direction', async () => { const onChange = jest.fn(); - const { getByText, getByTestId } = render( + render( , ); - expect(getByText('Right to left')).toBeInTheDocument(); + expect(screen.getByText('Right to left')).toBeInTheDocument(); - await userEvent.click(getByTestId('select')); - await userEvent.click(getByText('Top to bottom')); + await userEvent.click(screen.getByTestId('select')); + await userEvent.click(screen.getByText('Top to bottom')); await waitFor(() => { - expect(getByText('Top to bottom')).toBeInTheDocument(); + expect(screen.getByText('Top to bottom')).toBeInTheDocument(); expect(onChange).toHaveBeenCalledWith(Direction.TOP_BOTTOM); }); }); diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.test.tsx index 3f3a7f82a6..89f3169981 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.test.tsx @@ -13,64 +13,65 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { render, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; + +import { render, screen } from '@testing-library/react'; +import user from '@testing-library/user-event'; import React from 'react'; import { MaxDepthFilter } from './MaxDepthFilter'; describe('', () => { test('should display current value', () => { - const { getByLabelText } = render( - {}} />, - ); + render( {}} />); - expect(getByLabelText('maxp')).toBeInTheDocument(); - expect(getByLabelText('maxp')).toHaveValue(5); + expect(screen.getByLabelText('maxp')).toBeInTheDocument(); + expect(screen.getByLabelText('maxp')).toHaveValue(5); }); test('should display infinite if non finite', () => { - const { getByPlaceholderText, getByLabelText } = render( + render( {}} />, ); - expect(getByPlaceholderText(/Infinite/)).toBeInTheDocument(); - expect(getByLabelText('maxp')).toHaveValue(null); + expect(screen.getByPlaceholderText(/Infinite/)).toBeInTheDocument(); + expect(screen.getByLabelText('maxp')).toHaveValue(null); }); test('should clear max depth', async () => { const onChange = jest.fn(); - const { getByLabelText } = render( - , - ); + render(); - await userEvent.click(getByLabelText('clear max depth')); + expect(onChange).not.toHaveBeenCalled(); + await user.click(screen.getByLabelText('clear max depth')); expect(onChange).toHaveBeenCalledWith(Number.POSITIVE_INFINITY); }); test('should set max depth to undefined if below one', async () => { const onChange = jest.fn(); - const { getByLabelText } = render( - , - ); + render(); - await userEvent.clear(getByLabelText('maxp')); - await userEvent.type(getByLabelText('maxp'), '0'); + await user.clear(screen.getByLabelText('maxp')); + await user.type(screen.getByLabelText('maxp'), '0'); expect(onChange).toHaveBeenCalledWith(Number.POSITIVE_INFINITY); }); test('should select direction', async () => { - const onChange = jest.fn(); - const { getByLabelText } = render( - , + let value = 5; + render( + { + value = v; + }} + />, ); - expect(getByLabelText('maxp')).toHaveValue(5); + expect(screen.getByLabelText('maxp')).toHaveValue(5); + expect(value).toBe(5); - await userEvent.clear(getByLabelText('maxp')); - await userEvent.type(getByLabelText('maxp'), '10'); - waitFor(() => { - expect(onChange).toHaveBeenCalledWith(10); - }); + await user.clear(screen.getByLabelText('maxp')); + expect(value).toBe(Number.POSITIVE_INFINITY); + await user.type(screen.getByLabelText('maxp'), '10'); + expect(value).toBe(10); }); }); diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.tsx index b6b03d8831..e6fb2fbab4 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.tsx @@ -23,7 +23,7 @@ import { Typography, } from '@material-ui/core'; import ClearIcon from '@material-ui/icons/Clear'; -import React, { useCallback } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; export type Props = { value: number; @@ -42,18 +42,37 @@ const useStyles = makeStyles( export const MaxDepthFilter = ({ value, onChange }: Props) => { const classes = useStyles(); + const onChangeRef = useRef(onChange); + const [currentValue, setCurrentValue] = useState(value); + // Keep a fresh reference to the latest callback + useEffect(() => { + onChangeRef.current = onChange; + }, [onChange]); + + // If the value changes externally, update ourselves + useEffect(() => { + setCurrentValue(value); + }, [value]); + + // When the entered text changes, update ourselves and communicate externally const handleChange = useCallback( (event: React.ChangeEvent) => { - const v = Number(event.target.value); - onChange(v <= 0 ? Number.POSITIVE_INFINITY : v); + const newValueNumeric = Number(event.target.value); + const newValue = + Number.isFinite(newValueNumeric) && newValueNumeric > 0 + ? newValueNumeric + : Number.POSITIVE_INFINITY; + setCurrentValue(newValue); + onChangeRef.current(newValue); }, - [onChange], + [], ); const reset = useCallback(() => { - onChange(Number.POSITIVE_INFINITY); - }, [onChange]); + setCurrentValue(Number.POSITIVE_INFINITY); + onChangeRef.current(Number.POSITIVE_INFINITY); + }, [onChangeRef]); return ( @@ -62,7 +81,7 @@ export const MaxDepthFilter = ({ value, onChange }: Props) => { diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.test.tsx index 580c118b27..511592af0c 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.test.tsx @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { GetEntityFacetsResponse } from '@backstage/catalog-client'; import { ApiProvider } from '@backstage/core-app-api'; import { AlertApi, alertApiRef } from '@backstage/core-plugin-api'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { SelectedKindsFilter } from './SelectedKindsFilter'; @@ -42,37 +43,37 @@ const apis = TestApiRegistry.from( describe('', () => { it('should not explode while loading', async () => { - const rendered = await renderWithEffects( + const { baseElement } = await renderWithEffects( {}} /> , ); - expect(rendered.baseElement).toBeInTheDocument(); + expect(baseElement).toBeInTheDocument(); }); it('should render current value', async () => { - const rendered = await renderWithEffects( + await renderWithEffects( {}} /> , ); - expect(rendered.getByText('API')).toBeInTheDocument(); - expect(rendered.getByText('Component')).toBeInTheDocument(); + expect(screen.getByText('API')).toBeInTheDocument(); + expect(screen.getByText('Component')).toBeInTheDocument(); }); it('should select value', async () => { const onChange = jest.fn(); - const { getByLabelText, getByText } = await renderWithEffects( + await renderWithEffects( , ); - await userEvent.click(getByLabelText('Open')); - await waitFor(() => expect(getByText('System')).toBeInTheDocument()); + await userEvent.click(screen.getByLabelText('Open')); + await waitFor(() => expect(screen.getByText('System')).toBeInTheDocument()); - await userEvent.click(getByText('System')); + await userEvent.click(screen.getByText('System')); await waitFor(() => { expect(onChange).toHaveBeenCalledWith(['api', 'component', 'system']); @@ -81,7 +82,7 @@ describe('', () => { it('should return undefined if all values are selected', async () => { const onChange = jest.fn(); - const { getByLabelText, getByText } = await renderWithEffects( + await renderWithEffects( ', () => { /> , ); - await userEvent.click(getByLabelText('Open')); + await userEvent.click(screen.getByLabelText('Open')); - await waitFor(() => expect(getByText('Resource')).toBeInTheDocument()); + await waitFor(() => + expect(screen.getByText('Resource')).toBeInTheDocument(), + ); - await userEvent.click(getByText('Resource')); + await userEvent.click(screen.getByText('Resource')); await waitFor(() => { expect(onChange).toHaveBeenCalledWith(undefined); @@ -102,13 +105,13 @@ describe('', () => { it('should return all values when cleared', async () => { const onChange = jest.fn(); - const { getByRole } = await renderWithEffects( + await renderWithEffects( , ); - await userEvent.click(getByRole('combobox')); + await userEvent.click(screen.getByRole('combobox')); await userEvent.tab(); await waitFor(() => { diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedRelationsFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedRelationsFilter.test.tsx index bcc67edc3d..8baa7865b9 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedRelationsFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedRelationsFilter.test.tsx @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { RELATION_CHILD_OF, RELATION_HAS_MEMBER, RELATION_OWNED_BY, } from '@backstage/catalog-model'; -import { render, waitFor } from '@testing-library/react'; +import { render, waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { ALL_RELATION_PAIRS } from '../EntityRelationsGraph'; @@ -26,7 +27,7 @@ import { SelectedRelationsFilter } from './SelectedRelationsFilter'; describe('', () => { test('should render current value', () => { - const { getByText } = render( + render( ', () => { />, ); - expect(getByText(RELATION_OWNED_BY)).toBeInTheDocument(); - expect(getByText(RELATION_CHILD_OF)).toBeInTheDocument(); + expect(screen.getByText(RELATION_OWNED_BY)).toBeInTheDocument(); + expect(screen.getByText(RELATION_CHILD_OF)).toBeInTheDocument(); }); test('should select value', async () => { const onChange = jest.fn(); - const { getByText, getByLabelText } = render( + render( ', () => { />, ); - await userEvent.click(getByLabelText('Open')); + await userEvent.click(screen.getByLabelText('Open')); await waitFor(() => - expect(getByText(RELATION_HAS_MEMBER)).toBeInTheDocument(), + expect(screen.getByText(RELATION_HAS_MEMBER)).toBeInTheDocument(), ); - await userEvent.click(getByText(RELATION_HAS_MEMBER)); + await userEvent.click(screen.getByText(RELATION_HAS_MEMBER)); await waitFor(() => { expect(onChange).toHaveBeenCalledWith([ @@ -67,7 +68,7 @@ describe('', () => { test('should return undefined if all values are selected', async () => { const onChange = jest.fn(); - const { getByText, getByLabelText } = render( + render( p).filter( @@ -77,13 +78,13 @@ describe('', () => { />, ); - await userEvent.click(getByLabelText('Open')); + await userEvent.click(screen.getByLabelText('Open')); await waitFor(() => - expect(getByText(RELATION_HAS_MEMBER)).toBeInTheDocument(), + expect(screen.getByText(RELATION_HAS_MEMBER)).toBeInTheDocument(), ); - await userEvent.click(getByText(RELATION_HAS_MEMBER)); + await userEvent.click(screen.getByText(RELATION_HAS_MEMBER)); await waitFor(() => { expect(onChange).toHaveBeenCalledWith(undefined); @@ -92,7 +93,7 @@ describe('', () => { test('should return all values when cleared', async () => { const onChange = jest.fn(); - const { getByRole } = render( + render( ', () => { />, ); - await userEvent.click(getByRole('combobox')); + await userEvent.click(screen.getByRole('combobox')); await userEvent.tab(); await waitFor(() => { diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/SwitchFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/SwitchFilter.test.tsx index 08c7304989..322232d774 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/SwitchFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/SwitchFilter.test.tsx @@ -13,31 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { render } from '@testing-library/react'; + +import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { SwitchFilter } from './SwitchFilter'; describe('', () => { test('should render value', () => { - const { getByLabelText } = render( - {}} />, - ); + render( {}} />); - expect(getByLabelText('My label')).toBeInTheDocument(); - expect(getByLabelText('My label')).not.toBeChecked(); + expect(screen.getByLabelText('My label')).toBeInTheDocument(); + expect(screen.getByLabelText('My label')).not.toBeChecked(); }); test('should toggle value', async () => { const onChange = jest.fn(); - const { getByLabelText } = render( - , - ); + render(); - expect(getByLabelText('My label')).toBeInTheDocument(); - expect(getByLabelText('My label')).toBeChecked(); + expect(screen.getByLabelText('My label')).toBeInTheDocument(); + expect(screen.getByLabelText('My label')).toBeChecked(); - await userEvent.click(getByLabelText('My label')); + await userEvent.click(screen.getByLabelText('My label')); expect(onChange).toHaveBeenCalledWith(false); }); diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx index 5b8f50b983..ab280aea81 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx @@ -13,17 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { RELATION_CHILD_OF, RELATION_PARENT_OF, } from '@backstage/catalog-model'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import React from 'react'; import { CustomLabel } from './CustomLabel'; describe('', () => { test('renders label', () => { - const { getByText } = render( + render( ', () => { , ); - expect(getByText(RELATION_PARENT_OF)).toBeInTheDocument(); + expect(screen.getByText(RELATION_PARENT_OF)).toBeInTheDocument(); }); test('renders label with multiple relations', () => { - const { getByText } = render( + render( ', () => { , ); - expect(getByText(RELATION_PARENT_OF)).toBeInTheDocument(); - expect(getByText(RELATION_CHILD_OF)).toBeInTheDocument(); + expect(screen.getByText(RELATION_PARENT_OF)).toBeInTheDocument(); + expect(screen.getByText(RELATION_CHILD_OF)).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx index ff9fc9377e..7a34bbf688 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx @@ -13,14 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { CustomNode } from './CustomNode'; import userEvent from '@testing-library/user-event'; describe('', () => { test('renders node', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(getByText('kind:namespace/name')).toBeInTheDocument(); + expect(screen.getByText('kind:namespace/name')).toBeInTheDocument(); }); test('renders node, skips default namespace', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(getByText('kind:name')).toBeInTheDocument(); + expect(screen.getByText('kind:name')).toBeInTheDocument(); }); test('renders node with onClick', async () => { const onClick = jest.fn(); - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(getByText('kind:namespace/name')).toBeInTheDocument(); - await userEvent.click(getByText('kind:namespace/name')); + expect(screen.getByText('kind:namespace/name')).toBeInTheDocument(); + await userEvent.click(screen.getByText('kind:namespace/name')); expect(onClick).toHaveBeenCalledTimes(1); }); test('renders title if entity has one', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(getByText('Custom Title')).toBeInTheDocument(); + expect(screen.getByText('Custom Title')).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityKindIcon.test.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityKindIcon.test.tsx index 5d5f9dbf3c..6592c6195e 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityKindIcon.test.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityKindIcon.test.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { renderInTestApp } from '@backstage/test-utils'; import React from 'react'; import { EntityKindIcon } from './EntityKindIcon'; diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.test.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.test.tsx index d7b9f9e3eb..8a3352abb4 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.test.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.test.tsx @@ -24,6 +24,7 @@ import { import { DependencyGraphTypes } from '@backstage/core-components'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React, { FunctionComponent } from 'react'; import { EntityRelationsGraph } from './EntityRelationsGraph'; @@ -142,7 +143,7 @@ describe('', () => { relations: [], }); - const { findByText, findAllByTestId } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(1); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(1); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(1); }); test('renders a progress indicator while loading', async () => { catalog.getEntityByRef.mockImplementation(() => new Promise(() => {})); - const { findByRole } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(await findByRole('progressbar')).toBeInTheDocument(); + expect(await screen.findByRole('progressbar')).toBeInTheDocument(); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(1); }); @@ -197,7 +198,7 @@ describe('', () => { return undefined; }); - const { findByText, findAllByTestId } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(1); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(1); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(2); }); test('renders at max depth of one', async () => { catalog.getEntityByRef.mockImplementation(async n => entities[n as string]); - const { findByText, findAllByTestId, findAllByText } = - await renderInTestApp( - - - , - ); + await renderInTestApp( + + + , + ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findByText('b:d/c1')).toBeInTheDocument(); - expect(await findByText('k:d/a1')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(3); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c1')).toBeInTheDocument(); + expect(await screen.findByText('k:d/a1')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(3); - expect(await findAllByText('ownerOf')).toHaveLength(1); - expect(await findAllByText('hasPart')).toHaveLength(1); - expect(await findAllByTestId('label')).toHaveLength(2); + expect(await screen.findAllByText('ownerOf')).toHaveLength(1); + expect(await screen.findAllByText('hasPart')).toHaveLength(1); + expect(await screen.findAllByTestId('label')).toHaveLength(2); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(3); }); @@ -238,26 +238,25 @@ describe('', () => { test('renders simplified graph at full depth', async () => { catalog.getEntityByRef.mockImplementation(async n => entities[n as string]); - const { findByText, findAllByText, findAllByTestId } = - await renderInTestApp( - - - , - ); + await renderInTestApp( + + + , + ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findByText('b:d/c1')).toBeInTheDocument(); - expect(await findByText('k:d/a1')).toBeInTheDocument(); - expect(await findByText('b:d/c2')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(4); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c1')).toBeInTheDocument(); + expect(await screen.findByText('k:d/a1')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c2')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(4); - expect(await findAllByText('ownerOf')).toHaveLength(1); - expect(await findAllByText('hasPart')).toHaveLength(2); - expect(await findAllByTestId('label')).toHaveLength(3); + expect(await screen.findAllByText('ownerOf')).toHaveLength(1); + expect(await screen.findAllByText('hasPart')).toHaveLength(2); + expect(await screen.findAllByTestId('label')).toHaveLength(3); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(4); }); @@ -265,28 +264,27 @@ describe('', () => { test('renders full graph at full depth', async () => { catalog.getEntityByRef.mockImplementation(async n => entities[n as string]); - const { findAllByText, findByText, findAllByTestId } = - await renderInTestApp( - - - , - ); + await renderInTestApp( + + + , + ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findByText('b:d/c1')).toBeInTheDocument(); - expect(await findByText('k:d/a1')).toBeInTheDocument(); - expect(await findByText('b:d/c2')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(4); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c1')).toBeInTheDocument(); + expect(await screen.findByText('k:d/a1')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c2')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(4); - expect(await findAllByText('ownerOf')).toHaveLength(2); - expect(await findAllByText('ownedBy')).toHaveLength(2); - expect(await findAllByText('hasPart')).toHaveLength(2); - expect(await findAllByText('partOf')).toHaveLength(2); - expect(await findAllByTestId('label')).toHaveLength(8); + expect(await screen.findAllByText('ownerOf')).toHaveLength(2); + expect(await screen.findAllByText('ownedBy')).toHaveLength(2); + expect(await screen.findAllByText('hasPart')).toHaveLength(2); + expect(await screen.findAllByText('partOf')).toHaveLength(2); + expect(await screen.findAllByTestId('label')).toHaveLength(8); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(4); }); @@ -294,26 +292,25 @@ describe('', () => { test('renders full graph at full depth with merged relations', async () => { catalog.getEntityByRef.mockImplementation(async n => entities[n as string]); - const { findAllByText, findByText, findAllByTestId } = - await renderInTestApp( - - - , - ); + await renderInTestApp( + + + , + ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findByText('b:d/c1')).toBeInTheDocument(); - expect(await findByText('k:d/a1')).toBeInTheDocument(); - expect(await findByText('b:d/c2')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(4); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c1')).toBeInTheDocument(); + expect(await screen.findByText('k:d/a1')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c2')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(4); - expect(await findAllByText('ownerOf')).toHaveLength(2); - expect(await findAllByText('hasPart')).toHaveLength(2); - expect(await findAllByTestId('label')).toHaveLength(4); + expect(await screen.findAllByText('ownerOf')).toHaveLength(2); + expect(await screen.findAllByText('hasPart')).toHaveLength(2); + expect(await screen.findAllByTestId('label')).toHaveLength(4); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(4); }); @@ -321,27 +318,26 @@ describe('', () => { test('renders a graph with multiple root nodes', async () => { catalog.getEntityByRef.mockImplementation(async n => entities[n as string]); - const { findAllByText, findByText, findAllByTestId } = - await renderInTestApp( - - - , - ); + await renderInTestApp( + + + , + ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findByText('b:d/c1')).toBeInTheDocument(); - expect(await findByText('k:d/a1')).toBeInTheDocument(); - expect(await findByText('b:d/c2')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(4); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c1')).toBeInTheDocument(); + expect(await screen.findByText('k:d/a1')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c2')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(4); - expect(await findAllByText('ownerOf')).toHaveLength(1); - expect(await findAllByText('partOf')).toHaveLength(2); - expect(await findAllByTestId('label')).toHaveLength(3); + expect(await screen.findAllByText('ownerOf')).toHaveLength(1); + expect(await screen.findAllByText('partOf')).toHaveLength(2); + expect(await screen.findAllByTestId('label')).toHaveLength(3); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(4); }); @@ -349,23 +345,22 @@ describe('', () => { test('renders a graph with filtered kinds and relations', async () => { catalog.getEntityByRef.mockImplementation(async n => entities[n as string]); - const { findAllByText, findByText, findAllByTestId } = - await renderInTestApp( - - - , - ); + await renderInTestApp( + + + , + ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findByText('k:d/a1')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(2); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findByText('k:d/a1')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(2); - expect(await findAllByText('ownerOf')).toHaveLength(1); - expect(await findAllByTestId('label')).toHaveLength(1); + expect(await screen.findAllByText('ownerOf')).toHaveLength(1); + expect(await screen.findAllByTestId('label')).toHaveLength(1); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(2); }); @@ -374,7 +369,7 @@ describe('', () => { catalog.getEntityByRef.mockImplementation(async n => entities[n as string]); const onNodeClick = jest.fn(); - const { findByText } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - await userEvent.click(await findByText('k:d/a1')); + await userEvent.click(await screen.findByText('k:d/a1')); expect(onNodeClick).toHaveBeenCalledTimes(1); }); @@ -397,7 +392,7 @@ describe('', () => { ); - const { findAllByTestId, container } = await renderInTestApp( + const { container } = await renderInTestApp( ', () => { , ); - const node = await findAllByTestId(CUSTOM_TEST_ID); + const node = await screen.findAllByTestId(CUSTOM_TEST_ID); expect(node[0]).toBeInTheDocument(); expect(container.querySelector('circle')).toBeInTheDocument(); }); @@ -421,7 +416,7 @@ describe('', () => { ); - const { findAllByTestId, findAllByText, container } = await renderInTestApp( + const { container } = await renderInTestApp( ', () => { /> , ); - const node = await findAllByTestId(CUSTOM_TEST_ID); + const node = await screen.findAllByTestId(CUSTOM_TEST_ID); expect(node[0]).toBeInTheDocument(); expect(container.querySelector('circle')).toBeInTheDocument(); - const labels = await findAllByText('Test-Labelvisible'); + const labels = await screen.findAllByText('Test-Labelvisible'); expect(labels[0]).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-import/.eslintrc.js b/plugins/catalog-import/.eslintrc.js index e2a53a6ad2..45bb6db521 100644 --- a/plugins/catalog-import/.eslintrc.js +++ b/plugins/catalog-import/.eslintrc.js @@ -1 +1,5 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { + rules: { + 'testing-library/prefer-screen-queries': 'error', + }, +}); diff --git a/plugins/catalog-import/src/components/DefaultImportPage/DefaultImportPage.test.tsx b/plugins/catalog-import/src/components/DefaultImportPage/DefaultImportPage.test.tsx index d6132b249e..a231563484 100644 --- a/plugins/catalog-import/src/components/DefaultImportPage/DefaultImportPage.test.tsx +++ b/plugins/catalog-import/src/components/DefaultImportPage/DefaultImportPage.test.tsx @@ -19,6 +19,7 @@ import { ApiProvider, ConfigReader } from '@backstage/core-app-api'; import { configApiRef } from '@backstage/core-plugin-api'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { catalogImportApiRef, CatalogImportClient } from '../../api'; import { DefaultImportPage } from './DefaultImportPage'; @@ -65,14 +66,14 @@ describe('', () => { }); it('renders without exploding', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( , ); expect( - getByText('Start tracking your component in Backstage'), + screen.getByText('Start tracking your component in Backstage'), ).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.test.tsx b/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.test.tsx index 9a27532c57..c33de77ffd 100644 --- a/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.test.tsx +++ b/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.test.tsx @@ -21,6 +21,7 @@ import { TestApiProvider, TestApiRegistry, } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { CatalogImportApi, catalogImportApiRef } from '../../api'; import { ImportInfoCard } from './ImportInfoCard'; @@ -49,7 +50,7 @@ describe('', () => { }); it('renders without exploding', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(getByText('Register an existing component')).toBeInTheDocument(); + expect( + screen.getByText('Register an existing component'), + ).toBeInTheDocument(); }); it('renders section on GitHub discovery if supported', async () => { catalogImportApi.preparePullRequest = async () => ({ title: '', body: '' }); - const { getByText } = await renderInTestApp( + await renderInTestApp( , ); - expect(getByText(/The wizard discovers all/)).toBeInTheDocument(); + expect(screen.getByText(/The wizard discovers all/)).toBeInTheDocument(); }); it('renders section on pull requests if supported', async () => { catalogImportApi.preparePullRequest = async () => ({ title: '', body: '' }); - const { getByText } = await renderInTestApp( + await renderInTestApp( , ); expect( - getByText(/the wizard will prepare a Pull Request/), + screen.getByText(/the wizard will prepare a Pull Request/), ).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-import/src/components/ImportPage/ImportPage.test.tsx b/plugins/catalog-import/src/components/ImportPage/ImportPage.test.tsx index 498e786eb3..a38b801607 100644 --- a/plugins/catalog-import/src/components/ImportPage/ImportPage.test.tsx +++ b/plugins/catalog-import/src/components/ImportPage/ImportPage.test.tsx @@ -19,6 +19,7 @@ import { ApiProvider, ConfigReader } from '@backstage/core-app-api'; import { configApiRef } from '@backstage/core-plugin-api'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { useOutlet } from 'react-router'; import { catalogImportApiRef, CatalogImportClient } from '../../api'; @@ -71,26 +72,26 @@ describe('', () => { afterEach(() => jest.resetAllMocks()); it('renders without exploding', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( , ); expect( - getByText('Start tracking your component in Backstage'), + screen.getByText('Start tracking your component in Backstage'), ).toBeInTheDocument(); }); it('renders with custom children', async () => { (useOutlet as jest.Mock).mockReturnValue(
Hello World
); - const { getByText } = await renderInTestApp( + await renderInTestApp( , ); - expect(getByText('Hello World')).toBeInTheDocument(); + expect(screen.getByText('Hello World')).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.test.tsx b/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.test.tsx index 5a8110eb6c..a9d662cab0 100644 --- a/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.test.tsx +++ b/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.test.tsx @@ -16,7 +16,7 @@ import { errorApiRef } from '@backstage/core-plugin-api'; import { TestApiProvider } from '@backstage/test-utils'; -import { act, render } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { AnalyzeResult, catalogImportApiRef } from '../../api/'; @@ -60,19 +60,20 @@ describe('', () => { }); it('renders without exploding', async () => { - const { getByRole } = render( - undefined} />, - { - wrapper: Wrapper, - }, - ); + render( undefined} />, { + wrapper: Wrapper, + }); - expect(getByRole('textbox', { name: /Repository/i })).toBeInTheDocument(); - expect(getByRole('textbox', { name: /Repository/i })).toHaveValue(''); + expect( + screen.getByRole('textbox', { name: /Repository/i }), + ).toBeInTheDocument(); + expect(screen.getByRole('textbox', { name: /Repository/i })).toHaveValue( + '', + ); }); it('should use default analysis url', async () => { - const { getByRole } = render( + render( undefined} analysisUrl="https://default" @@ -82,8 +83,10 @@ describe('', () => { }, ); - expect(getByRole('textbox', { name: /Repository/i })).toBeInTheDocument(); - expect(getByRole('textbox', { name: /Repository/i })).toHaveValue( + expect( + screen.getByRole('textbox', { name: /Repository/i }), + ).toBeInTheDocument(); + expect(screen.getByRole('textbox', { name: /Repository/i })).toHaveValue( 'https://default', ); }); @@ -91,16 +94,13 @@ describe('', () => { it('should not analyze without url', async () => { const onAnalysisFn = jest.fn(); - const { getByRole } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); await act(async () => { try { - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); } catch { return; } @@ -114,26 +114,23 @@ describe('', () => { it('should not analyze invalid value', async () => { const onAnalysisFn = jest.fn(); - const { getByRole, getByText } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'http:/', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(catalogImportApi.analyzeUrl).toHaveBeenCalledTimes(0); expect(onAnalysisFn).toHaveBeenCalledTimes(0); expect(errorApi.post).toHaveBeenCalledTimes(0); expect( - getByText('Must start with http:// or https://.'), + screen.getByText('Must start with http:// or https://.'), ).toBeInTheDocument(); }); @@ -145,12 +142,9 @@ describe('', () => { locations: [location], } as AnalyzeResult; - const { getByRole } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); catalogImportApi.analyzeUrl.mockReturnValueOnce( Promise.resolve(analyzeResult), @@ -158,10 +152,10 @@ describe('', () => { await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'https://my-repository', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(onAnalysisFn).toHaveBeenCalledTimes(1); @@ -182,12 +176,9 @@ describe('', () => { locations: [location, location], } as AnalyzeResult; - const { getByRole } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); catalogImportApi.analyzeUrl.mockReturnValueOnce( Promise.resolve(analyzeResult), @@ -195,10 +186,10 @@ describe('', () => { await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'https://my-repository-1', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(onAnalysisFn).toHaveBeenCalledTimes(1); @@ -218,12 +209,9 @@ describe('', () => { locations: [], } as AnalyzeResult; - const { getByRole, getByText } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); catalogImportApi.analyzeUrl.mockReturnValueOnce( Promise.resolve(analyzeResult), @@ -231,15 +219,15 @@ describe('', () => { await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'https://my-repository-1', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(onAnalysisFn).toHaveBeenCalledTimes(0); expect( - getByText('There are no entities at this location'), + screen.getByText('There are no entities at this location'), ).toBeInTheDocument(); expect(errorApi.post).toHaveBeenCalledTimes(0); }); @@ -262,12 +250,9 @@ describe('', () => { ], } as AnalyzeResult; - const { getByRole } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); catalogImportApi.analyzeUrl.mockReturnValueOnce( Promise.resolve(analyzeResult), @@ -275,10 +260,10 @@ describe('', () => { await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'https://my-repository-2', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(onAnalysisFn).toHaveBeenCalledTimes(1); @@ -300,12 +285,9 @@ describe('', () => { generatedEntities: [], } as AnalyzeResult; - const { getByRole, getByText } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); catalogImportApi.analyzeUrl.mockReturnValueOnce( Promise.resolve(analyzeResult), @@ -313,15 +295,15 @@ describe('', () => { await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'https://my-repository-2', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(onAnalysisFn).toHaveBeenCalledTimes(0); expect( - getByText("Couldn't generate entities for your repository"), + screen.getByText("Couldn't generate entities for your repository"), ).toBeInTheDocument(); expect(errorApi.post).toHaveBeenCalledTimes(0); }); @@ -344,7 +326,7 @@ describe('', () => { ], } as AnalyzeResult; - const { getByRole, getByText } = render( + render( , { wrapper: Wrapper, @@ -357,15 +339,15 @@ describe('', () => { await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'https://my-repository-2', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(onAnalysisFn).toHaveBeenCalledTimes(0); expect( - getByText("Couldn't generate entities for your repository"), + screen.getByText("Couldn't generate entities for your repository"), ).toBeInTheDocument(); expect(errorApi.post).toHaveBeenCalledTimes(0); }); @@ -373,12 +355,9 @@ describe('', () => { it('should report unknown type to the errorapi', async () => { const onAnalysisFn = jest.fn(); - const { getByRole, getByText } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); catalogImportApi.analyzeUrl.mockReturnValueOnce( Promise.resolve({ type: 'unknown' } as any as AnalyzeResult), @@ -386,15 +365,15 @@ describe('', () => { await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'https://my-repository-2', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(onAnalysisFn).toHaveBeenCalledTimes(0); expect( - getByText( + screen.getByText( 'Received unknown analysis result of type unknown. Please contact the support team.', ), ).toBeInTheDocument(); diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx index 3acb7390a9..02b67090fa 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx @@ -15,7 +15,7 @@ */ import { FormHelperText, TextField } from '@material-ui/core'; -import { act, render } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { asInputRef } from '../helpers'; @@ -25,7 +25,7 @@ describe('', () => { it('renders without exploding', async () => { const onSubmitFn = jest.fn(); - const { getByRole } = render( + render( defaultValues={{ main: 'default' }} render={({ register }) => ( @@ -39,7 +39,7 @@ describe('', () => { ); await act(async () => { - await userEvent.click(getByRole('button', { name: /submit/i })); + await userEvent.click(screen.getByRole('button', { name: /submit/i })); }); expect(onSubmitFn).toHaveBeenCalledTimes(1); @@ -49,7 +49,7 @@ describe('', () => { it('should register a text field', async () => { const onSubmitFn = jest.fn(); - const { getByRole, getByLabelText } = render( + render( defaultValues={{ main: 'default' }} render={({ register }) => ( @@ -67,9 +67,9 @@ describe('', () => { ); await act(async () => { - await userEvent.clear(getByLabelText('Main Field')); - await userEvent.type(getByLabelText('Main Field'), 'My Text'); - await userEvent.click(getByRole('button', { name: /submit/i })); + await userEvent.clear(screen.getByLabelText('Main Field')); + await userEvent.type(screen.getByLabelText('Main Field'), 'My Text'); + await userEvent.click(screen.getByRole('button', { name: /submit/i })); }); expect(onSubmitFn).toHaveBeenCalledTimes(1); @@ -79,7 +79,7 @@ describe('', () => { it('registers required attribute', async () => { const onSubmitFn = jest.fn(); - const { queryByText, getByRole } = render( + render( defaultValues={{}} render={({ formState, register }) => ( @@ -100,13 +100,17 @@ describe('', () => { />, ); - expect(queryByText('Error in required main field')).not.toBeInTheDocument(); + expect( + screen.queryByText('Error in required main field'), + ).not.toBeInTheDocument(); await act(async () => { - await userEvent.click(getByRole('button', { name: /submit/i })); + await userEvent.click(screen.getByRole('button', { name: /submit/i })); }); expect(onSubmitFn).not.toHaveBeenCalled(); - expect(queryByText('Error in required main field')).toBeInTheDocument(); + expect( + screen.queryByText('Error in required main field'), + ).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewPullRequestComponent.test.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewPullRequestComponent.test.tsx index 8d0d085b8a..d983b6d359 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewPullRequestComponent.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewPullRequestComponent.test.tsx @@ -15,7 +15,7 @@ */ import { makeStyles } from '@material-ui/core'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import { renderHook } from '@testing-library/react-hooks'; import React from 'react'; import { PreviewPullRequestComponent } from './PreviewPullRequestComponent'; @@ -28,15 +28,15 @@ const useStyles = makeStyles({ describe('', () => { it('renders without exploding', async () => { - const { getByText } = render( + render( , ); - const title = getByText('My Title'); - const description = getByText('description', { selector: 'strong' }); + const title = screen.getByText('My Title'); + const description = screen.getByText('description', { selector: 'strong' }); expect(title).toBeInTheDocument(); expect(title).toBeVisible(); expect(description).toBeInTheDocument(); @@ -46,7 +46,7 @@ describe('', () => { it('renders card with custom styles', async () => { const { result } = renderHook(() => useStyles()); - const { getByText } = render( + render( ', () => { />, ); - const title = getByText('My Title'); - const description = getByText('description', { selector: 'strong' }); + const title = screen.getByText('My Title'); + const description = screen.getByText('description', { selector: 'strong' }); expect(title).toBeInTheDocument(); expect(title).not.toBeVisible(); expect(description).toBeInTheDocument(); @@ -65,7 +65,7 @@ describe('', () => { it('renders with custom styles', async () => { const { result } = renderHook(() => useStyles()); - const { getByText } = render( + render( ', () => { />, ); - const title = getByText('My Title'); - const description = getByText('description', { selector: 'strong' }); + const title = screen.getByText('My Title'); + const description = screen.getByText('description', { selector: 'strong' }); expect(title).toBeInTheDocument(); expect(title).toBeVisible(); expect(description).toBeInTheDocument(); diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.test.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.test.tsx index ae6225f912..36f1217c37 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.test.tsx @@ -102,7 +102,7 @@ describe('', () => { catalogApi.getEntities.mockReturnValue(Promise.resolve({ items: [] })); await act(async () => { - const { findByText } = render( + render( ', () => { }, ); - const title = await findByText('My title'); - const description = await findByText('body', { selector: 'strong' }); + const title = await screen.findByText('My title'); + const description = await screen.findByText('body', { + selector: 'strong', + }); expect(title).toBeInTheDocument(); expect(title).toBeVisible(); expect(description).toBeInTheDocument(); diff --git a/plugins/catalog-import/src/components/StepPrepareSelectLocations/StepPrepareSelectLocations.test.tsx b/plugins/catalog-import/src/components/StepPrepareSelectLocations/StepPrepareSelectLocations.test.tsx index 1817ece1b8..5a30c7735a 100644 --- a/plugins/catalog-import/src/components/StepPrepareSelectLocations/StepPrepareSelectLocations.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareSelectLocations/StepPrepareSelectLocations.test.tsx @@ -15,7 +15,7 @@ */ import { renderInTestApp } from '@backstage/test-utils'; -import { act } from '@testing-library/react'; +import { act, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { AnalyzeResult } from '../../api'; @@ -58,7 +58,7 @@ describe('', () => { }); it('renders display locations to be added', async () => { - const rendered = await renderInTestApp( + await renderInTestApp( undefined} @@ -66,15 +66,15 @@ describe('', () => { />, ); - expect(rendered.getByText('url-1')).toBeInTheDocument(); - expect(rendered.getByText('url-2')).toBeInTheDocument(); + expect(screen.getByText('url-1')).toBeInTheDocument(); + expect(screen.getByText('url-2')).toBeInTheDocument(); expect( - rendered.queryByText(/Select one or more locations/), + screen.queryByText(/Select one or more locations/), ).toBeInTheDocument(); expect( - rendered.queryByText(/locations already exist/), + screen.queryByText(/locations already exist/), ).not.toBeInTheDocument(); - expect(rendered.getByRole('button', { name: /Review/i })).toBeDisabled(); + expect(screen.getByRole('button', { name: /Review/i })).toBeDisabled(); }); it('should display existing locations only', async () => { @@ -95,7 +95,7 @@ describe('', () => { ], } as Extract; - const rendered = await renderInTestApp( + await renderInTestApp( undefined} @@ -103,15 +103,15 @@ describe('', () => { />, ); - expect(rendered.getByText(/my-target/)).toBeInTheDocument(); - expect(rendered.queryByText(/locations already exist/)).toBeInTheDocument(); + expect(screen.getByText(/my-target/)).toBeInTheDocument(); + expect(screen.queryByText(/locations already exist/)).toBeInTheDocument(); expect( - rendered.queryByText(/Select one or more locations/), + screen.queryByText(/Select one or more locations/), ).not.toBeInTheDocument(); }); it('should select and deselect all', async () => { - const { getByRole, getAllByRole } = await renderInTestApp( + await renderInTestApp( undefined} @@ -119,27 +119,31 @@ describe('', () => { />, ); - const checkboxes = getAllByRole('checkbox'); + const checkboxes = screen.getAllByRole('checkbox'); checkboxes.forEach(c => expect(c).not.toBeChecked()); - expect(getByRole('button', { name: /Review/i })).toBeDisabled(); + expect(screen.getByRole('button', { name: /Review/i })).toBeDisabled(); await act(async () => { - await userEvent.click(getByRole('button', { name: /Select All/i })); + await userEvent.click( + screen.getByRole('button', { name: /Select All/i }), + ); }); checkboxes.forEach(c => expect(c).toBeChecked()); - expect(getByRole('button', { name: /Review/i })).not.toBeDisabled(); + expect(screen.getByRole('button', { name: /Review/i })).not.toBeDisabled(); await act(async () => { - await userEvent.click(getByRole('button', { name: /Select All/i })); + await userEvent.click( + screen.getByRole('button', { name: /Select All/i }), + ); }); checkboxes.forEach(c => expect(c).not.toBeChecked()); - expect(getByRole('button', { name: /Review/i })).toBeDisabled(); + expect(screen.getByRole('button', { name: /Review/i })).toBeDisabled(); }); it('should preselect prepared locations', async () => { - const { getAllByRole } = await renderInTestApp( + await renderInTestApp( ', () => { />, ); - const checkboxes = getAllByRole('checkbox'); + const checkboxes = screen.getAllByRole('checkbox'); expect(checkboxes[0]).not.toBeChecked(); expect(checkboxes[1]).toBeChecked(); @@ -159,7 +163,7 @@ describe('', () => { }); it('should select items', async () => { - const { getAllByRole } = await renderInTestApp( + await renderInTestApp( undefined} @@ -167,7 +171,7 @@ describe('', () => { />, ); - const checkboxes = getAllByRole('checkbox'); + const checkboxes = screen.getAllByRole('checkbox'); checkboxes.forEach(c => expect(c).not.toBeChecked()); await act(async () => { @@ -188,7 +192,7 @@ describe('', () => { it('should go back', async () => { const onGoBack = jest.fn(); - const { getByRole } = await renderInTestApp( + await renderInTestApp( undefined} @@ -197,7 +201,7 @@ describe('', () => { ); await act(async () => { - await userEvent.click(getByRole('button', { name: /Back/i })); + await userEvent.click(screen.getByRole('button', { name: /Back/i })); }); expect(onGoBack).toHaveBeenCalledTimes(1); @@ -206,7 +210,7 @@ describe('', () => { it('should submit', async () => { const onPrepare = jest.fn(); - const { getAllByRole, getByRole } = await renderInTestApp( + await renderInTestApp( ', () => { />, ); - const checkboxes = getAllByRole('checkbox'); + const checkboxes = screen.getAllByRole('checkbox'); await act(async () => { await userEvent.click(checkboxes[1]); }); await act(async () => { - await userEvent.click(getByRole('button', { name: /Review/i })); + await userEvent.click(screen.getByRole('button', { name: /Review/i })); }); expect(onPrepare).toHaveBeenCalledTimes(1); diff --git a/plugins/catalog-react/.eslintrc.js b/plugins/catalog-react/.eslintrc.js index e2a53a6ad2..45bb6db521 100644 --- a/plugins/catalog-react/.eslintrc.js +++ b/plugins/catalog-react/.eslintrc.js @@ -1 +1,5 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { + rules: { + 'testing-library/prefer-screen-queries': 'error', + }, +}); diff --git a/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx b/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx index 65ad3f227a..8a6b7206c6 100644 --- a/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx @@ -19,7 +19,7 @@ import { Entity } from '@backstage/catalog-model'; import { ApiProvider } from '@backstage/core-app-api'; import { alertApiRef } from '@backstage/core-plugin-api'; import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils'; -import { fireEvent, waitFor } from '@testing-library/react'; +import { fireEvent, waitFor, screen } from '@testing-library/react'; import { capitalize } from 'lodash'; import { default as React } from 'react'; import { catalogApiRef } from '../../api'; @@ -75,7 +75,7 @@ describe('', () => { ); it('renders available entity kinds', async () => { - const rendered = await renderWithEffects( + await renderWithEffects( ', () => { , ); - expect(rendered.getByText('Kind')).toBeInTheDocument(); + expect(screen.getByText('Kind')).toBeInTheDocument(); - const input = rendered.getByTestId('select'); + const input = screen.getByTestId('select'); fireEvent.click(input); - await waitFor(() => rendered.getByText('Domain')); + await waitFor(() => screen.getByText('Domain')); entities.forEach(entity => { expect( - rendered.getByRole('option', { + screen.getByRole('option', { name: capitalize(entity.kind as string), }), ).toBeInTheDocument(); @@ -102,7 +102,7 @@ describe('', () => { it('sets the selected kind filter', async () => { const updateFilters = jest.fn(); - const rendered = await renderWithEffects( + await renderWithEffects( ', () => { , ); - const input = rendered.getByTestId('select'); + const input = screen.getByTestId('select'); fireEvent.click(input); - await waitFor(() => rendered.getByText('Domain')); - fireEvent.click(rendered.getByText('Domain')); + await waitFor(() => screen.getByText('Domain')); + fireEvent.click(screen.getByText('Domain')); expect(updateFilters).toHaveBeenLastCalledWith({ kind: new EntityKindFilter('domain'), diff --git a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx index 47e85292e2..f925625b23 100644 --- a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx @@ -15,7 +15,7 @@ */ import { Entity } from '@backstage/catalog-model'; -import { fireEvent, render } from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; import { MockEntityListContextProvider } from '../../testUtils/providers'; import { EntityLifecycleFilter } from '../../filters'; @@ -56,36 +56,36 @@ const sampleEntities: Entity[] = [ describe('', () => { it('renders all lifecycles', () => { - const rendered = render( + render( , ); - expect(rendered.getByText('Lifecycle')).toBeInTheDocument(); + expect(screen.getByText('Lifecycle')).toBeInTheDocument(); - fireEvent.click(rendered.getByTestId('lifecycle-picker-expand')); + fireEvent.click(screen.getByTestId('lifecycle-picker-expand')); sampleEntities .map(e => e.spec?.lifecycle!) .forEach(lifecycle => { - expect(rendered.getByText(lifecycle as string)).toBeInTheDocument(); + expect(screen.getByText(lifecycle as string)).toBeInTheDocument(); }); }); it('renders unique lifecycles in alphabetical order', () => { - const rendered = render( + render( , ); - expect(rendered.getByText('Lifecycle')).toBeInTheDocument(); + expect(screen.getByText('Lifecycle')).toBeInTheDocument(); - fireEvent.click(rendered.getByTestId('lifecycle-picker-expand')); + fireEvent.click(screen.getByTestId('lifecycle-picker-expand')); - expect(rendered.getAllByRole('option').map(o => o.textContent)).toEqual([ + expect(screen.getAllByRole('option').map(o => o.textContent)).toEqual([ 'experimental', 'production', ]); @@ -114,7 +114,7 @@ describe('', () => { it('adds lifecycles to filters', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { lifecycles: undefined, }); - fireEvent.click(rendered.getByTestId('lifecycle-picker-expand')); - fireEvent.click(rendered.getByText('production')); + fireEvent.click(screen.getByTestId('lifecycle-picker-expand')); + fireEvent.click(screen.getByText('production')); expect(updateFilters).toHaveBeenLastCalledWith({ lifecycles: new EntityLifecycleFilter(['production']), }); @@ -138,7 +138,7 @@ describe('', () => { it('removes lifecycles from filters', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { expect(updateFilters).toHaveBeenLastCalledWith({ lifecycles: new EntityLifecycleFilter(['production']), }); - fireEvent.click(rendered.getByTestId('lifecycle-picker-expand')); - expect(rendered.getByLabelText('production')).toBeChecked(); + fireEvent.click(screen.getByTestId('lifecycle-picker-expand')); + expect(screen.getByLabelText('production')).toBeChecked(); - fireEvent.click(rendered.getByLabelText('production')); + fireEvent.click(screen.getByLabelText('production')); expect(updateFilters).toHaveBeenLastCalledWith({ lifecycles: undefined, }); diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.test.tsx b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.test.tsx index f648b51456..c816810b10 100644 --- a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.test.tsx @@ -15,7 +15,7 @@ */ import { Entity, parseEntityRef } from '@backstage/catalog-model'; -import { fireEvent, render } from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; import { MockEntityListContextProvider } from '../../testUtils/providers'; import { EntityOwnerFilter } from '../../filters'; @@ -69,36 +69,36 @@ const sampleEntities: Entity[] = [ describe('', () => { it('renders all owners', () => { - const rendered = render( + render( , ); - expect(rendered.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner')).toBeInTheDocument(); - fireEvent.click(rendered.getByTestId('owner-picker-expand')); + fireEvent.click(screen.getByTestId('owner-picker-expand')); sampleEntities .flatMap(e => e.relations?.map(r => parseEntityRef(r.targetRef).name)) .forEach(owner => { - expect(rendered.getByText(owner as string)).toBeInTheDocument(); + expect(screen.getByText(owner as string)).toBeInTheDocument(); }); }); it('renders unique owners in alphabetical order', () => { - const rendered = render( + render( , ); - expect(rendered.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner')).toBeInTheDocument(); - fireEvent.click(rendered.getByTestId('owner-picker-expand')); + fireEvent.click(screen.getByTestId('owner-picker-expand')); - expect(rendered.getAllByRole('option').map(o => o.textContent)).toEqual([ + expect(screen.getAllByRole('option').map(o => o.textContent)).toEqual([ 'another-owner', 'some-owner', 'some-owner-2', @@ -128,7 +128,7 @@ describe('', () => { it('adds owners to filters', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { owners: undefined, }); - fireEvent.click(rendered.getByTestId('owner-picker-expand')); - fireEvent.click(rendered.getByText('some-owner')); + fireEvent.click(screen.getByTestId('owner-picker-expand')); + fireEvent.click(screen.getByText('some-owner')); expect(updateFilters).toHaveBeenLastCalledWith({ owners: new EntityOwnerFilter(['some-owner']), }); @@ -152,7 +152,7 @@ describe('', () => { it('removes owners from filters', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { expect(updateFilters).toHaveBeenLastCalledWith({ owners: new EntityOwnerFilter(['some-owner']), }); - fireEvent.click(rendered.getByTestId('owner-picker-expand')); - expect(rendered.getByLabelText('some-owner')).toBeChecked(); + fireEvent.click(screen.getByTestId('owner-picker-expand')); + expect(screen.getByLabelText('some-owner')).toBeChecked(); - fireEvent.click(rendered.getByLabelText('some-owner')); + fireEvent.click(screen.getByLabelText('some-owner')); expect(updateFilters).toHaveBeenLastCalledWith({ owner: undefined, }); diff --git a/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.test.tsx b/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.test.tsx index 8e90e04148..00d36e5e13 100644 --- a/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.test.tsx @@ -15,7 +15,7 @@ */ import { Entity } from '@backstage/catalog-model'; -import { fireEvent, render } from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; import { EntityErrorFilter, EntityOrphanFilter } from '../../filters'; import { MockEntityListContextProvider } from '../../testUtils/providers'; @@ -52,23 +52,23 @@ const sampleEntities: Entity[] = [ describe('', () => { it('renders all processing status options', () => { - const rendered = render( + render( , ); - expect(rendered.getByText('Processing Status')).toBeInTheDocument(); + expect(screen.getByText('Processing Status')).toBeInTheDocument(); - fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); - expect(rendered.getByText('Is Orphan')).toBeInTheDocument(); - expect(rendered.getByText('Has Error')).toBeInTheDocument(); + fireEvent.click(screen.getByTestId('processing-status-picker-expand')); + expect(screen.getByText('Is Orphan')).toBeInTheDocument(); + expect(screen.getByText('Has Error')).toBeInTheDocument(); }); it('adds orphan to orphan filter', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { , ); - fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); - fireEvent.click(rendered.getByText('Is Orphan')); + fireEvent.click(screen.getByTestId('processing-status-picker-expand')); + fireEvent.click(screen.getByText('Is Orphan')); expect(updateFilters).toHaveBeenCalledWith({ orphan: new EntityOrphanFilter(true), }); @@ -89,7 +89,7 @@ describe('', () => { it('adds error to error filter', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { , ); - fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); - fireEvent.click(rendered.getByText('Has Error')); + fireEvent.click(screen.getByTestId('processing-status-picker-expand')); + fireEvent.click(screen.getByText('Has Error')); expect(updateFilters).toHaveBeenCalledWith({ error: new EntityErrorFilter(true), }); @@ -110,7 +110,7 @@ describe('', () => { it('remove orphan from orphan filter', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { , ); - fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); - fireEvent.click(rendered.getByText('Is Orphan')); + fireEvent.click(screen.getByTestId('processing-status-picker-expand')); + fireEvent.click(screen.getByText('Is Orphan')); expect(updateFilters).toHaveBeenCalledWith({ orphan: undefined, }); @@ -131,7 +131,7 @@ describe('', () => { it('remove error from error filter', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { , ); - fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); - fireEvent.click(rendered.getByText('Has Error')); + fireEvent.click(screen.getByTestId('processing-status-picker-expand')); + fireEvent.click(screen.getByText('Has Error')); expect(updateFilters).toHaveBeenCalledWith({ error: undefined, }); diff --git a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.test.tsx b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.test.tsx index eaaaab39bc..b0a6b46bf5 100644 --- a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.test.tsx +++ b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.test.tsx @@ -15,6 +15,7 @@ */ import { renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { entityRouteRef } from '../../routes'; import { EntityRefLink } from './EntityRefLink'; @@ -33,16 +34,13 @@ describe('', () => { lifecycle: 'production', }, }; - const { getByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, - ); + }); - expect(getByText('component:software')).toHaveAttribute( + expect(screen.getByText('component:software')).toHaveAttribute( 'href', '/catalog/default/component/software', ); @@ -62,15 +60,12 @@ describe('', () => { lifecycle: 'production', }, }; - const { getByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, - ); - expect(getByText('component:test/software')).toHaveAttribute( + }); + expect(screen.getByText('component:test/software')).toHaveAttribute( 'href', '/catalog/test/component/software', ); @@ -90,7 +85,7 @@ describe('', () => { lifecycle: 'production', }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( , { mountedRoutes: { @@ -98,7 +93,7 @@ describe('', () => { }, }, ); - expect(getByText('test/software')).toHaveAttribute( + expect(screen.getByText('test/software')).toHaveAttribute( 'href', '/catalog/test/component/software', ); @@ -110,15 +105,12 @@ describe('', () => { namespace: 'default', name: 'software', }; - const { getByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, - ); - expect(getByText('component:software')).toHaveAttribute( + }); + expect(screen.getByText('component:software')).toHaveAttribute( 'href', '/catalog/default/component/software', ); @@ -130,15 +122,12 @@ describe('', () => { namespace: 'test', name: 'software', }; - const { getByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, - ); - expect(getByText('component:test/software')).toHaveAttribute( + }); + expect(screen.getByText('component:test/software')).toHaveAttribute( 'href', '/catalog/test/component/software', ); @@ -150,7 +139,7 @@ describe('', () => { namespace: 'test', name: 'software', }; - const { getByText } = await renderInTestApp( + await renderInTestApp( , { mountedRoutes: { @@ -158,7 +147,7 @@ describe('', () => { }, }, ); - expect(getByText('test/software')).toHaveAttribute( + expect(screen.getByText('test/software')).toHaveAttribute( 'href', '/catalog/test/component/software', ); @@ -170,7 +159,7 @@ describe('', () => { namespace: 'test', name: 'software', }; - const { getByText } = await renderInTestApp( + await renderInTestApp( Custom Children , @@ -180,7 +169,7 @@ describe('', () => { }, }, ); - expect(getByText('Custom Children')).toHaveAttribute( + expect(screen.getByText('Custom Children')).toHaveAttribute( 'href', '/catalog/test/component/software', ); diff --git a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.test.tsx b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.test.tsx index 147e139749..4e0d52ef4e 100644 --- a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.test.tsx +++ b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.test.tsx @@ -15,6 +15,7 @@ */ import { renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { entityRouteRef } from '../../routes'; import { EntityRefLinks } from './EntityRefLinks'; @@ -28,15 +29,12 @@ describe('', () => { name: 'software', }, ]; - const { getByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, - ); - expect(getByText('component:software')).toHaveAttribute( + }); + expect(screen.getByText('component:software')).toHaveAttribute( 'href', '/catalog/default/component/software', ); @@ -55,20 +53,17 @@ describe('', () => { name: 'interface', }, ]; - const { getByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, - ); - expect(getByText(',')).toBeInTheDocument(); - expect(getByText('component:software')).toHaveAttribute( + }); + expect(screen.getByText(',')).toBeInTheDocument(); + expect(screen.getByText('component:software')).toHaveAttribute( 'href', '/catalog/default/component/software', ); - expect(getByText('api:interface')).toHaveAttribute( + expect(screen.getByText('api:interface')).toHaveAttribute( 'href', '/catalog/default/api/interface', ); diff --git a/plugins/catalog-react/src/components/EntityRefLink/FetchedEntityRefLinks.test.tsx b/plugins/catalog-react/src/components/EntityRefLink/FetchedEntityRefLinks.test.tsx index 36e2c0450d..9db6511662 100644 --- a/plugins/catalog-react/src/components/EntityRefLink/FetchedEntityRefLinks.test.tsx +++ b/plugins/catalog-react/src/components/EntityRefLink/FetchedEntityRefLinks.test.tsx @@ -16,6 +16,7 @@ import { FetchedEntityRefLinks } from './FetchedEntityRefLinks'; import { entityRouteRef } from '../../routes'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import { Entity } from '@backstage/catalog-model'; import React from 'react'; import { JsonObject } from '@backstage/types'; @@ -60,7 +61,7 @@ describe('', () => { }), }; - const rendered = await renderInTestApp( + await renderInTestApp( , @@ -71,12 +72,12 @@ describe('', () => { }, ); - expect(rendered.getByText('SOFTWARE')).toHaveAttribute( + expect(screen.getByText('SOFTWARE')).toHaveAttribute( 'href', '/catalog/default/component/software', ); - expect(rendered.getByText('INTERFACE')).toHaveAttribute( + expect(screen.getByText('INTERFACE')).toHaveAttribute( 'href', '/catalog/default/api/interface', ); @@ -111,7 +112,7 @@ describe('', () => { const catalogApi: Partial = {}; - const rendered = await renderInTestApp( + await renderInTestApp( , @@ -122,12 +123,12 @@ describe('', () => { }, ); - expect(rendered.getByText('TOOL')).toHaveAttribute( + expect(screen.getByText('TOOL')).toHaveAttribute( 'href', '/catalog/default/component/tool', ); - expect(rendered.getByText('IMPLEMENTATION')).toHaveAttribute( + expect(screen.getByText('IMPLEMENTATION')).toHaveAttribute( 'href', '/catalog/default/api/implementation', ); @@ -189,7 +190,7 @@ describe('', () => { }), }; - const rendered = await renderInTestApp( + await renderInTestApp( , @@ -200,17 +201,17 @@ describe('', () => { }, ); - expect(rendered.getByText('TOOL')).toHaveAttribute( + expect(screen.getByText('TOOL')).toHaveAttribute( 'href', '/catalog/default/component/tool', ); - expect(rendered.getByText('IMPLEMENTATION')).toHaveAttribute( + expect(screen.getByText('IMPLEMENTATION')).toHaveAttribute( 'href', '/catalog/default/api/implementation', ); - expect(rendered.getByText('INTERFACE')).toHaveAttribute( + expect(screen.getByText('INTERFACE')).toHaveAttribute( 'href', '/catalog/default/component/interface', ); diff --git a/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.test.tsx b/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.test.tsx index 0b9ea9cb98..94db377e26 100644 --- a/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.test.tsx +++ b/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.test.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { fireEvent, render, waitFor } from '@testing-library/react'; +import { fireEvent, render, waitFor, screen } from '@testing-library/react'; import { EntitySearchBar } from './EntitySearchBar'; import { DefaultEntityFilters } from '../../hooks/useEntityListProvider'; import { EntityTextFilter } from '../../filters'; @@ -29,13 +29,13 @@ describe('EntitySearchBar', () => { text: new EntityTextFilter('hello'), }; - const { getByDisplayValue } = render( + render( , ); - const searchInput = getByDisplayValue('hello'); + const searchInput = screen.getByDisplayValue('hello'); expect(searchInput).toBeInTheDocument(); fireEvent.change(searchInput, { target: { value: 'world' } }); diff --git a/plugins/catalog-react/src/components/EntityTable/EntityTable.test.tsx b/plugins/catalog-react/src/components/EntityTable/EntityTable.test.tsx index 99f998118f..d5f1dc20d3 100644 --- a/plugins/catalog-react/src/components/EntityTable/EntityTable.test.tsx +++ b/plugins/catalog-react/src/components/EntityTable/EntityTable.test.tsx @@ -16,13 +16,13 @@ import { Entity } from '@backstage/catalog-model'; import { renderInTestApp } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { EntityTable } from './EntityTable'; describe('', () => { it('shows empty table', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { />, ); - expect(getByText('Entities')).toBeInTheDocument(); - expect(getByText('EMPTY')).toBeInTheDocument(); + expect(screen.getByText('Entities')).toBeInTheDocument(); + expect(screen.getByText('EMPTY')).toBeInTheDocument(); }); it('shows entities', async () => { @@ -47,7 +47,7 @@ describe('', () => { }, ]; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { ); await waitFor(() => { - expect(getByText('my-entity')).toBeInTheDocument(); + expect(screen.getByText('my-entity')).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog-react/src/components/EntityTable/presets.test.tsx b/plugins/catalog-react/src/components/EntityTable/presets.test.tsx index cca9c7d494..82075df5e9 100644 --- a/plugins/catalog-react/src/components/EntityTable/presets.test.tsx +++ b/plugins/catalog-react/src/components/EntityTable/presets.test.tsx @@ -21,7 +21,7 @@ import { SystemEntity, } from '@backstage/catalog-model'; import { renderInTestApp } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { entityRouteRef } from '../../routes'; import { EntityTable } from './EntityTable'; @@ -54,7 +54,7 @@ describe('systemEntityColumns', () => { }, ]; - const { getByText } = await renderInTestApp( + await renderInTestApp( { ); await waitFor(() => { - expect(getByText('my-namespace/my-system')).toBeInTheDocument(); - expect(getByText('my-namespace/my-domain')).toBeInTheDocument(); - expect(getByText('test')).toBeInTheDocument(); - expect(getByText(/Some/)).toBeInTheDocument(); + expect(screen.getByText('my-namespace/my-system')).toBeInTheDocument(); + expect(screen.getByText('my-namespace/my-domain')).toBeInTheDocument(); + expect(screen.getByText('test')).toBeInTheDocument(); + expect(screen.getByText(/Some/)).toBeInTheDocument(); }); }); }); @@ -106,7 +106,7 @@ describe('componentEntityColumns', () => { }, ]; - const { getByText } = await renderInTestApp( + await renderInTestApp( { ); await waitFor(() => { - expect(getByText('my-namespace/my-component')).toBeInTheDocument(); - expect(getByText('my-namespace/my-system')).toBeInTheDocument(); - expect(getByText('test')).toBeInTheDocument(); - expect(getByText('production')).toBeInTheDocument(); - expect(getByText('service')).toBeInTheDocument(); - expect(getByText(/Some/)).toBeInTheDocument(); + expect(screen.getByText('my-namespace/my-component')).toBeInTheDocument(); + expect(screen.getByText('my-namespace/my-system')).toBeInTheDocument(); + expect(screen.getByText('test')).toBeInTheDocument(); + expect(screen.getByText('production')).toBeInTheDocument(); + expect(screen.getByText('service')).toBeInTheDocument(); + expect(screen.getByText(/Some/)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.test.tsx b/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.test.tsx index 78bdde1994..7da357d806 100644 --- a/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { fireEvent, render, waitFor } from '@testing-library/react'; +import { fireEvent, render, waitFor, screen } from '@testing-library/react'; import React from 'react'; import { MockEntityListContextProvider } from '../../testUtils/providers'; import { EntityTagFilter } from '../../filters'; @@ -35,34 +35,34 @@ describe('', () => { } as unknown as CatalogApi; it('renders all tags', async () => { - const rendered = render( + render( , ); - await waitFor(() => expect(rendered.getByText('Tags')).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText('Tags')).toBeInTheDocument()); - fireEvent.click(rendered.getByTestId('tag-picker-expand')); + fireEvent.click(screen.getByTestId('tag-picker-expand')); tags.forEach(tag => { - expect(rendered.getByText(tag)).toBeInTheDocument(); + expect(screen.getByText(tag)).toBeInTheDocument(); }); }); it('renders unique tags in alphabetical order', async () => { - const rendered = render( + render( , ); - await waitFor(() => expect(rendered.getByText('Tags')).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText('Tags')).toBeInTheDocument()); - fireEvent.click(rendered.getByTestId('tag-picker-expand')); + fireEvent.click(screen.getByTestId('tag-picker-expand')); - expect(rendered.getAllByRole('option').map(o => o.textContent)).toEqual([ + expect(screen.getAllByRole('option').map(o => o.textContent)).toEqual([ 'tag1', 'tag2', 'tag3', @@ -71,18 +71,18 @@ describe('', () => { }); it('renders tags with counts', async () => { - const rendered = render( + render( , ); - await waitFor(() => expect(rendered.getByText('Tags')).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText('Tags')).toBeInTheDocument()); - fireEvent.click(rendered.getByTestId('tag-picker-expand')); + fireEvent.click(screen.getByTestId('tag-picker-expand')); - expect(rendered.getAllByRole('option').map(o => o.textContent)).toEqual([ + expect(screen.getAllByRole('option').map(o => o.textContent)).toEqual([ 'tag1 (0)', 'tag2 (1)', 'tag3 (2)', @@ -115,7 +115,7 @@ describe('', () => { it('adds tags to filters', async () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { }), ); - fireEvent.click(rendered.getByTestId('tag-picker-expand')); - fireEvent.click(rendered.getByText('tag1')); + fireEvent.click(screen.getByTestId('tag-picker-expand')); + fireEvent.click(screen.getByText('tag1')); expect(updateFilters).toHaveBeenLastCalledWith({ tags: new EntityTagFilter(['tag1']), }); @@ -141,7 +141,7 @@ describe('', () => { it('removes tags from filters', async () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { tags: new EntityTagFilter(['tag1']), }), ); - fireEvent.click(rendered.getByTestId('tag-picker-expand')); - expect(rendered.getByLabelText('tag1')).toBeChecked(); + fireEvent.click(screen.getByTestId('tag-picker-expand')); + expect(screen.getByLabelText('tag1')).toBeChecked(); - fireEvent.click(rendered.getByLabelText('tag1')); + fireEvent.click(screen.getByLabelText('tag1')); expect(updateFilters).toHaveBeenLastCalledWith({ tags: undefined, }); diff --git a/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx b/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx index 525c757dea..d61a6c480f 100644 --- a/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx @@ -15,13 +15,12 @@ */ import React from 'react'; -import { fireEvent, waitFor } from '@testing-library/react'; +import { fireEvent, waitFor, screen } from '@testing-library/react'; import { Entity } from '@backstage/catalog-model'; import { EntityTypePicker } from './EntityTypePicker'; import { MockEntityListContextProvider } from '../../testUtils/providers'; import { catalogApiRef } from '../../api'; import { EntityKindFilter, EntityTypeFilter } from '../../filters'; - import { alertApiRef } from '@backstage/core-plugin-api'; import { ApiProvider } from '@backstage/core-app-api'; import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils'; @@ -84,7 +83,7 @@ const apis = TestApiRegistry.from( describe('', () => { it('renders available entity types', async () => { - const rendered = await renderWithEffects( + await renderWithEffects( ', () => { , ); - expect(rendered.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); - const input = rendered.getByTestId('select'); + const input = screen.getByTestId('select'); fireEvent.click(input); - await waitFor(() => rendered.getByText('service')); + await waitFor(() => screen.getByText('service')); entities.forEach(entity => { - expect( - rendered.getByText(entity.spec!.type as string), - ).toBeInTheDocument(); + expect(screen.getByText(entity.spec!.type as string)).toBeInTheDocument(); }); }); it('sets the selected type filter', async () => { const updateFilters = jest.fn(); - const rendered = await renderWithEffects( + await renderWithEffects( ', () => { , ); - const input = rendered.getByTestId('select'); + const input = screen.getByTestId('select'); fireEvent.click(input); - await waitFor(() => rendered.getByText('service')); - fireEvent.click(rendered.getByText('service')); + await waitFor(() => screen.getByText('service')); + fireEvent.click(screen.getByText('service')); expect(updateFilters).toHaveBeenLastCalledWith({ type: new EntityTypeFilter(['service']), }); fireEvent.click(input); - fireEvent.click(rendered.getByText('all')); + fireEvent.click(screen.getByText('all')); expect(updateFilters).toHaveBeenLastCalledWith({ type: undefined }); }); diff --git a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx index 3db2fab0fb..d5309a383d 100644 --- a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx +++ b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { fireEvent, render, waitFor } from '@testing-library/react'; +import { fireEvent, render, waitFor, screen } from '@testing-library/react'; import { Entity, RELATION_OWNED_BY, @@ -27,7 +27,6 @@ import { EntityTagFilter, UserListFilter } from '../../filters'; import { CatalogApi } from '@backstage/catalog-client'; import { catalogApiRef } from '../../api'; import { MockStorageApi, TestApiRegistry } from '@backstage/test-utils'; - import { ApiProvider } from '@backstage/core-app-api'; import { ConfigApi, @@ -144,7 +143,7 @@ const backendEntities: Entity[] = [ describe('', () => { it('renders filter groups', () => { - const { queryByText } = render( + render( @@ -152,12 +151,12 @@ describe('', () => { , ); - expect(queryByText('Personal')).toBeInTheDocument(); - expect(queryByText('Test Company')).toBeInTheDocument(); + expect(screen.queryByText('Personal')).toBeInTheDocument(); + expect(screen.queryByText('Test Company')).toBeInTheDocument(); }); it('renders filters', () => { - const { getAllByRole } = render( + render( @@ -166,12 +165,12 @@ describe('', () => { ); expect( - getAllByRole('menuitem').map(({ textContent }) => textContent), + screen.getAllByRole('menuitem').map(({ textContent }) => textContent), ).toEqual(['Owned 1', 'Starred 1', 'All 4']); }); it('includes counts alongside each filter', async () => { - const { getAllByRole } = render( + render( @@ -183,13 +182,13 @@ describe('', () => { // menuitem itself, so we pick off the next sibling. await waitFor(() => { expect( - getAllByRole('menuitem').map(({ textContent }) => textContent), + screen.getAllByRole('menuitem').map(({ textContent }) => textContent), ).toEqual(['Owned 1', 'Starred 1', 'All 4']); }); }); it('respects other frontend filters in counts', async () => { - const { getAllByRole } = render( + render( ', () => { await waitFor(() => { expect( - getAllByRole('menuitem').map(({ textContent }) => textContent), + screen.getAllByRole('menuitem').map(({ textContent }) => textContent), ).toEqual(['Owned 1', 'Starred 0', 'All 2']); }); }); @@ -229,7 +228,7 @@ describe('', () => { it('updates user filter when a menuitem is selected', () => { const updateFilters = jest.fn(); - const { getByText } = render( + render( ', () => { , ); - fireEvent.click(getByText('Starred')); + fireEvent.click(screen.getByText('Starred')); expect(updateFilters).toHaveBeenLastCalledWith({ user: new UserListFilter( diff --git a/plugins/catalog/.eslintrc.js b/plugins/catalog/.eslintrc.js index e2a53a6ad2..45bb6db521 100644 --- a/plugins/catalog/.eslintrc.js +++ b/plugins/catalog/.eslintrc.js @@ -1 +1,5 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { + rules: { + 'testing-library/prefer-screen-queries': 'error', + }, +}); diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 1528737aac..244aab8626 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -49,6 +49,7 @@ "@material-ui/lab": "4.0.0-alpha.57", "history": "^5.0.0", "lodash": "^4.17.21", + "pluralize": "^8.0.0", "react-helmet": "6.1.0", "react-use": "^17.2.4", "zen-observable": "^0.9.0" diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx index c77787f717..869d6f1757 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx @@ -28,6 +28,7 @@ import { } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import userEvent from '@testing-library/user-event'; +import { screen } from '@testing-library/react'; import React from 'react'; import { viewTechDocRouteRef } from '../../routes'; import { AboutCard } from './AboutCard'; @@ -73,7 +74,7 @@ describe('', () => { ], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { }, ); - expect(getByText('service')).toBeInTheDocument(); - expect(getByText('user:guest')).toBeInTheDocument(); - expect(getByText('production')).toBeInTheDocument(); - expect(getByText('This is the description')).toBeInTheDocument(); + expect(screen.getByText('service')).toBeInTheDocument(); + expect(screen.getByText('user:guest')).toBeInTheDocument(); + expect(screen.getByText('production')).toBeInTheDocument(); + expect(screen.getByText('This is the description')).toBeInTheDocument(); }); it('renders "view source" link', async () => { @@ -122,7 +123,7 @@ describe('', () => { }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { }, }, ); - expect(getByText('View Source').closest('a')).toHaveAttribute( + expect(screen.getByText('View Source').closest('a')).toHaveAttribute( 'href', 'https://github.com/backstage/backstage/blob/master/software.yaml', ); @@ -177,7 +178,7 @@ describe('', () => { }, }; - const { getByTitle } = await renderInTestApp( + await renderInTestApp( ', () => { }, ); - const editLink = getByTitle('Edit Metadata').closest('a'); + const editLink = screen.getByTitle('Edit Metadata').closest('a'); expect(editLink).toHaveAttribute( 'href', 'https://github.com/backstage/backstage/edit/master/software.yaml', @@ -230,7 +231,7 @@ describe('', () => { }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { }, }, ); - expect(getByText('View Source')).toBeVisible(); - expect(getByText('View Source').closest('a')).toBeNull(); + expect(screen.getByText('View Source')).toBeVisible(); + expect(screen.getByText('View Source').closest('a')).toBeNull(); }); it.each([ @@ -274,7 +275,7 @@ describe('', () => { }, }; - const { getByTitle } = await renderInTestApp( + await renderInTestApp( ', () => { 'component:default/software', ); - await userEvent.click(getByTitle('Schedule entity refresh')); + await userEvent.click(screen.getByTitle('Schedule entity refresh')); expect(catalogApi.refreshEntity).toHaveBeenCalledWith( 'component:default/software', @@ -320,7 +321,7 @@ describe('', () => { }, }; - const { queryByTitle } = await renderInTestApp( + await renderInTestApp( ', () => { }, ); - expect(queryByTitle('Schedule entity refresh')).not.toBeInTheDocument(); + expect( + screen.queryByTitle('Schedule entity refresh'), + ).not.toBeInTheDocument(); }); it('renders techdocs link', async () => { @@ -361,7 +364,7 @@ describe('', () => { }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { }, ); - expect(getByText('View TechDocs').closest('a')).toHaveAttribute( + expect(screen.getByText('View TechDocs').closest('a')).toHaveAttribute( 'href', '/docs/default/Component/software', ); @@ -414,7 +417,7 @@ describe('', () => { }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { }, ); - expect(getByText('View TechDocs')).toBeVisible(); - expect(getByText('View TechDocs').closest('a')).toBeNull(); + expect(screen.getByText('View TechDocs')).toBeVisible(); + expect(screen.getByText('View TechDocs').closest('a')).toBeNull(); }); it('renders disabled techdocs link when route is not bound', async () => { @@ -467,7 +470,7 @@ describe('', () => { }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { }, ); - expect(getByText('View TechDocs')).toBeVisible(); - expect(getByText('View TechDocs').closest('a')).toBeNull(); + expect(screen.getByText('View TechDocs')).toBeVisible(); + expect(screen.getByText('View TechDocs').closest('a')).toBeNull(); }); }); diff --git a/plugins/catalog/src/components/AboutCard/AboutContent.test.tsx b/plugins/catalog/src/components/AboutCard/AboutContent.test.tsx index f4dbb73b45..2a89f2fd92 100644 --- a/plugins/catalog/src/components/AboutCard/AboutContent.test.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutContent.test.tsx @@ -21,6 +21,7 @@ import { } from '@backstage/catalog-model'; import { entityRouteRef } from '@backstage/plugin-catalog-react'; import { renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { AboutContent } from './AboutContent'; @@ -62,32 +63,29 @@ describe('', () => { }); it('renders info', async () => { - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('user:o'); - expect(getByText('Domain')).toBeInTheDocument(); - expect(getByText('Domain').nextSibling).toHaveTextContent('d'); - expect(getByText('System')).toBeInTheDocument(); - expect(getByText('System').nextSibling).toHaveTextContent('s'); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('t'); - expect(getByText('Lifecycle')).toBeInTheDocument(); - expect(getByText('Lifecycle').nextSibling).toHaveTextContent('l'); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent('user:o'); + expect(screen.getByText('Domain')).toBeInTheDocument(); + expect(screen.getByText('Domain').nextSibling).toHaveTextContent('d'); + expect(screen.getByText('System')).toBeInTheDocument(); + expect(screen.getByText('System').nextSibling).toHaveTextContent('s'); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('t'); + expect(screen.getByText('Lifecycle')).toBeInTheDocument(); + expect(screen.getByText('Lifecycle').nextSibling).toHaveTextContent('l'); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('tag-1'); }); it('highlights missing required fields', async () => { @@ -95,26 +93,25 @@ describe('', () => { entity.spec = {}; entity.relations = []; - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(queryByText('System')).not.toBeInTheDocument(); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(queryByText('Type')).not.toBeInTheDocument(); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.queryByText('System')).not.toBeInTheDocument(); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.queryByText('Type')).not.toBeInTheDocument(); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); }); }); @@ -151,33 +148,34 @@ describe('', () => { }); it('renders info', async () => { - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(getByText('System')).toBeInTheDocument(); - expect(getByText('System').nextSibling).toHaveTextContent('system'); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('openapi'); - expect(getByText('Lifecycle')).toBeInTheDocument(); - expect(getByText('Lifecycle').nextSibling).toHaveTextContent( + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'user:guest', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.getByText('System')).toBeInTheDocument(); + expect(screen.getByText('System').nextSibling).toHaveTextContent( + 'system', + ); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('openapi'); + expect(screen.getByText('Lifecycle')).toBeInTheDocument(); + expect(screen.getByText('Lifecycle').nextSibling).toHaveTextContent( 'production', ); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1'); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('tag-1'); }); it('highlights missing required fields', async () => { @@ -187,31 +185,34 @@ describe('', () => { delete entity.spec!.system; entity.relations = []; - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(getByText('System')).toBeInTheDocument(); - expect(getByText('System').nextSibling).toHaveTextContent('No System'); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('unknown'); - expect(getByText('Lifecycle')).toBeInTheDocument(); - expect(getByText('Lifecycle').nextSibling).toHaveTextContent('unknown'); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.getByText('System')).toBeInTheDocument(); + expect(screen.getByText('System').nextSibling).toHaveTextContent( + 'No System', + ); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('unknown'); + expect(screen.getByText('Lifecycle')).toBeInTheDocument(); + expect(screen.getByText('Lifecycle').nextSibling).toHaveTextContent( + 'unknown', + ); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('No Tags'); }); }); @@ -252,36 +253,37 @@ describe('', () => { }); it('renders info', async () => { - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(getByText('System')).toBeInTheDocument(); - expect(getByText('System').nextSibling).toHaveTextContent('system'); - expect(getByText('Parent Component')).toBeInTheDocument(); - expect(getByText('Parent Component').nextSibling).toHaveTextContent( - 'parent-software', + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'user:guest', ); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('service'); - expect(getByText('Lifecycle')).toBeInTheDocument(); - expect(getByText('Lifecycle').nextSibling).toHaveTextContent( + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.getByText('System')).toBeInTheDocument(); + expect(screen.getByText('System').nextSibling).toHaveTextContent( + 'system', + ); + expect(screen.getByText('Parent Component')).toBeInTheDocument(); + expect( + screen.getByText('Parent Component').nextSibling, + ).toHaveTextContent('parent-software'); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('service'); + expect(screen.getByText('Lifecycle')).toBeInTheDocument(); + expect(screen.getByText('Lifecycle').nextSibling).toHaveTextContent( 'production', ); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1'); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('tag-1'); }); it('highlights missing required fields', async () => { @@ -291,31 +293,34 @@ describe('', () => { delete entity.spec!.system; entity.relations = []; - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(getByText('System')).toBeInTheDocument(); - expect(getByText('System').nextSibling).toHaveTextContent('No System'); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('unknown'); - expect(getByText('Lifecycle')).toBeInTheDocument(); - expect(getByText('Lifecycle').nextSibling).toHaveTextContent('unknown'); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.getByText('System')).toBeInTheDocument(); + expect(screen.getByText('System').nextSibling).toHaveTextContent( + 'No System', + ); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('unknown'); + expect(screen.getByText('Lifecycle')).toBeInTheDocument(); + expect(screen.getByText('Lifecycle').nextSibling).toHaveTextContent( + 'unknown', + ); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('No Tags'); }); }); @@ -344,56 +349,54 @@ describe('', () => { }); it('renders info', async () => { - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(queryByText('System')).not.toBeInTheDocument(); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(queryByText('Type')).not.toBeInTheDocument(); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'user:guest', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.queryByText('System')).not.toBeInTheDocument(); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.queryByText('Type')).not.toBeInTheDocument(); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('tag-1'); }); it('highlights missing required fields', async () => { delete entity.metadata.tags; entity.relations = []; - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(queryByText('System')).not.toBeInTheDocument(); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(queryByText('Type')).not.toBeInTheDocument(); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.queryByText('System')).not.toBeInTheDocument(); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.queryByText('Type')).not.toBeInTheDocument(); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('No Tags'); }); }); @@ -418,31 +421,30 @@ describe('', () => { }); it('renders info', async () => { - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(queryByText('System')).not.toBeInTheDocument(); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('root'); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1'); - expect(getByText('Targets')).toBeInTheDocument(); - expect(getByText('Targets').nextSibling).toHaveTextContent( + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.queryByText('System')).not.toBeInTheDocument(); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('root'); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('tag-1'); + expect(screen.getByText('Targets')).toBeInTheDocument(); + expect(screen.getByText('Targets').nextSibling).toHaveTextContent( 'https://backstage.io', ); }); @@ -451,29 +453,28 @@ describe('', () => { delete entity.metadata.tags; delete entity.spec!.type; - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(queryByText('System')).not.toBeInTheDocument(); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('unknown'); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.queryByText('System')).not.toBeInTheDocument(); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('unknown'); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('No Tags'); }); }); @@ -508,30 +509,31 @@ describe('', () => { }); it('renders info', async () => { - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(getByText('System')).toBeInTheDocument(); - expect(getByText('System').nextSibling).toHaveTextContent('system'); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('s3'); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'user:guest', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.getByText('System')).toBeInTheDocument(); + expect(screen.getByText('System').nextSibling).toHaveTextContent( + 'system', + ); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('s3'); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('tag-1'); }); it('highlights missing required fields', async () => { @@ -540,30 +542,31 @@ describe('', () => { delete entity.spec!.system; entity.relations = []; - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(getByText('System')).toBeInTheDocument(); - expect(getByText('System').nextSibling).toHaveTextContent('No System'); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('unknown'); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.getByText('System')).toBeInTheDocument(); + expect(screen.getByText('System').nextSibling).toHaveTextContent( + 'No System', + ); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('unknown'); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('No Tags'); }); }); @@ -597,29 +600,30 @@ describe('', () => { }); it('renders info', async () => { - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest'); - expect(getByText('Domain')).toBeInTheDocument(); - expect(getByText('Domain').nextSibling).toHaveTextContent('domain'); - expect(queryByText('System')).not.toBeInTheDocument(); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(queryByText('Type')).not.toBeInTheDocument(); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'user:guest', + ); + expect(screen.getByText('Domain')).toBeInTheDocument(); + expect(screen.getByText('Domain').nextSibling).toHaveTextContent( + 'domain', + ); + expect(screen.queryByText('System')).not.toBeInTheDocument(); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.queryByText('Type')).not.toBeInTheDocument(); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('tag-1'); }); it('highlights missing required fields', async () => { @@ -627,29 +631,30 @@ describe('', () => { delete entity.spec!.domain; entity.relations = []; - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(getByText('Domain')).toBeInTheDocument(); - expect(getByText('Domain').nextSibling).toHaveTextContent('No Domain'); - expect(queryByText('System')).not.toBeInTheDocument(); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(queryByText('Type')).not.toBeInTheDocument(); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.getByText('Domain')).toBeInTheDocument(); + expect(screen.getByText('Domain').nextSibling).toHaveTextContent( + 'No Domain', + ); + expect(screen.queryByText('System')).not.toBeInTheDocument(); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.queryByText('Type')).not.toBeInTheDocument(); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('No Tags'); }); }); }); diff --git a/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.test.tsx b/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.test.tsx index 15d41f3ab3..f511f58c58 100644 --- a/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.test.tsx +++ b/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.test.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { fireEvent } from '@testing-library/react'; +import { fireEvent, screen } from '@testing-library/react'; import { GetEntityFacetsResponse } from '@backstage/catalog-client'; import { Entity } from '@backstage/catalog-model'; import { @@ -31,6 +31,7 @@ import { } from '@backstage/test-utils'; import { CatalogKindHeader } from './CatalogKindHeader'; import { errorApiRef } from '@backstage/core-plugin-api'; +import pluralize from 'pluralize'; const entities: Entity[] = [ { @@ -83,7 +84,7 @@ const apis = TestApiRegistry.from( describe('', () => { it('renders available kinds', async () => { - const rendered = await renderWithEffects( + await renderWithEffects( @@ -91,18 +92,18 @@ describe('', () => { , ); - const input = rendered.getByText('Components'); + const input = screen.getByText('Components'); fireEvent.mouseDown(input); entities.map(entity => { expect( - rendered.getByRole('option', { name: `${entity.kind}s` }), + screen.getByRole('option', { name: `${pluralize(entity.kind)}` }), ).toBeInTheDocument(); }); }); it('renders unknown kinds provided in query parameters', async () => { - const rendered = await renderWithEffects( + await renderWithEffects( ', () => { , ); - expect(rendered.getByText('Frobs')).toBeInTheDocument(); + expect(screen.getByText('Frobs')).toBeInTheDocument(); }); it('updates the kind filter', async () => { const updateFilters = jest.fn(); - const rendered = await renderWithEffects( + await renderWithEffects( @@ -125,10 +126,10 @@ describe('', () => { , ); - const input = rendered.getByText('Components'); + const input = screen.getByText('Components'); fireEvent.mouseDown(input); - const option = rendered.getByRole('option', { name: 'Templates' }); + const option = screen.getByRole('option', { name: 'Templates' }); fireEvent.click(option); expect(updateFilters).toHaveBeenCalledWith({ @@ -171,7 +172,7 @@ describe('', () => { }); it('limits kinds when allowedKinds is set', async () => { - const rendered = await renderWithEffects( + await renderWithEffects( @@ -179,22 +180,20 @@ describe('', () => { , ); - const input = rendered.getByText('Components'); + const input = screen.getByText('Components'); fireEvent.mouseDown(input); expect( - rendered.getByRole('option', { name: 'Components' }), + screen.getByRole('option', { name: 'Components' }), ).toBeInTheDocument(); + expect(screen.getByRole('option', { name: 'Systems' })).toBeInTheDocument(); expect( - rendered.getByRole('option', { name: 'Systems' }), - ).toBeInTheDocument(); - expect( - rendered.queryByRole('option', { name: 'Templates' }), + screen.queryByRole('option', { name: 'Templates' }), ).not.toBeInTheDocument(); }); it('renders kind from the query parameter even when not in allowedKinds', async () => { - const rendered = await renderWithEffects( + await renderWithEffects( ', () => { , ); - expect(rendered.getByText('Frobs')).toBeInTheDocument(); - const input = rendered.getByText('Frobs'); + expect(screen.getByText('Frobs')).toBeInTheDocument(); + const input = screen.getByText('Frobs'); fireEvent.mouseDown(input); - expect( - rendered.getByRole('option', { name: 'Systems' }), - ).toBeInTheDocument(); + expect(screen.getByRole('option', { name: 'Systems' })).toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.tsx b/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.tsx index b0aaeae658..bb9e98d9c3 100644 --- a/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.tsx +++ b/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.tsx @@ -31,6 +31,7 @@ import { } from '@backstage/plugin-catalog-react'; import useAsync from 'react-use/lib/useAsync'; import { useApi } from '@backstage/core-plugin-api'; +import pluralize from 'pluralize'; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -132,7 +133,7 @@ export function CatalogKindHeader(props: CatalogKindHeaderProps) { > {Object.keys(options).map(kind => ( - {`${options[kind]}s`} + {`${pluralize(options[kind])}`} ))} diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx index 6fbdcf40a2..5b80f5976c 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx @@ -16,6 +16,7 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import { useOutlet } from 'react-router'; import { CatalogPage } from './CatalogPage'; @@ -30,15 +31,15 @@ jest.mock('./DefaultCatalogPage', () => ({ describe('CatalogPage', () => { it('renders provided router element', async () => { - const { getByText } = await renderInTestApp(); + await renderInTestApp(); - expect(getByText('Route Children')).toBeInTheDocument(); + expect(screen.getByText('Route Children')).toBeInTheDocument(); }); it('renders DefaultCatalogPage home when no router children are provided', async () => { (useOutlet as jest.Mock).mockReturnValueOnce(null); - const { getByText } = await renderInTestApp(); + await renderInTestApp(); - expect(getByText('DefaultCatalogPage')).toBeInTheDocument(); + expect(screen.getByText('DefaultCatalogPage')).toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx index cc00a57314..969992bd4e 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx @@ -178,11 +178,11 @@ describe('DefaultCatalogPage', () => { // limit. We should investigate why these timeouts happen. it('should render the default column of the grid', async () => { - const { getAllByRole } = await renderWrapped(); + await renderWrapped(); - const columnHeader = getAllByRole('button').filter( - c => c.tagName === 'SPAN', - ); + const columnHeader = screen + .getAllByRole('button') + .filter(c => c.tagName === 'SPAN'); const columnHeaderLabels = columnHeader.map(c => c.textContent); expect(columnHeaderLabels).toEqual([ @@ -203,26 +203,22 @@ describe('DefaultCatalogPage', () => { { title: 'Bar', field: 'entity.bar' }, { title: 'Baz', field: 'entity.spec.lifecycle' }, ]; - const { getAllByRole } = await renderWrapped( - , - ); + await renderWrapped(); - const columnHeader = getAllByRole('button').filter( - c => c.tagName === 'SPAN', - ); + const columnHeader = screen + .getAllByRole('button') + .filter(c => c.tagName === 'SPAN'); const columnHeaderLabels = columnHeader.map(c => c.textContent); expect(columnHeaderLabels).toEqual(['Foo', 'Bar', 'Baz', 'Actions']); }, 20_000); it('should render the default actions of an item in the grid', async () => { - const { getByTestId, findByTitle, findByText } = await renderWrapped( - , - ); - fireEvent.click(getByTestId('user-picker-owned')); - expect(await findByText(/Owned \(1\)/)).toBeInTheDocument(); - expect(await findByTitle(/View/)).toBeInTheDocument(); - expect(await findByTitle(/Edit/)).toBeInTheDocument(); - expect(await findByTitle(/Add to favorites/)).toBeInTheDocument(); + await renderWrapped(); + fireEvent.click(screen.getByTestId('user-picker-owned')); + expect(await screen.findByText(/Owned \(1\)/)).toBeInTheDocument(); + expect(await screen.findByTitle(/View/)).toBeInTheDocument(); + expect(await screen.findByTitle(/Edit/)).toBeInTheDocument(); + expect(await screen.findByTitle(/Add to favorites/)).toBeInTheDocument(); }, 20_000); it('should render the custom actions of an item passed as prop', async () => { @@ -245,41 +241,35 @@ describe('DefaultCatalogPage', () => { }, ]; - const { getByTestId, findByTitle, findByText } = await renderWrapped( - , - ); - fireEvent.click(getByTestId('user-picker-owned')); - expect(await findByText(/Owned \(1\)/)).toBeInTheDocument(); - expect(await findByTitle(/Foo Action/)).toBeInTheDocument(); - expect(await findByTitle(/Bar Action/)).toBeInTheDocument(); - expect((await findByTitle(/Bar Action/)).firstChild).toBeDisabled(); + await renderWrapped(); + fireEvent.click(screen.getByTestId('user-picker-owned')); + expect(await screen.findByText(/Owned \(1\)/)).toBeInTheDocument(); + expect(await screen.findByTitle(/Foo Action/)).toBeInTheDocument(); + expect(await screen.findByTitle(/Bar Action/)).toBeInTheDocument(); + expect((await screen.findByTitle(/Bar Action/)).firstChild).toBeDisabled(); }, 20_000); // this test right now causes some red lines in the log output when running tests // related to some theme issues in mui-table // https://github.com/mbrn/material-table/issues/1293 it('should render', async () => { - const { findByText, getByTestId } = await renderWrapped( - , - ); - fireEvent.click(getByTestId('user-picker-owned')); - await expect(findByText(/Owned \(1\)/)).resolves.toBeInTheDocument(); - fireEvent.click(getByTestId('user-picker-all')); - await expect(findByText(/All \(2\)/)).resolves.toBeInTheDocument(); + await renderWrapped(); + fireEvent.click(screen.getByTestId('user-picker-owned')); + await expect(screen.findByText(/Owned \(1\)/)).resolves.toBeInTheDocument(); + fireEvent.click(screen.getByTestId('user-picker-all')); + await expect(screen.findByText(/All \(2\)/)).resolves.toBeInTheDocument(); }, 20_000); it('should set initial filter correctly', async () => { - const { findByText } = await renderWrapped( - , - ); - await expect(findByText(/All \(2\)/)).resolves.toBeInTheDocument(); + await renderWrapped(); + await expect(screen.findByText(/All \(2\)/)).resolves.toBeInTheDocument(); }, 20_000); // this test is for fixing the bug after favoriting an entity, the matching // entities defaulting to "owned" filter and not based on the selected filter it('should render the correct entities filtered on the selected filter', async () => { - const { getByTestId } = await renderWrapped(); - fireEvent.click(getByTestId('user-picker-owned')); + await renderWrapped(); + fireEvent.click(screen.getByTestId('user-picker-owned')); await expect(screen.findByText(/Owned \(1\)/)).resolves.toBeInTheDocument(); // The "Starred" menu option should initially be disabled, since there // aren't any starred entities. @@ -308,10 +298,12 @@ describe('DefaultCatalogPage', () => { it('should wrap filter in drawer on smaller screens', async () => { mockBreakpoint({ matches: true }); - const { getByRole } = await renderWrapped(); - const button = getByRole('button', { name: 'Filters' }); - expect(getByRole('presentation', { hidden: true })).toBeInTheDocument(); + await renderWrapped(); + const button = screen.getByRole('button', { name: 'Filters' }); + expect( + screen.getByRole('presentation', { hidden: true }), + ).toBeInTheDocument(); fireEvent.click(button); - expect(getByRole('presentation')).toBeVisible(); + expect(screen.getByRole('presentation')).toBeVisible(); }, 20_000); }); diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx index bb2f302a37..652c9f3873 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx @@ -29,7 +29,7 @@ import { UserListFilter, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; -import { act, fireEvent } from '@testing-library/react'; +import { act, fireEvent, screen } from '@testing-library/react'; import * as React from 'react'; import { CatalogTable } from './CatalogTable'; @@ -66,7 +66,7 @@ describe('CatalogTable component', () => { }); it('should render error message', async () => { - const rendered = await renderInTestApp( + await renderInTestApp( @@ -78,14 +78,14 @@ describe('CatalogTable component', () => { }, }, ); - const errorMessage = await rendered.findByText( + const errorMessage = await screen.findByText( /Could not fetch catalog entities./, ); expect(errorMessage).toBeInTheDocument(); }); it('should display entity names when loading has finished and no error occurred', async () => { - const rendered = await renderInTestApp( + await renderInTestApp( { }, }, ); - expect(rendered.getByText(/Owned \(3\)/)).toBeInTheDocument(); - expect(rendered.getByText(/component1/)).toBeInTheDocument(); - expect(rendered.getByText(/component2/)).toBeInTheDocument(); - expect(rendered.getByText(/component3/)).toBeInTheDocument(); + expect(screen.getByText(/Owned \(3\)/)).toBeInTheDocument(); + expect(screen.getByText(/component1/)).toBeInTheDocument(); + expect(screen.getByText(/component2/)).toBeInTheDocument(); + expect(screen.getByText(/component3/)).toBeInTheDocument(); }); it('should use specified edit URL if in annotation', async () => { @@ -124,7 +124,7 @@ describe('CatalogTable component', () => { }, }; - const { getByTitle } = await renderInTestApp( + await renderInTestApp( @@ -137,7 +137,7 @@ describe('CatalogTable component', () => { }, ); - const editButton = getByTitle('Edit'); + const editButton = screen.getByTitle('Edit'); await act(async () => { fireEvent.click(editButton); @@ -156,7 +156,7 @@ describe('CatalogTable component', () => { }, }; - const { getByTitle } = await renderInTestApp( + await renderInTestApp( @@ -169,7 +169,7 @@ describe('CatalogTable component', () => { }, ); - const viewButton = getByTitle('View'); + const viewButton = screen.getByTitle('View'); await act(async () => { fireEvent.click(viewButton); @@ -278,7 +278,7 @@ describe('CatalogTable component', () => { ])( 'should render correct columns with kind filter $kind', async ({ kind, expectedColumns }) => { - const { getAllByRole } = await renderInTestApp( + await renderInTestApp( { }, ); - const columnHeader = getAllByRole('button').filter( - c => c.tagName === 'SPAN', - ); + const columnHeader = screen + .getAllByRole('button') + .filter(c => c.tagName === 'SPAN'); const columnHeaderLabels = columnHeader.map(c => c.textContent); expect(columnHeaderLabels).toEqual(expectedColumns); }, @@ -316,7 +316,7 @@ describe('CatalogTable component', () => { }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -329,7 +329,7 @@ describe('CatalogTable component', () => { }, ); - expect(getByText('Should be rendered')).toBeInTheDocument(); + expect(screen.getByText('Should be rendered')).toBeInTheDocument(); }); it('should render the label column with customised title and value as specified', async () => { @@ -347,7 +347,7 @@ describe('CatalogTable component', () => { }; const expectedColumns = ['Name', 'Category', 'Actions']; - const { getAllByRole, getByText } = await renderInTestApp( + await renderInTestApp( @@ -360,13 +360,13 @@ describe('CatalogTable component', () => { }, ); - const columnHeader = getAllByRole('button').filter( - c => c.tagName === 'SPAN', - ); + const columnHeader = screen + .getAllByRole('button') + .filter(c => c.tagName === 'SPAN'); const columnHeaderLabels = columnHeader.map(c => c.textContent); expect(columnHeaderLabels).toEqual(expectedColumns); - const labelCellValue = getByText('generic'); + const labelCellValue = screen.getByText('generic'); expect(labelCellValue).toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/DependencyOfComponentsCard/DependencyOfComponentsCard.test.tsx b/plugins/catalog/src/components/DependencyOfComponentsCard/DependencyOfComponentsCard.test.tsx index b3f8262076..94a9a86b70 100644 --- a/plugins/catalog/src/components/DependencyOfComponentsCard/DependencyOfComponentsCard.test.tsx +++ b/plugins/catalog/src/components/DependencyOfComponentsCard/DependencyOfComponentsCard.test.tsx @@ -22,7 +22,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import React from 'react'; import { DependencyOfComponentsCard } from './DependencyOfComponentsCard'; @@ -51,7 +51,7 @@ describe('', () => { relations: [], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -64,9 +64,9 @@ describe('', () => { }, ); - expect(getByText('Dependency of components')).toBeInTheDocument(); + expect(screen.getByText('Dependency of components')).toBeInTheDocument(); expect( - getByText(/No component depends on this component/i), + screen.getByText(/No component depends on this component/i), ).toBeInTheDocument(); }); @@ -99,7 +99,7 @@ describe('', () => { ], }); - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -113,8 +113,8 @@ describe('', () => { ); await waitFor(() => { - expect(getByText('Dependency of components')).toBeInTheDocument(); - expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(screen.getByText('Dependency of components')).toBeInTheDocument(); + expect(screen.getByText(/target-name/i)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.test.tsx b/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.test.tsx index 57106015b2..657f95c60f 100644 --- a/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.test.tsx +++ b/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.test.tsx @@ -22,7 +22,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { DependsOnComponentsCard } from './DependsOnComponentsCard'; @@ -51,7 +51,7 @@ describe('', () => { relations: [], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -64,9 +64,9 @@ describe('', () => { }, ); - expect(getByText('Depends on components')).toBeInTheDocument(); + expect(screen.getByText('Depends on components')).toBeInTheDocument(); expect( - getByText(/No component is a dependency of this component/i), + screen.getByText(/No component is a dependency of this component/i), ).toBeInTheDocument(); }); @@ -99,7 +99,7 @@ describe('', () => { ], }); - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -113,8 +113,8 @@ describe('', () => { ); await waitFor(() => { - expect(getByText('Depends on components')).toBeInTheDocument(); - expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(screen.getByText('Depends on components')).toBeInTheDocument(); + expect(screen.getByText(/target-name/i)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.test.tsx b/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.test.tsx index bd52bfeeb8..5a77cd6ab4 100644 --- a/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.test.tsx +++ b/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.test.tsx @@ -22,7 +22,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { DependsOnResourcesCard } from './DependsOnResourcesCard'; @@ -51,7 +51,7 @@ describe('', () => { relations: [], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -64,9 +64,9 @@ describe('', () => { }, ); - expect(getByText('Depends on resources')).toBeInTheDocument(); + expect(screen.getByText('Depends on resources')).toBeInTheDocument(); expect( - getByText(/No resource is a dependency of this component/i), + screen.getByText(/No resource is a dependency of this component/i), ).toBeInTheDocument(); }); @@ -99,7 +99,7 @@ describe('', () => { ], }); - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -113,8 +113,8 @@ describe('', () => { ); await waitFor(() => { - expect(getByText('Depends on resources')).toBeInTheDocument(); - expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(screen.getByText('Depends on resources')).toBeInTheDocument(); + expect(screen.getByText(/target-name/i)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.test.tsx b/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.test.tsx index 04cfa1b947..f7220cb1ea 100644 --- a/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.test.tsx +++ b/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.test.tsx @@ -63,7 +63,7 @@ describe('ComponentContextMenu', () => { it('check Unregister entity button is disabled', async () => { const mockCallback = jest.fn(); - const { getByText } = await render( + await render( { expect(button).toBeInTheDocument(); fireEvent.click(button); - const unregister = await screen.getByText('Unregister entity'); + const unregister = screen.getByText('Unregister entity'); expect(unregister).toBeInTheDocument(); - const unregisterSpanItem = getByText(/Unregister entity/); + const unregisterSpanItem = screen.getByText(/Unregister entity/); const unregisterMenuListItem = unregisterSpanItem?.parentElement?.parentElement; expect(unregisterMenuListItem).toHaveAttribute('aria-disabled'); diff --git a/plugins/catalog/src/components/EntityContextMenu/UnregisterEntity.test.tsx b/plugins/catalog/src/components/EntityContextMenu/UnregisterEntity.test.tsx index 95cb601ea0..fd57b79431 100644 --- a/plugins/catalog/src/components/EntityContextMenu/UnregisterEntity.test.tsx +++ b/plugins/catalog/src/components/EntityContextMenu/UnregisterEntity.test.tsx @@ -60,7 +60,7 @@ describe('ComponentContextMenu', () => { it('check Unregister entity button is disabled', async () => { const mockCallback = jest.fn(); - const { getByText } = await render( + await render( { />, ); - const unregister = await screen.getByText('Unregister entity'); + const unregister = screen.getByText('Unregister entity'); expect(unregister).toBeInTheDocument(); - const unregisterSpanItem = getByText(/Unregister entity/); + const unregisterSpanItem = screen.getByText(/Unregister entity/); const unregisterMenuListItem = unregisterSpanItem?.parentElement?.parentElement; expect(unregisterMenuListItem).toHaveAttribute('aria-disabled'); diff --git a/plugins/catalog/src/components/EntityLayout/EntityLayout.test.tsx b/plugins/catalog/src/components/EntityLayout/EntityLayout.test.tsx index 548fff3ff1..980ce13109 100644 --- a/plugins/catalog/src/components/EntityLayout/EntityLayout.test.tsx +++ b/plugins/catalog/src/components/EntityLayout/EntityLayout.test.tsx @@ -32,7 +32,7 @@ import { renderInTestApp, TestApiRegistry, } from '@backstage/test-utils'; -import { act, fireEvent } from '@testing-library/react'; +import { act, fireEvent, screen } from '@testing-library/react'; import React from 'react'; import { EntityLayout } from './EntityLayout'; @@ -52,7 +52,7 @@ const mockApis = TestApiRegistry.from( describe('EntityLayout', () => { it('renders simplest case', async () => { - const rendered = await renderInTestApp( + await renderInTestApp( @@ -69,9 +69,9 @@ describe('EntityLayout', () => { }, ); - expect(rendered.getByText('my-entity')).toBeInTheDocument(); - expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument(); - expect(rendered.getByText('tabbed-test-content')).toBeInTheDocument(); + expect(screen.getByText('my-entity')).toBeInTheDocument(); + expect(screen.getByText('tabbed-test-title')).toBeInTheDocument(); + expect(screen.getByText('tabbed-test-content')).toBeInTheDocument(); }); it('renders the entity title if defined', async () => { @@ -83,7 +83,7 @@ describe('EntityLayout', () => { }, } as Entity; - const rendered = await renderInTestApp( + await renderInTestApp( @@ -100,13 +100,13 @@ describe('EntityLayout', () => { }, ); - expect(rendered.getByText('My Entity')).toBeInTheDocument(); - expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument(); - expect(rendered.getByText('tabbed-test-content')).toBeInTheDocument(); + expect(screen.getByText('My Entity')).toBeInTheDocument(); + expect(screen.getByText('tabbed-test-title')).toBeInTheDocument(); + expect(screen.getByText('tabbed-test-content')).toBeInTheDocument(); }); it('renders default error message when entity is not found', async () => { - const rendered = await renderInTestApp( + await renderInTestApp( @@ -123,14 +123,14 @@ describe('EntityLayout', () => { }, ); - expect(rendered.getByText('Warning: Entity not found')).toBeInTheDocument(); - expect(rendered.queryByText('my-entity')).not.toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-title')).not.toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-content')).not.toBeInTheDocument(); + expect(screen.getByText('Warning: Entity not found')).toBeInTheDocument(); + expect(screen.queryByText('my-entity')).not.toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-title')).not.toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-content')).not.toBeInTheDocument(); }); it('renders custom message when entity is not found', async () => { - const rendered = await renderInTestApp( + await renderInTestApp( { ); expect( - rendered.getByText('Oppps.. Your entity was not found'), + screen.getByText('Oppps.. Your entity was not found'), ).toBeInTheDocument(); - expect(rendered.queryByText('my-entity')).not.toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-title')).not.toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-content')).not.toBeInTheDocument(); + expect(screen.queryByText('my-entity')).not.toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-title')).not.toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-content')).not.toBeInTheDocument(); }); it('navigates when user clicks different tab', async () => { - const rendered = await renderInTestApp( + await renderInTestApp( @@ -181,23 +181,23 @@ describe('EntityLayout', () => { }, ); - const secondTab = rendered.queryAllByRole('tab')[1]; + const secondTab = screen.queryAllByRole('tab')[1]; act(() => { fireEvent.click(secondTab); }); - expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-content')).not.toBeInTheDocument(); + expect(screen.getByText('tabbed-test-title')).toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-content')).not.toBeInTheDocument(); - expect(rendered.getByText('tabbed-test-title-2')).toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-content-2')).toBeInTheDocument(); + expect(screen.getByText('tabbed-test-title-2')).toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-content-2')).toBeInTheDocument(); }); it('should conditionally render tabs', async () => { const shouldRenderTab = (e: Entity) => e.metadata.name === 'my-entity'; const shouldNotRenderTab = (e: Entity) => e.metadata.name === 'some-entity'; - const rendered = await renderInTestApp( + await renderInTestApp( @@ -228,8 +228,8 @@ describe('EntityLayout', () => { }, ); - expect(rendered.queryByText('tabbed-test-title')).toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-title-2')).not.toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-title-3')).toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-title')).toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-title-2')).not.toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-title-3')).toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/EntityLinksCard/EntityLinksCard.test.tsx b/plugins/catalog/src/components/EntityLinksCard/EntityLinksCard.test.tsx index 8972cd2f9e..6316bdbc85 100644 --- a/plugins/catalog/src/components/EntityLinksCard/EntityLinksCard.test.tsx +++ b/plugins/catalog/src/components/EntityLinksCard/EntityLinksCard.test.tsx @@ -17,6 +17,7 @@ import { Entity, EntityLink } from '@backstage/catalog-model'; import { EntityProvider } from '@backstage/plugin-catalog-react'; import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { EntityLinksCard } from './EntityLinksCard'; @@ -43,7 +44,7 @@ describe('EntityLinksCard', () => { it('should render a link', async () => { const links: EntityLink[] = [createLink()]; - const { queryByText } = await renderWithEffects( + await renderWithEffects( wrapInTestApp( @@ -51,12 +52,12 @@ describe('EntityLinksCard', () => { ), ); - expect(queryByText('admin dashboard')).toBeInTheDocument(); - expect(queryByText('derp')).not.toBeInTheDocument(); + expect(screen.queryByText('admin dashboard')).toBeInTheDocument(); + expect(screen.queryByText('derp')).not.toBeInTheDocument(); }); it('should show empty state', async () => { - const { queryByText } = await renderWithEffects( + await renderWithEffects( wrapInTestApp( @@ -65,8 +66,8 @@ describe('EntityLinksCard', () => { ); expect( - queryByText(/.*No links defined for this entity.*/), + screen.queryByText(/.*No links defined for this entity.*/), ).toBeInTheDocument(); - expect(queryByText('admin dashboard')).not.toBeInTheDocument(); + expect(screen.queryByText('admin dashboard')).not.toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/EntityLinksCard/IconLink.test.tsx b/plugins/catalog/src/components/EntityLinksCard/IconLink.test.tsx index 9c1b021071..164e54a2eb 100644 --- a/plugins/catalog/src/components/EntityLinksCard/IconLink.test.tsx +++ b/plugins/catalog/src/components/EntityLinksCard/IconLink.test.tsx @@ -17,13 +17,13 @@ import { lightTheme } from '@backstage/theme'; import { ThemeProvider } from '@material-ui/core'; import CloudIcon from '@material-ui/icons/Cloud'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import React from 'react'; import { IconLink } from './IconLink'; describe('IconLink', () => { it('should render an icon link', () => { - const rendered = render( + render( { , ); - expect(rendered.queryByText('I am Link')).toBeInTheDocument(); + expect(screen.queryByText('I am Link')).toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/EntityNotFound/EntityNotFound.test.tsx b/plugins/catalog/src/components/EntityNotFound/EntityNotFound.test.tsx index 4f16078eb9..6f214dc1e0 100644 --- a/plugins/catalog/src/components/EntityNotFound/EntityNotFound.test.tsx +++ b/plugins/catalog/src/components/EntityNotFound/EntityNotFound.test.tsx @@ -16,13 +16,16 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import { EntityNotFound } from './EntityNotFound'; describe('', () => { it('renders without exploding', async () => { - const { getByText } = await renderInTestApp(); - expect(getByText(/entity was not found/i)).toBeInTheDocument(); - expect(getByText(/getting started documentation/i)).toBeInTheDocument(); - expect(getByText(/docs/i)).toBeInTheDocument(); + await renderInTestApp(); + expect(screen.getByText(/entity was not found/i)).toBeInTheDocument(); + expect( + screen.getByText(/getting started documentation/i), + ).toBeInTheDocument(); + expect(screen.getByText(/docs/i)).toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/EntityOrphanWarning/EntityOrphanWarning.test.tsx b/plugins/catalog/src/components/EntityOrphanWarning/EntityOrphanWarning.test.tsx index f00474868f..6071d15166 100644 --- a/plugins/catalog/src/components/EntityOrphanWarning/EntityOrphanWarning.test.tsx +++ b/plugins/catalog/src/components/EntityOrphanWarning/EntityOrphanWarning.test.tsx @@ -15,8 +15,8 @@ */ import { catalogApiRef, EntityProvider } from '@backstage/plugin-catalog-react'; - import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { rootRouteRef } from '../../routes'; import { EntityOrphanWarning } from './EntityOrphanWarning'; @@ -39,7 +39,7 @@ describe('', () => { }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { }, ); expect( - getByText( + screen.getByText( 'This entity is not referenced by any location and is therefore not receiving updates. Click here to delete.', ), ).toBeInTheDocument(); diff --git a/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.test.tsx b/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.test.tsx index 2297493a7a..cfe3022261 100644 --- a/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.test.tsx +++ b/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.test.tsx @@ -23,6 +23,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { EntityProcessingErrorsPanel } from './EntityProcessingErrorsPanel'; @@ -100,7 +101,7 @@ describe('', () => { rootEntityRef: stringifyEntityRef(entity), items: [{ entity, parentEntityRefs: [] }], }); - const { getByText, queryByText } = await renderInTestApp( + await renderInTestApp( @@ -109,14 +110,14 @@ describe('', () => { ); expect( - getByText( + screen.getByText( 'Error: Policy check failed; caused by Error: Malformed envelope, /metadata/labels should be object', ), ).toBeInTheDocument(); - expect(getByText('Error: Foo')).toBeInTheDocument(); - expect(queryByText('Error: This should not be rendered')).toBeNull(); + expect(screen.getByText('Error: Foo')).toBeInTheDocument(); + expect(screen.queryByText('Error: This should not be rendered')).toBeNull(); expect( - queryByText('The error below originates from'), + screen.queryByText('The error below originates from'), ).not.toBeInTheDocument(); }); @@ -204,7 +205,7 @@ describe('', () => { { entity: parent, parentEntityRefs: [] }, ], }); - const { getByText, queryByText } = await renderInTestApp( + await renderInTestApp( @@ -218,12 +219,14 @@ describe('', () => { ); expect( - getByText( + screen.getByText( 'Error: Policy check failed; caused by Error: Malformed envelope, /metadata/labels should be object', ), ).toBeInTheDocument(); - expect(getByText('Error: Foo')).toBeInTheDocument(); - expect(queryByText('Error: This should not be rendered')).toBeNull(); - expect(queryByText('The error below originates from')).toBeInTheDocument(); + expect(screen.getByText('Error: Foo')).toBeInTheDocument(); + expect(screen.queryByText('Error: This should not be rendered')).toBeNull(); + expect( + screen.queryByText('The error below originates from'), + ).toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx b/plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx index 4ee9d9c820..d2c51465b5 100644 --- a/plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx +++ b/plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx @@ -19,7 +19,7 @@ import { AsyncEntityProvider, EntityProvider, } from '@backstage/plugin-catalog-react'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import React from 'react'; import { isKind } from './conditions'; import { EntitySwitch } from './EntitySwitch'; @@ -54,9 +54,9 @@ describe('EntitySwitch', () => { , ); - expect(rendered.queryByText('A')).toBeInTheDocument(); - expect(rendered.queryByText('B')).not.toBeInTheDocument(); - expect(rendered.queryByText('C')).not.toBeInTheDocument(); + expect(screen.queryByText('A')).toBeInTheDocument(); + expect(screen.queryByText('B')).not.toBeInTheDocument(); + expect(screen.queryByText('C')).not.toBeInTheDocument(); rendered.rerender( @@ -68,9 +68,9 @@ describe('EntitySwitch', () => { , ); - expect(rendered.queryByText('A')).not.toBeInTheDocument(); - expect(rendered.queryByText('B')).toBeInTheDocument(); - expect(rendered.queryByText('C')).not.toBeInTheDocument(); + expect(screen.queryByText('A')).not.toBeInTheDocument(); + expect(screen.queryByText('B')).toBeInTheDocument(); + expect(screen.queryByText('C')).not.toBeInTheDocument(); rendered.rerender( @@ -82,9 +82,9 @@ describe('EntitySwitch', () => { , ); - expect(rendered.queryByText('A')).not.toBeInTheDocument(); - expect(rendered.queryByText('B')).not.toBeInTheDocument(); - expect(rendered.queryByText('C')).toBeInTheDocument(); + expect(screen.queryByText('A')).not.toBeInTheDocument(); + expect(screen.queryByText('B')).not.toBeInTheDocument(); + expect(screen.queryByText('C')).toBeInTheDocument(); rendered.rerender( @@ -94,9 +94,9 @@ describe('EntitySwitch', () => { , ); - expect(rendered.queryByText('A')).not.toBeInTheDocument(); - expect(rendered.queryByText('B')).not.toBeInTheDocument(); - expect(rendered.queryByText('C')).toBeInTheDocument(); + expect(screen.queryByText('A')).not.toBeInTheDocument(); + expect(screen.queryByText('B')).not.toBeInTheDocument(); + expect(screen.queryByText('C')).toBeInTheDocument(); }); it('should switch child when filters switch', () => { @@ -113,8 +113,8 @@ describe('EntitySwitch', () => { , ); - expect(rendered.queryByText('A')).toBeInTheDocument(); - expect(rendered.queryByText('B')).not.toBeInTheDocument(); + expect(screen.queryByText('A')).toBeInTheDocument(); + expect(screen.queryByText('B')).not.toBeInTheDocument(); rendered.rerender( @@ -127,15 +127,15 @@ describe('EntitySwitch', () => { , ); - expect(rendered.queryByText('A')).not.toBeInTheDocument(); - expect(rendered.queryByText('B')).toBeInTheDocument(); + expect(screen.queryByText('A')).not.toBeInTheDocument(); + expect(screen.queryByText('B')).toBeInTheDocument(); }); it('should switch with async condition that is true', async () => { const entity = { metadata: { name: 'mock' }, kind: 'component' } as Entity; const shouldRender = () => Promise.resolve(true); - const rendered = render( + render( @@ -146,15 +146,15 @@ describe('EntitySwitch', () => { , ); - await expect(rendered.findByText('A')).resolves.toBeInTheDocument(); - expect(rendered.queryByText('B')).not.toBeInTheDocument(); + await expect(screen.findByText('A')).resolves.toBeInTheDocument(); + expect(screen.queryByText('B')).not.toBeInTheDocument(); }); it('should switch with sync condition that is false', async () => { const entity = { metadata: { name: 'mock' }, kind: 'component' } as Entity; const shouldRender = () => Promise.resolve(false); - const rendered = render( + render( @@ -165,15 +165,15 @@ describe('EntitySwitch', () => { , ); - await expect(rendered.findByText('B')).resolves.toBeInTheDocument(); - expect(rendered.queryByText('A')).not.toBeInTheDocument(); + await expect(screen.findByText('B')).resolves.toBeInTheDocument(); + expect(screen.queryByText('A')).not.toBeInTheDocument(); }); it('should switch with sync condition that throws', async () => { const entity = { metadata: { name: 'mock' }, kind: 'component' } as Entity; const shouldRender = () => Promise.reject(); - const rendered = render( + render( @@ -185,8 +185,8 @@ describe('EntitySwitch', () => { , ); - await expect(rendered.findByText('C')).resolves.toBeInTheDocument(); - expect(rendered.queryByText('A')).not.toBeInTheDocument(); - expect(rendered.queryByText('B')).not.toBeInTheDocument(); + await expect(screen.findByText('C')).resolves.toBeInTheDocument(); + expect(screen.queryByText('A')).not.toBeInTheDocument(); + expect(screen.queryByText('B')).not.toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.test.tsx b/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.test.tsx index 77fdb74f2f..9d46f2ed42 100644 --- a/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.test.tsx +++ b/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.test.tsx @@ -22,7 +22,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { HasComponentsCard } from './HasComponentsCard'; @@ -51,7 +51,7 @@ describe('', () => { relations: [], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -64,9 +64,9 @@ describe('', () => { }, ); - expect(getByText('Has components')).toBeInTheDocument(); + expect(screen.getByText('Has components')).toBeInTheDocument(); expect( - getByText(/No component is part of this system/i), + screen.getByText(/No component is part of this system/i), ).toBeInTheDocument(); }); @@ -99,7 +99,7 @@ describe('', () => { ], }); - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -113,8 +113,8 @@ describe('', () => { ); await waitFor(() => { - expect(getByText('Has components')).toBeInTheDocument(); - expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(screen.getByText('Has components')).toBeInTheDocument(); + expect(screen.getByText(/target-name/i)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog/src/components/HasResourcesCard/HasResourcesCard.test.tsx b/plugins/catalog/src/components/HasResourcesCard/HasResourcesCard.test.tsx index 20830d2ccd..364d503a96 100644 --- a/plugins/catalog/src/components/HasResourcesCard/HasResourcesCard.test.tsx +++ b/plugins/catalog/src/components/HasResourcesCard/HasResourcesCard.test.tsx @@ -22,7 +22,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { HasResourcesCard } from './HasResourcesCard'; @@ -51,7 +51,7 @@ describe('', () => { relations: [], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -59,9 +59,9 @@ describe('', () => { , ); - expect(getByText('Has resources')).toBeInTheDocument(); + expect(screen.getByText('Has resources')).toBeInTheDocument(); expect( - getByText(/No resource is part of this system/i), + screen.getByText(/No resource is part of this system/i), ).toBeInTheDocument(); }); @@ -94,7 +94,7 @@ describe('', () => { ], }); - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -108,8 +108,8 @@ describe('', () => { ); await waitFor(() => { - expect(getByText('Has resources')).toBeInTheDocument(); - expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(screen.getByText('Has resources')).toBeInTheDocument(); + expect(screen.getByText(/target-name/i)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.test.tsx b/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.test.tsx index 7fd0273939..b76a0a68ec 100644 --- a/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.test.tsx +++ b/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.test.tsx @@ -22,7 +22,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { HasSubcomponentsCard } from './HasSubcomponentsCard'; @@ -51,7 +51,7 @@ describe('', () => { relations: [], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -64,9 +64,9 @@ describe('', () => { }, ); - expect(getByText('Has subcomponents')).toBeInTheDocument(); + expect(screen.getByText('Has subcomponents')).toBeInTheDocument(); expect( - getByText(/No subcomponent is part of this component/i), + screen.getByText(/No subcomponent is part of this component/i), ).toBeInTheDocument(); }); @@ -99,7 +99,7 @@ describe('', () => { ], }); - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -113,8 +113,8 @@ describe('', () => { ); await waitFor(() => { - expect(getByText('Has subcomponents')).toBeInTheDocument(); - expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(screen.getByText('Has subcomponents')).toBeInTheDocument(); + expect(screen.getByText(/target-name/i)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.test.tsx b/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.test.tsx index cd11e4d3f3..634fe57e82 100644 --- a/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.test.tsx +++ b/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.test.tsx @@ -22,7 +22,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { HasSystemsCard } from './HasSystemsCard'; @@ -51,7 +51,7 @@ describe('', () => { relations: [], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -64,8 +64,10 @@ describe('', () => { }, ); - expect(getByText('Has systems')).toBeInTheDocument(); - expect(getByText(/No system is part of this domain/i)).toBeInTheDocument(); + expect(screen.getByText('Has systems')).toBeInTheDocument(); + expect( + screen.getByText(/No system is part of this domain/i), + ).toBeInTheDocument(); }); it('shows related systems', async () => { @@ -97,7 +99,7 @@ describe('', () => { ], }); - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -111,8 +113,8 @@ describe('', () => { ); await waitFor(() => { - expect(getByText('Has systems')).toBeInTheDocument(); - expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(screen.getByText('Has systems')).toBeInTheDocument(); + expect(screen.getByText(/target-name/i)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.test.tsx b/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.test.tsx index 18b45a84d4..dc8d68e2b2 100644 --- a/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.test.tsx +++ b/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.test.tsx @@ -22,6 +22,7 @@ import { } from '@backstage/plugin-catalog-react'; import { Entity, RELATION_PART_OF } from '@backstage/catalog-model'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { SystemDiagramCard } from './SystemDiagramCard'; @@ -53,7 +54,7 @@ describe('', () => { relations: [], }; - const { queryByText } = await renderInTestApp( + await renderInTestApp( @@ -66,9 +67,9 @@ describe('', () => { }, ); - expect(queryByText(/System Diagram/)).toBeInTheDocument(); - expect(queryByText(/namespace2\/system2/)).toBeInTheDocument(); - expect(queryByText(/namespace\/entity/)).not.toBeInTheDocument(); + expect(screen.queryByText(/System Diagram/)).toBeInTheDocument(); + expect(screen.queryByText(/namespace2\/system2/)).toBeInTheDocument(); + expect(screen.queryByText(/namespace\/entity/)).not.toBeInTheDocument(); }); it('shows related systems', async () => { @@ -108,7 +109,7 @@ describe('', () => { ], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -121,9 +122,9 @@ describe('', () => { }, ); - expect(getByText('System Diagram')).toBeInTheDocument(); - expect(getByText('namespace/system')).toBeInTheDocument(); - expect(getByText('namespace/entity')).toBeInTheDocument(); + expect(screen.getByText('System Diagram')).toBeInTheDocument(); + expect(screen.getByText('namespace/system')).toBeInTheDocument(); + expect(screen.getByText('namespace/entity')).toBeInTheDocument(); }); it('should truncate long domains, systems or entities', async () => { @@ -163,7 +164,7 @@ describe('', () => { ], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -176,8 +177,8 @@ describe('', () => { }, ); - expect(getByText('namespace/alongdomai...')).toBeInTheDocument(); - expect(getByText('namespace/alongsyste...')).toBeInTheDocument(); - expect(getByText('namespace/alongentit...')).toBeInTheDocument(); + expect(screen.getByText('namespace/alongdomai...')).toBeInTheDocument(); + expect(screen.getByText('namespace/alongsyste...')).toBeInTheDocument(); + expect(screen.getByText('namespace/alongentit...')).toBeInTheDocument(); }); }); diff --git a/plugins/codescene/package.json b/plugins/codescene/package.json index f18267e5bc..42e4b6ecf1 100644 --- a/plugins/codescene/package.json +++ b/plugins/codescene/package.json @@ -30,7 +30,7 @@ "@material-ui/core": "^4.9.10", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.57", - "rc-progress": "3.4.0", + "rc-progress": "3.4.1", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.test.tsx b/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.test.tsx index de04f73e36..6d772efc9b 100644 --- a/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.test.tsx +++ b/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.test.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import React from 'react'; import { SyntheticsLocation } from './SyntheticsLocation'; import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; @@ -60,6 +61,6 @@ describe('SyntheticsLocation', () => { , ); expect(await rendered.findByText(/__location__/)).toBeInTheDocument(); - expect(await rendered.queryByText(/failed/)).not.toBeInTheDocument(); + expect(rendered.queryByText(/failed/)).not.toBeInTheDocument(); }); }); diff --git a/plugins/events-backend/README.md b/plugins/events-backend/README.md index f71df21517..cef8ac7330 100644 --- a/plugins/events-backend/README.md +++ b/plugins/events-backend/README.md @@ -170,8 +170,8 @@ const http = HttpPostIngressEventPublisher.fromConfig({ }, }, logger: env.logger, - router: httpRouter, }); +http.bind(router); await new EventsBackend(env.logger) .addPublishers(http) diff --git a/plugins/events-backend/api-report.md b/plugins/events-backend/api-report.md index 4154168fa1..69d7600ed3 100644 --- a/plugins/events-backend/api-report.md +++ b/plugins/events-backend/api-report.md @@ -33,6 +33,8 @@ export const eventsPlugin: (options?: undefined) => BackendFeature; // @public export class HttpPostIngressEventPublisher implements EventPublisher { + // (undocumented) + bind(router: express.Router): void; // (undocumented) static fromConfig(env: { config: Config; @@ -40,7 +42,6 @@ export class HttpPostIngressEventPublisher implements EventPublisher { [topic: string]: Omit; }; logger: Logger; - router: express.Router; }): HttpPostIngressEventPublisher; // (undocumented) setEventBroker(eventBroker: EventBroker): Promise; diff --git a/plugins/events-backend/src/service/EventsPlugin.ts b/plugins/events-backend/src/service/EventsPlugin.ts index d5a38a7fd7..65e7770d87 100644 --- a/plugins/events-backend/src/service/EventsPlugin.ts +++ b/plugins/events-backend/src/service/EventsPlugin.ts @@ -90,14 +90,11 @@ export const eventsPlugin = createBackendPlugin({ env.registerInit({ deps: { config: configServiceRef, - httpRouter: httpRouterServiceRef, logger: loggerServiceRef, + router: httpRouterServiceRef, }, - async init({ config, httpRouter, logger }) { + async init({ config, logger, router }) { const winstonLogger = loggerToWinstonLogger(logger); - const eventsRouter = Router(); - const router = Router(); - eventsRouter.use('/http', router); const ingresses = Object.fromEntries( extensionPoint.httpPostIngresses.map(ingress => [ @@ -108,23 +105,20 @@ export const eventsPlugin = createBackendPlugin({ const http = HttpPostIngressEventPublisher.fromConfig({ config, - logger: winstonLogger, - router, ingresses, + logger: winstonLogger, }); + const eventsRouter = Router(); + http.bind(eventsRouter); + router.use(eventsRouter); - if (!extensionPoint.eventBroker) { - extensionPoint.setEventBroker(new InMemoryEventBroker(winstonLogger)); - } + const eventBroker = + extensionPoint.eventBroker ?? new InMemoryEventBroker(winstonLogger); - extensionPoint.eventBroker!.subscribe(extensionPoint.subscribers); + eventBroker.subscribe(extensionPoint.subscribers); [extensionPoint.publishers, http] .flat() - .forEach(publisher => - publisher.setEventBroker(extensionPoint.eventBroker!), - ); - - httpRouter.use(eventsRouter); + .forEach(publisher => publisher.setEventBroker(eventBroker)); }, }); }, diff --git a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts index 3d93e347b3..28f3ddcac4 100644 --- a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts +++ b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { errorHandler, getVoidLogger } from '@backstage/backend-common'; +import { getVoidLogger } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { TestEventBroker } from '@backstage/plugin-events-backend-test-utils'; import express from 'express'; @@ -35,37 +35,35 @@ describe('HttpPostIngressEventPublisher', () => { }); const router = Router(); - router.use(express.json()); - router.use(errorHandler()); const app = express().use(router); const publisher = HttpPostIngressEventPublisher.fromConfig({ config, - logger, - router, ingresses: { testB: {}, }, + logger, }); + publisher.bind(router); const eventBroker = new TestEventBroker(); await publisher.setEventBroker(eventBroker); const notFoundResponse = await request(app) - .post('/unknown') + .post('/http/unknown') .timeout(100) .send({ test: 'data' }); expect(notFoundResponse.status).toBe(404); const response1 = await request(app) - .post('/testA') + .post('/http/testA') .set('X-Custom-Header', 'test-value') .timeout(100) .send({ testA: 'data' }); expect(response1.status).toBe(202); const response2 = await request(app) - .post('/testB') + .post('/http/testB') .set('X-Custom-Header', 'test-value') .timeout(100) .send({ testB: 'data' }); @@ -100,14 +98,10 @@ describe('HttpPostIngressEventPublisher', () => { }); const router = Router(); - router.use(express.json()); - router.use(errorHandler()); const app = express().use(router); const publisher = HttpPostIngressEventPublisher.fromConfig({ config, - logger, - router, ingresses: { testB: { validator: async (req, context) => { @@ -148,26 +142,28 @@ describe('HttpPostIngressEventPublisher', () => { }, }, }, + logger, }); + publisher.bind(router); const eventBroker = new TestEventBroker(); await publisher.setEventBroker(eventBroker); const response1 = await request(app) - .post('/testA') + .post('/http/testA') .timeout(100) .send({ test: 'data' }); expect(response1.status).toBe(202); const response2 = await request(app) - .post('/testB') + .post('/http/testB') .timeout(100) .send({ test: 'data' }); expect(response2.status).toBe(400); expect(response2.body).toEqual({ message: 'wrong signature' }); const response3 = await request(app) - .post('/testB') + .post('/http/testB') .set('X-Test-Signature', 'wrong') .timeout(100) .send({ test: 'data' }); @@ -175,21 +171,21 @@ describe('HttpPostIngressEventPublisher', () => { expect(response3.body).toEqual({ message: 'wrong signature' }); const response4 = await request(app) - .post('/testB') + .post('/http/testB') .set('X-Test-Signature', 'testB-signature') .timeout(100) .send({ test: 'data' }); expect(response4.status).toBe(202); const response5 = await request(app) - .post('/testC') + .post('/http/testC') .timeout(100) .send({ test: 'data' }); expect(response5.status).toBe(404); expect(response5.body).toEqual({}); const response6 = await request(app) - .post('/testD') + .post('/http/testD') .timeout(100) .send({ test: 'data' }); expect(response6.status).toBe(403); @@ -210,15 +206,10 @@ describe('HttpPostIngressEventPublisher', () => { it('without configuration', async () => { const config = new ConfigReader({}); - const router = Router(); - router.use(express.json()); - router.use(errorHandler()); - expect(() => HttpPostIngressEventPublisher.fromConfig({ config, logger, - router, }), ).not.toThrow(); }); diff --git a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts index 64b7140b54..b5a6ccbca1 100644 --- a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts +++ b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts @@ -41,7 +41,6 @@ export class HttpPostIngressEventPublisher implements EventPublisher { config: Config; ingresses?: { [topic: string]: Omit }; logger: Logger; - router: express.Router; }): HttpPostIngressEventPublisher { const topics = env.config.getOptionalStringArray('events.http.topics') ?? []; @@ -55,15 +54,18 @@ export class HttpPostIngressEventPublisher implements EventPublisher { } }); - return new HttpPostIngressEventPublisher(env.logger, env.router, ingresses); + return new HttpPostIngressEventPublisher(env.logger, ingresses); } private constructor( - private logger: Logger, - router: express.Router, - ingresses: { [topic: string]: Omit }, - ) { - router.use(this.createRouter(ingresses)); + private readonly logger: Logger, + private readonly ingresses: { + [topic: string]: Omit; + }, + ) {} + + bind(router: express.Router): void { + router.use('/http', this.createRouter(this.ingresses)); } async setEventBroker(eventBroker: EventBroker): Promise { @@ -92,8 +94,12 @@ export class HttpPostIngressEventPublisher implements EventPublisher { const path = `/${topic}`; router.post(path, async (request, response) => { + const requestDetails = { + body: request.body, + headers: request.headers, + }; const context = new RequestValidationContextImpl(); - await validator?.(request, context); + await validator?.(requestDetails, context); if (context.wasRejected()) { response .status(context.rejectionDetails!.status) diff --git a/plugins/events-node/api-report.md b/plugins/events-node/api-report.md index 1054211cbc..51136fd301 100644 --- a/plugins/events-node/api-report.md +++ b/plugins/events-node/api-report.md @@ -4,7 +4,6 @@ ```ts import { ExtensionPoint } from '@backstage/backend-plugin-api'; -import { Request as Request_2 } from 'express'; // @public export interface EventBroker { @@ -74,6 +73,12 @@ export interface HttpPostIngressOptions { validator?: RequestValidator; } +// @public (undocumented) +export interface RequestDetails { + body: unknown; + headers: Record; +} + // @public export interface RequestRejectionDetails { // (undocumented) @@ -89,7 +94,7 @@ export interface RequestValidationContext { // @public export type RequestValidator = ( - request: Request_2, + request: RequestDetails, context: RequestValidationContext, ) => Promise; diff --git a/plugins/events-node/package.json b/plugins/events-node/package.json index d193f33b00..978037f8ff 100644 --- a/plugins/events-node/package.json +++ b/plugins/events-node/package.json @@ -24,9 +24,7 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/backend-plugin-api": "workspace:^", - "@types/express": "^4.17.6", - "express": "^4.17.1" + "@backstage/backend-plugin-api": "workspace:^" }, "devDependencies": { "@backstage/cli": "workspace:^" diff --git a/plugins/events-node/src/api/http/validation/RequestDetails.ts b/plugins/events-node/src/api/http/validation/RequestDetails.ts new file mode 100644 index 0000000000..83c2669503 --- /dev/null +++ b/plugins/events-node/src/api/http/validation/RequestDetails.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @public + */ +export interface RequestDetails { + /** + * Request body. JSON payloads have been parsed already. + */ + body: unknown; + /** + * Key-value pairs of header names and values. Header names are lower-cased. + */ + headers: Record; +} diff --git a/plugins/events-node/src/api/http/validation/RequestValidator.ts b/plugins/events-node/src/api/http/validation/RequestValidator.ts index b419b855cf..3008db03ae 100644 --- a/plugins/events-node/src/api/http/validation/RequestValidator.ts +++ b/plugins/events-node/src/api/http/validation/RequestValidator.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Request } from 'express'; +import { RequestDetails } from './RequestDetails'; import { RequestValidationContext } from './RequestValidationContext'; /** @@ -29,6 +29,6 @@ import { RequestValidationContext } from './RequestValidationContext'; * @public */ export type RequestValidator = ( - request: Request, + request: RequestDetails, context: RequestValidationContext, ) => Promise; diff --git a/plugins/events-node/src/api/http/validation/index.ts b/plugins/events-node/src/api/http/validation/index.ts index 95f2d474eb..11f2e9f1f6 100644 --- a/plugins/events-node/src/api/http/validation/index.ts +++ b/plugins/events-node/src/api/http/validation/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +export type { RequestDetails } from './RequestDetails'; export type { RequestRejectionDetails } from './RequestRejectionDetails'; export type { RequestValidationContext } from './RequestValidationContext'; export type { RequestValidator } from './RequestValidator'; diff --git a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx index 23553b8b76..674ef1e43f 100644 --- a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx +++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx @@ -43,8 +43,10 @@ import useAsync from 'react-use/lib/useAsync'; const useStyles = makeStyles((theme: BackstageTheme) => ({ graph: { - flex: 1, - minHeight: 0, + minHeight: '100%', + }, + graphWrapper: { + height: '100%', }, organizationNode: { fill: theme.palette.secondary.light, @@ -62,6 +64,15 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({ justifyContent: 'center', color: 'black', }, + legend: { + position: 'absolute', + bottom: 0, + right: 0, + padding: theme.spacing(1), + '& .icon': { + verticalAlign: 'bottom', + }, + }, textOrganization: { color: theme.palette.secondary.contrastText, }, @@ -221,7 +232,7 @@ export function GroupsDiagram() { } return ( - <> +
+ - Use pinch & zoom - to move around the diagram. + Use pinch & zoom to move around the + diagram. - +
); } diff --git a/plugins/gocd/src/components/Select/Select.test.tsx b/plugins/gocd/src/components/Select/Select.test.tsx index fb467b946f..9d73194ed8 100644 --- a/plugins/gocd/src/components/Select/Select.test.tsx +++ b/plugins/gocd/src/components/Select/Select.test.tsx @@ -40,7 +40,7 @@ describe('Select', () => { expect(rendered.getAllByText(testLabel)).toHaveLength(2); }); - describe('when the user hasn`t clicked on it', () => { + describe("when the user hasn't clicked on it", () => { it('should only render the current select item', async () => { const rendered = await renderInTestApp( ', () => { it('renders nothing', async () => { const { queryByTestId } = subject(); - expect(await queryByTestId('AuditListTable')).toBeNull(); + expect(queryByTestId('AuditListTable')).toBeNull(); }); }); @@ -137,7 +137,7 @@ describe('', () => { it('renders nothing', async () => { const { queryByTestId } = subject(); - expect(await queryByTestId('AuditListTable')).toBeNull(); + expect(queryByTestId('AuditListTable')).toBeNull(); }); }); }); diff --git a/plugins/lighthouse/src/components/Cards/LastLighthouseAuditCard.test.tsx b/plugins/lighthouse/src/components/Cards/LastLighthouseAuditCard.test.tsx index 58a3033a1f..ab1e2683b6 100644 --- a/plugins/lighthouse/src/components/Cards/LastLighthouseAuditCard.test.tsx +++ b/plugins/lighthouse/src/components/Cards/LastLighthouseAuditCard.test.tsx @@ -151,10 +151,10 @@ describe('', () => { it('renders nothing', async () => { const { queryByTestId } = subject(); - expect(await queryByTestId('AuditListTable')).toBeNull(); + expect(queryByTestId('AuditListTable')).toBeNull(); }); }); - // + describe('where there is no data', () => { beforeEach(() => { (useWebsiteForEntity as jest.Mock).mockReturnValue({ @@ -166,7 +166,7 @@ describe('', () => { it('renders nothing', async () => { const { queryByTestId } = subject(); - expect(await queryByTestId('AuditListTable')).toBeNull(); + expect(queryByTestId('AuditListTable')).toBeNull(); }); }); }); diff --git a/plugins/org-react/src/components/GroupListPicker/GroupListPicker.test.tsx b/plugins/org-react/src/components/GroupListPicker/GroupListPicker.test.tsx index b8a499b8fe..21b85ffe5c 100644 --- a/plugins/org-react/src/components/GroupListPicker/GroupListPicker.test.tsx +++ b/plugins/org-react/src/components/GroupListPicker/GroupListPicker.test.tsx @@ -15,7 +15,8 @@ */ import React from 'react'; -import { fireEvent, render, waitFor } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { ApiProvider } from '@backstage/core-app-api'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { CatalogApi } from '@backstage/catalog-client'; @@ -74,13 +75,13 @@ describe('', () => {
, ); - fireEvent.click(getByTestId('group-list-picker-button')); + await userEvent.click(getByTestId('group-list-picker-button')); const input = getByTestId('group-list-picker-input').querySelector('input'); - fireEvent.change(input as HTMLElement, { target: { value: 'GR' } }); + await userEvent.type(input as HTMLElement, 'GR'); await waitFor(async () => { expect(getByText('Group A')).toBeInTheDocument(); - fireEvent.click(getByText('Group A')); + await userEvent.click(getByText('Group A')); expect(getByText('Group A')).toBeInTheDocument(); }); }); diff --git a/plugins/pagerduty/src/api/client.ts b/plugins/pagerduty/src/api/client.ts index 7266d5e9eb..7d99be219d 100644 --- a/plugins/pagerduty/src/api/client.ts +++ b/plugins/pagerduty/src/api/client.ts @@ -73,7 +73,7 @@ export class PagerDutyClient implements PagerDutyApi { url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/pagerduty/services?${commonGetServiceParams}&query=${integrationKey}`; - const { services } = await this.getByUrl(url); + const { services } = await this.findByUrl(url); const service = services[0]; if (!service) throw new NotFoundError(); @@ -84,7 +84,7 @@ export class PagerDutyClient implements PagerDutyApi { 'proxy', )}/pagerduty/services/${serviceId}?${commonGetServiceParams}`; - response = await this.getByUrl(url); + response = await this.findByUrl(url); } else { throw new NotFoundError(); } @@ -100,7 +100,7 @@ export class PagerDutyClient implements PagerDutyApi { 'proxy', )}/pagerduty/incidents?${params}`; - return await this.getByUrl(url); + return await this.findByUrl(url); } async getChangeEventsByServiceId( @@ -111,7 +111,7 @@ export class PagerDutyClient implements PagerDutyApi { 'proxy', )}/pagerduty/services/${serviceId}/change_events?${params}`; - return await this.getByUrl(url); + return await this.findByUrl(url); } async getOnCallByPolicyId( @@ -122,7 +122,7 @@ export class PagerDutyClient implements PagerDutyApi { 'proxy', )}/pagerduty/oncalls?${params}`; - return await this.getByUrl(url); + return await this.findByUrl(url); } triggerAlarm(request: PagerDutyTriggerAlarmRequest): Promise { @@ -158,7 +158,7 @@ export class PagerDutyClient implements PagerDutyApi { return this.request(`${url}/enqueue`, options); } - private async getByUrl(url: string): Promise { + private async findByUrl(url: string): Promise { const options = { method: 'GET', headers: { diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts index 2891ea51cf..fe29378e2e 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Config } from '@backstage/config'; import { assertError, InputError } from '@backstage/errors'; import { @@ -124,6 +125,7 @@ export async function createGithubRepoWithCollaboratorsAndTopics( topics: string[] | undefined, logger: Logger, ) { + // eslint-disable-next-line testing-library/no-await-sync-query const user = await client.rest.users.getByUsername({ username: owner, }); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index eff7e95c50..634e344a2c 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -23,9 +23,10 @@ import { stringifyEntityRef, UserEntity, } from '@backstage/catalog-model'; -import { Config, JsonObject, JsonValue } from '@backstage/config'; +import { Config } from '@backstage/config'; import { InputError, NotFoundError, stringifyError } from '@backstage/errors'; import { ScmIntegrations } from '@backstage/integration'; +import { JsonObject, JsonValue } from '@backstage/types'; import { TaskSpec, TemplateEntityV1beta3, diff --git a/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx b/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx index 837b87437f..57dde13a43 100644 --- a/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx +++ b/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx @@ -236,7 +236,7 @@ describe('', () => { ); await act(async () => { - const allButton = await getByText('All'); + const allButton = getByText('All'); fireEvent.click(allButton); }); diff --git a/plugins/scaffolder/src/components/ListTasksPage/OwnerListPicker.test.tsx b/plugins/scaffolder/src/components/ListTasksPage/OwnerListPicker.test.tsx index 63f943d6dd..7c335a0ef7 100644 --- a/plugins/scaffolder/src/components/ListTasksPage/OwnerListPicker.test.tsx +++ b/plugins/scaffolder/src/components/ListTasksPage/OwnerListPicker.test.tsx @@ -15,7 +15,6 @@ */ import { renderInTestApp } from '@backstage/test-utils'; - import React from 'react'; import { OwnerListPicker } from './OwnerListPicker'; import { fireEvent } from '@testing-library/react'; @@ -29,8 +28,8 @@ describe('', () => { const { getByText } = await renderInTestApp(); - expect(await getByText('Owned')).toBeDefined(); - expect(await getByText('All')).toBeDefined(); + expect(getByText('Owned')).toBeDefined(); + expect(getByText('All')).toBeDefined(); }); it('should call the function on select other item', async () => { @@ -41,7 +40,7 @@ describe('', () => { const { getByText } = await renderInTestApp(); - fireEvent.click(await getByText('All')); + fireEvent.click(getByText('All')); expect(props.onSelectOwner).toHaveBeenCalledWith('all'); }); }); diff --git a/plugins/scaffolder/src/components/Router.tsx b/plugins/scaffolder/src/components/Router.tsx index da7ceebe5b..d35bcbc67a 100644 --- a/plugins/scaffolder/src/components/Router.tsx +++ b/plugins/scaffolder/src/components/Router.tsx @@ -167,6 +167,7 @@ export const Router = (props: RouterProps) => { ReviewStepComponent={ReviewStepComponent} customFieldExtensions={fieldExtensions} layouts={customLayouts} + headerOptions={props.headerOptions} /> } diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx index a48644e72d..71f5ffad47 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { MockAnalyticsApi, renderInTestApp, @@ -340,9 +341,9 @@ describe('TemplatePage', () => { }, ); - expect(await queryByText('Name')).not.toBeInTheDocument(); - expect(await queryByText('Description')).toBeInTheDocument(); - expect(await queryByText('Owner')).toBeInTheDocument(); - expect(await queryByText('Send data')).toBeInTheDocument(); + expect(queryByText('Name')).not.toBeInTheDocument(); + expect(queryByText('Description')).toBeInTheDocument(); + expect(queryByText('Owner')).toBeInTheDocument(); + expect(queryByText('Send data')).toBeInTheDocument(); }); }); diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index 01dc3b832d..cfe7992ab1 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -52,15 +52,23 @@ const useTemplateParameterSchema = (templateRef: string) => { return { schema: value, loading, error }; }; +type Props = { + ReviewStepComponent?: ComponentType; + customFieldExtensions?: FieldExtensionOptions[]; + layouts?: LayoutOptions[]; + headerOptions?: { + pageTitleOverride?: string; + title?: string; + subtitle?: string; + }; +}; + export const TemplatePage = ({ ReviewStepComponent, customFieldExtensions = [], layouts = [], -}: { - ReviewStepComponent?: ComponentType; - customFieldExtensions?: FieldExtensionOptions[]; - layouts?: LayoutOptions[]; -}) => { + headerOptions, +}: Props) => { const apiHolder = useApiHolder(); const secretsContext = useContext(SecretsContext); const errorApi = useApi(errorApiRef); @@ -139,6 +147,7 @@ export const TemplatePage = ({ pageTitleOverride="Create a New Component" title="Create a New Component" subtitle="Create new software components using standard templates" + {...headerOptions} /> {loading && } diff --git a/plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.test.tsx b/plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.test.tsx index e6022d16fb..a8e377bd3d 100644 --- a/plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.test.tsx +++ b/plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.test.tsx @@ -34,7 +34,7 @@ describe('RegisterExistingButton', () => { , ); - expect(await queryByText('Pick me')).not.toBeInTheDocument(); + expect(queryByText('Pick me')).not.toBeInTheDocument(); }); it('should not render if permissions are not allowed', async () => { @@ -43,7 +43,7 @@ describe('RegisterExistingButton', () => { , ); - expect(await queryByText('Pick me')).not.toBeInTheDocument(); + expect(queryByText('Pick me')).not.toBeInTheDocument(); }); it('should render the button with the text', async () => { @@ -52,6 +52,6 @@ describe('RegisterExistingButton', () => { , ); - expect(await queryByText('Pick me')).toBeInTheDocument(); + expect(queryByText('Pick me')).toBeInTheDocument(); }); }); diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/ReviewState.test.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/ReviewState.test.tsx index 2b2e706102..c03df8f73c 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/ReviewState.test.tsx +++ b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/ReviewState.test.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import React from 'react'; import { ReviewState } from './ReviewState'; import { render } from '@testing-library/react'; @@ -97,9 +98,7 @@ describe('ReviewState', () => { , ); - expect( - await queryByRole('row', { name: 'Name ******' }), - ).not.toBeInTheDocument(); + expect(queryByRole('row', { name: 'Name ******' })).not.toBeInTheDocument(); }); it('should allow for masking an option with a set text', () => { diff --git a/plugins/search-backend-node/src/indexing/BatchSearchEngineIndexer.ts b/plugins/search-backend-node/src/indexing/BatchSearchEngineIndexer.ts index 043e2c1953..b41dd524a9 100644 --- a/plugins/search-backend-node/src/indexing/BatchSearchEngineIndexer.ts +++ b/plugins/search-backend-node/src/indexing/BatchSearchEngineIndexer.ts @@ -34,26 +34,10 @@ export type BatchSearchEngineOptions = { export abstract class BatchSearchEngineIndexer extends Writable { private batchSize: number; private currentBatch: IndexableDocument[] = []; - private initialized: Promise; constructor(options: BatchSearchEngineOptions) { super({ objectMode: true }); this.batchSize = options.batchSize; - - // @todo Once node v15 is minimum, convert to _construct implementation. - this.initialized = new Promise(done => { - // Necessary to allow concrete implementation classes to construct - // themselves before calling their initialize() methods. - setImmediate(async () => { - try { - await this.initialize(); - done(undefined); - } catch (e) { - assertError(e); - done(e); - } - }); - }); } /** @@ -73,6 +57,20 @@ export abstract class BatchSearchEngineIndexer extends Writable { */ public abstract finalize(): Promise; + /** + * Encapsulates initialization logic. + * @internal + */ + async _construct(done: (error?: Error | null | undefined) => void) { + try { + await this.initialize(); + done(); + } catch (e) { + assertError(e); + done(e); + } + } + /** * Encapsulates batch stream write logic. * @internal @@ -82,13 +80,6 @@ export abstract class BatchSearchEngineIndexer extends Writable { _e: any, done: (error?: Error | null) => void, ) { - // Wait for init before proceeding. Throw error if initialization failed. - const maybeError = await this.initialized; - if (maybeError) { - done(maybeError); - return; - } - this.currentBatch.push(doc); if (this.currentBatch.length < this.batchSize) { done(); @@ -111,12 +102,6 @@ export abstract class BatchSearchEngineIndexer extends Writable { */ async _final(done: (error?: Error | null) => void) { try { - const maybeError = await this.initialized; - if (maybeError) { - done(maybeError); - return; - } - // Index any remaining documents. if (this.currentBatch.length) { await this.index(this.currentBatch); diff --git a/plugins/search-backend-node/src/indexing/DecoratorBase.ts b/plugins/search-backend-node/src/indexing/DecoratorBase.ts index bcf5729674..9ed8dfe0a7 100644 --- a/plugins/search-backend-node/src/indexing/DecoratorBase.ts +++ b/plugins/search-backend-node/src/indexing/DecoratorBase.ts @@ -24,25 +24,8 @@ import { Transform } from 'stream'; * @public */ export abstract class DecoratorBase extends Transform { - private initialized: Promise; - constructor() { super({ objectMode: true }); - - // @todo Once node v15 is minimum, convert to _construct implementation. - this.initialized = new Promise(done => { - // Necessary to allow concrete implementation classes to construct - // themselves before calling their initialize() methods. - setImmediate(async () => { - try { - await this.initialize(); - done(undefined); - } catch (e) { - assertError(e); - done(e); - } - }); - }); } /** @@ -68,6 +51,20 @@ export abstract class DecoratorBase extends Transform { */ public abstract finalize(): Promise; + /** + * Encapsulates initialization logic. + * @internal + */ + async _construct(done: (error?: Error | null | undefined) => void) { + try { + await this.initialize(); + done(); + } catch (e) { + assertError(e); + done(e); + } + } + /** * Encapsulates simple transform stream logic. * @internal @@ -77,13 +74,6 @@ export abstract class DecoratorBase extends Transform { _: any, done: (error?: Error | null) => void, ) { - // Wait for init before proceeding. Throw error if initialization failed. - const maybeError = await this.initialized; - if (maybeError) { - done(maybeError); - return; - } - try { const decorated = await this.decorate(document); diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index d8f54b80f5..1ea59e61f8 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -44,7 +44,7 @@ "@material-ui/lab": "4.0.0-alpha.57", "@material-ui/styles": "^4.10.0", "cross-fetch": "^3.1.5", - "rc-progress": "3.4.0", + "rc-progress": "3.4.1", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/splunk-on-call/src/api/client.ts b/plugins/splunk-on-call/src/api/client.ts index aa655a4772..2c5e122ac8 100644 --- a/plugins/splunk-on-call/src/api/client.ts +++ b/plugins/splunk-on-call/src/api/client.ts @@ -64,7 +64,7 @@ export class SplunkOnCallClient implements SplunkOnCallApi { 'proxy', )}/splunk-on-call/v1/incidents`; - const { incidents } = await this.getByUrl(url); + const { incidents } = await this.findByUrl(url); return incidents; } @@ -73,7 +73,7 @@ export class SplunkOnCallClient implements SplunkOnCallApi { const url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/splunk-on-call/v1/oncall/current`; - const { teamsOnCall } = await this.getByUrl(url); + const { teamsOnCall } = await this.findByUrl(url); return teamsOnCall; } @@ -82,7 +82,7 @@ export class SplunkOnCallClient implements SplunkOnCallApi { const url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/splunk-on-call/v1/team`; - const teams = await this.getByUrl(url); + const teams = await this.findByUrl(url); return teams; } @@ -91,7 +91,7 @@ export class SplunkOnCallClient implements SplunkOnCallApi { const url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/splunk-on-call/v1/org/routing-keys`; - const { routingKeys } = await this.getByUrl(url); + const { routingKeys } = await this.findByUrl(url); return routingKeys; } @@ -100,7 +100,7 @@ export class SplunkOnCallClient implements SplunkOnCallApi { const url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/splunk-on-call/v2/user`; - const { users } = await this.getByUrl(url); + const { users } = await this.findByUrl(url); return users; } @@ -109,7 +109,7 @@ export class SplunkOnCallClient implements SplunkOnCallApi { const url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/splunk-on-call/v1/policies`; - const { policies } = await this.getByUrl(url); + const { policies } = await this.findByUrl(url); return policies; } @@ -146,7 +146,7 @@ export class SplunkOnCallClient implements SplunkOnCallApi { return this.request(url, options); } - private async getByUrl(url: string): Promise { + private async findByUrl(url: string): Promise { const options = { method: 'GET', headers: { diff --git a/plugins/splunk-on-call/src/components/EntitySplunkOnCallCard.test.tsx b/plugins/splunk-on-call/src/components/EntitySplunkOnCallCard.test.tsx index 051d9de030..704d2d9ed9 100644 --- a/plugins/splunk-on-call/src/components/EntitySplunkOnCallCard.test.tsx +++ b/plugins/splunk-on-call/src/components/EntitySplunkOnCallCard.test.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Entity } from '@backstage/catalog-model'; import { ApiProvider, ConfigReader } from '@backstage/core-app-api'; import { @@ -187,7 +188,7 @@ describe('SplunkOnCallCard', () => { { timeout: 2000 }, ); - const createIncidentButton = await getByText('Create Incident'); + const createIncidentButton = getByText('Create Incident'); await act(async () => { fireEvent.click(createIncidentButton); }); diff --git a/plugins/techdocs-react/api-report.md b/plugins/techdocs-react/api-report.md index d04b852d07..09da4e012c 100644 --- a/plugins/techdocs-react/api-report.md +++ b/plugins/techdocs-react/api-report.md @@ -173,7 +173,7 @@ export const useShadowRootElements: < ) => TReturnedElement[]; // @public -export const useShadowRootSelection: (wait?: number) => Selection | null; +export const useShadowRootSelection: (waitMillis?: number) => Selection | null; // @public export const useTechDocsAddons: () => { diff --git a/plugins/techdocs-react/src/hooks.ts b/plugins/techdocs-react/src/hooks.ts index 560c548156..e8cc7bddc8 100644 --- a/plugins/techdocs-react/src/hooks.ts +++ b/plugins/techdocs-react/src/hooks.ts @@ -57,10 +57,10 @@ const isValidSelection = (newSelection: Selection) => { }; /** - * Hook for retreiving a selection within the ShadowRoot. + * Hook for retrieving a selection within the ShadowRoot. * @public */ -export const useShadowRootSelection = (wait: number = 0) => { +export const useShadowRootSelection = (waitMillis: number = 0) => { const shadowRoot = useShadowRoot(); const [selection, setSelection] = useState(null); const handleSelectionChange = useMemo( @@ -78,8 +78,8 @@ export const useShadowRootSelection = (wait: number = 0) => { } else { setSelection(null); } - }, wait), - [shadowRoot, setSelection, wait], + }, waitMillis), + [shadowRoot, setSelection, waitMillis], ); useEffect(() => { diff --git a/plugins/techdocs/src/home/components/Grids/EntityListDocsGrid.test.tsx b/plugins/techdocs/src/home/components/Grids/EntityListDocsGrid.test.tsx index effd8fe0c5..2837ab340a 100644 --- a/plugins/techdocs/src/home/components/Grids/EntityListDocsGrid.test.tsx +++ b/plugins/techdocs/src/home/components/Grids/EntityListDocsGrid.test.tsx @@ -103,10 +103,10 @@ describe('Entity List Docs Grid', () => { }, ); - expect(await screen.queryByText('All Documentation')).toBeInTheDocument(); - expect(await screen.queryByText('Documentation #1')).toBeInTheDocument(); - expect(await screen.queryByText('Documentation #2')).toBeInTheDocument(); - expect(await screen.queryByTestId('doc-not-found')).not.toBeInTheDocument(); + expect(screen.queryByText('All Documentation')).toBeInTheDocument(); + expect(screen.queryByText('Documentation #1')).toBeInTheDocument(); + expect(screen.queryByText('Documentation #2')).toBeInTheDocument(); + expect(screen.queryByTestId('doc-not-found')).not.toBeInTheDocument(); }); it('should render only filtered entities with filtering', async () => { @@ -131,14 +131,10 @@ describe('Entity List Docs Grid', () => { }, ); - expect( - await screen.queryByText('Curated Documentation'), - ).toBeInTheDocument(); - expect(await screen.queryByText('Documentation #1')).toBeInTheDocument(); - expect( - await screen.queryByText('Documentation #2'), - ).not.toBeInTheDocument(); - expect(await screen.queryByTestId('doc-not-found')).not.toBeInTheDocument(); + expect(screen.queryByText('Curated Documentation')).toBeInTheDocument(); + expect(screen.queryByText('Documentation #1')).toBeInTheDocument(); + expect(screen.queryByText('Documentation #2')).not.toBeInTheDocument(); + expect(screen.queryByTestId('doc-not-found')).not.toBeInTheDocument(); }); it('should render nothing with filtering yielding no result', async () => { @@ -163,16 +159,10 @@ describe('Entity List Docs Grid', () => { }, ); - expect( - await screen.queryByText('Curated Documentation'), - ).not.toBeInTheDocument(); - expect( - await screen.queryByText('Documentation #1'), - ).not.toBeInTheDocument(); - expect( - await screen.queryByText('Documentation #2'), - ).not.toBeInTheDocument(); - expect(await screen.queryByTestId('doc-not-found')).not.toBeInTheDocument(); + expect(screen.queryByText('Curated Documentation')).not.toBeInTheDocument(); + expect(screen.queryByText('Documentation #1')).not.toBeInTheDocument(); + expect(screen.queryByText('Documentation #2')).not.toBeInTheDocument(); + expect(screen.queryByTestId('doc-not-found')).not.toBeInTheDocument(); }); it('should render an error without any documentation and without filtering', async () => { @@ -189,15 +179,9 @@ describe('Entity List Docs Grid', () => { }, ); - expect( - await screen.queryByText('All Documentation'), - ).not.toBeInTheDocument(); - expect( - await screen.queryByText('Documentation #1'), - ).not.toBeInTheDocument(); - expect( - await screen.queryByText('Documentation #2'), - ).not.toBeInTheDocument(); - expect(await screen.queryByTestId('doc-not-found')).toBeInTheDocument(); + expect(screen.queryByText('All Documentation')).not.toBeInTheDocument(); + expect(screen.queryByText('Documentation #1')).not.toBeInTheDocument(); + expect(screen.queryByText('Documentation #2')).not.toBeInTheDocument(); + expect(screen.queryByTestId('doc-not-found')).toBeInTheDocument(); }); }); diff --git a/plugins/vault-backend/src/service/VaultBuilder.ts b/plugins/vault-backend/src/service/VaultBuilder.ts index b82a5802bb..788b90ff01 100644 --- a/plugins/vault-backend/src/service/VaultBuilder.ts +++ b/plugins/vault-backend/src/service/VaultBuilder.ts @@ -17,7 +17,8 @@ import { Config } from '@backstage/config'; import { InputError } from '@backstage/errors'; import { Logger } from 'winston'; -import express, { Router } from 'express'; +import express from 'express'; +import Router from 'express-promise-router'; import { VaultClient } from './vaultApi'; import { TaskRunner, PluginTaskScheduler } from '@backstage/backend-tasks'; import { errorHandler } from '@backstage/backend-common'; diff --git a/plugins/vault-backend/src/service/vaultApi.ts b/plugins/vault-backend/src/service/vaultApi.ts index 785352fea8..ea588964dc 100644 --- a/plugins/vault-backend/src/service/vaultApi.ts +++ b/plugins/vault-backend/src/service/vaultApi.ts @@ -15,7 +15,7 @@ */ import { Config } from '@backstage/config'; -import { NotFoundError } from '@backstage/errors'; +import { NotAllowedError, NotFoundError } from '@backstage/errors'; import fetch from 'node-fetch'; import plimit from 'p-limit'; import { getVaultConfig, VaultConfig } from '../config'; @@ -103,6 +103,8 @@ export class VaultClient implements VaultApi { return (await response.json()) as T; } else if (response.status === 404) { throw new NotFoundError(`No secrets found in path '${path}'`); + } else if (response.status === 403) { + throw new NotAllowedError(response.statusText); } throw new Error( `Unexpected error while fetching secrets from path '${path}'`, diff --git a/scripts/generate-merge-message.js b/scripts/generate-merge-message.js new file mode 100755 index 0000000000..9b6fb5cbc6 --- /dev/null +++ b/scripts/generate-merge-message.js @@ -0,0 +1,115 @@ +#!/usr/bin/env node +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const { execFile: execFileCb } = require('child_process'); +const { promisify } = require('util'); +const { resolve: resolvePath } = require('path'); + +const execFile = promisify(execFileCb); + +async function hasNewChangesets(ref) { + if (!ref) { + throw new Error('ref is required'); + } + + const { stdout } = await execFile('git', [ + 'diff', + '--compact-summary', + ref, + '.changeset/*.md', + ]); + return stdout.includes('(new)'); +} + +function getReleaseOfMonth(year, month) { + const base = new Date(Date.UTC(year, month)); + const wednesdayOffset = + base.getUTCDay() > 3 ? 10 - base.getUTCDay() : 3 - base.getUTCDay(); + const thirdWednesdayOffset = wednesdayOffset + 7 * 2; + const releaseOffset = thirdWednesdayOffset - 1; + const releaseDay = new Date( + Date.UTC(base.getUTCFullYear(), base.getUTCMonth(), releaseOffset + 1), + ); + return releaseDay; +} + +function getReleaseSchedule() { + const firstReleaseYear = 2022; + const firstReleaseMonth = 2; + + return Array(100) + .fill(0) + .map((_, i) => { + const date = getReleaseOfMonth(firstReleaseYear, firstReleaseMonth + i); + return { version: `1.${i}.0`, date }; + }); +} + +function getCurrentRelease() { + const { version: releaseVersion } = require(resolvePath('package.json')); + + const match = releaseVersion.match(/^(\d+\.\d+\.\d+)/); + if (!match) { + throw new Error(`Failed to parse release version, '${releaseVersion}'`); + } + const [versionStr] = match; + if (versionStr === releaseVersion) { + return releaseVersion; + } + const [major, minor] = versionStr.split('.').map(Number); + return `${major}.${minor - 1}.0`; +} + +function findNextRelease(currentRelease, releaseSchedule) { + const currentIndex = releaseSchedule.findIndex( + r => r.version === currentRelease, + ); + if (currentIndex === -1) { + throw new Error( + `Failed to find current release '${currentRelease}' in release schedule`, + ); + } + + return releaseSchedule[currentIndex + 1]; +} + +async function main() { + const [diffRef = 'origin/master'] = process.argv.slice(2); + const needsMessage = await hasNewChangesets(diffRef); + if (!needsMessage) { + return; + } + + const currentRelease = getCurrentRelease(); + const releaseSchedule = getReleaseSchedule(); + const nextRelease = findNextRelease(currentRelease, releaseSchedule); + + const scheduledDate = nextRelease.date + .toUTCString() + .replace(/\s*\d+:\d+:\d+.*/, ''); + process.stdout.write( + [ + 'Thank you for contributing to Backstage! The changes in this pull request will be part', + `of the \`${nextRelease.version}\` release, scheduled for ${scheduledDate}.`, + ].join(' '), + ); +} + +main().catch(error => { + console.error(error.stack); + process.exit(1); +}); diff --git a/scripts/verify-lockfile-duplicates.js b/scripts/verify-lockfile-duplicates.js new file mode 100644 index 0000000000..590de0f93d --- /dev/null +++ b/scripts/verify-lockfile-duplicates.js @@ -0,0 +1,135 @@ +#!/usr/bin/env node +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* eslint-disable import/no-extraneous-dependencies */ + +const { execFile: execFileCb } = require('child_process'); +const { resolve: resolvePath, dirname: dirnamePath } = require('path'); +const { promisify } = require('util'); + +const execFile = promisify(execFileCb); + +async function findLockFiles() { + const projectRoot = resolvePath(__dirname, '..'); + + let files = process.argv.slice(2).filter(arg => !arg.startsWith('--')); + + for (const argumentFile of files) { + if (!argumentFile.match(/(?:^|[\/\\])yarn.lock$/)) { + throw new Error(`Not a yarn.lock file path argument: "${argumentFile}"`); + } + } + + if (!files.length) { + // List all lock files that are in the root or in an immediate subdirectory + files = ['yarn.lock', 'cypress/yarn.lock', 'microsite/yarn.lock']; + } + + return files.map(file => ({ + fileRelativeToProjectRoot: file, + directoryRelativeToProjectRoot: dirnamePath(file), + directoryAbsolute: resolvePath(projectRoot, dirnamePath(file)), + })); +} + +async function main() { + const lockFiles = await findLockFiles(); + + let fix = false; + for (const arg of process.argv) { + if (arg.startsWith('--')) { + if (arg === '--fix') { + fix = true; + } else { + throw new Error(`Unknown argument ${arg}`); + } + } + } + + for (const lockFile of lockFiles) { + console.log('Checking lock file', lockFile.fileRelativeToProjectRoot); + + let stdout; + let stderr; + let failed; + + try { + const result = await execFile( + 'yarn', + ['dedupe', ...(fix ? [] : ['--check'])], + { + shell: true, + cwd: lockFile.directoryAbsolute, + }, + ); + stdout = result.stdout?.trim(); + stderr = result.stderr?.trim(); + failed = false; + } catch (error) { + stdout = error.stdout?.trim(); + stderr = error.stderr?.trim(); + failed = true; + } + + if (stdout) { + console.log(stdout); + } + + if (stderr) { + console.error(stderr); + } + + if (failed) { + if (!fix) { + const command = `yarn dedupe${ + lockFile.directoryRelativeToProjectRoot === '.' + ? '' + : ` --cwd ${lockFile.directoryRelativeToProjectRoot}` + }`; + const padding = ' '.repeat(Math.max(0, 85 - 6 - command.length)); + console.error(''); + console.error( + '*************************************************************************************', + ); + console.error( + '* You have duplicate versions of some packages in a yarn.lock file. *', + ); + console.error( + '* To solve this, run the following command from the project root and commit all *', + ); + console.log( + '* yarn.lock changes. *', + ); + console.log( + '* *', + ); + console.log(`* ${command}${padding} *`); + console.error( + '*************************************************************************************', + ); + console.error(''); + } + + process.exit(1); + } + } +} + +main().catch(error => { + console.error(error.stack); + process.exit(1); +}); diff --git a/storybook/yarn.lock b/storybook/yarn.lock index 3317acf774..f7c56bc493 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -1528,18 +1528,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.12.11, @babel/types@npm:^7.12.7, @babel/types@npm:^7.18.10, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.2.0, @babel/types@npm:^7.4.4": - version: 7.18.10 - resolution: "@babel/types@npm:7.18.10" - dependencies: - "@babel/helper-string-parser": ^7.18.10 - "@babel/helper-validator-identifier": ^7.18.6 - to-fast-properties: ^2.0.0 - checksum: 11632c9b106e54021937a6498138014ebc9ad6c327a07b2af3ba8700773945aba4055fd136431cbe3a500d0f363cbf9c68eb4d6d38229897c5de9d06e14c85e8 - languageName: node - linkType: hard - -"@babel/types@npm:^7.8.3": +"@babel/types@npm:^7.12.11, @babel/types@npm:^7.12.7, @babel/types@npm:^7.18.10, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.2.0, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.18.13 resolution: "@babel/types@npm:7.18.13" dependencies: @@ -1985,7 +1974,7 @@ __metadata: languageName: node linkType: hard -"@storybook/addons@npm:6.5.13, @storybook/addons@npm:^6.5.9": +"@storybook/addons@npm:6.5.13, @storybook/addons@npm:^6.0.0, @storybook/addons@npm:^6.5.9": version: 6.5.13 resolution: "@storybook/addons@npm:6.5.13" dependencies: @@ -2007,28 +1996,6 @@ __metadata: languageName: node linkType: hard -"@storybook/addons@npm:^6.0.0": - version: 6.5.12 - resolution: "@storybook/addons@npm:6.5.12" - dependencies: - "@storybook/api": 6.5.12 - "@storybook/channels": 6.5.12 - "@storybook/client-logger": 6.5.12 - "@storybook/core-events": 6.5.12 - "@storybook/csf": 0.0.2--canary.4566f4d.1 - "@storybook/router": 6.5.12 - "@storybook/theming": 6.5.12 - "@types/webpack-env": ^1.16.0 - core-js: ^3.8.2 - global: ^4.4.0 - regenerator-runtime: ^0.13.7 - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: c6242a80c7355544eb309603e77fdc3787d78ad983aba931f00812aeba75cc2cbd0e98c1ac0ce01441b58fabcdb671a9a799358f4bd6511cab289bc030d91f61 - languageName: node - linkType: hard - "@storybook/api@npm:6.5.10": version: 6.5.10 resolution: "@storybook/api@npm:6.5.10" @@ -2057,35 +2024,7 @@ __metadata: languageName: node linkType: hard -"@storybook/api@npm:6.5.12, @storybook/api@npm:^6.0.0": - version: 6.5.12 - resolution: "@storybook/api@npm:6.5.12" - dependencies: - "@storybook/channels": 6.5.12 - "@storybook/client-logger": 6.5.12 - "@storybook/core-events": 6.5.12 - "@storybook/csf": 0.0.2--canary.4566f4d.1 - "@storybook/router": 6.5.12 - "@storybook/semver": ^7.3.2 - "@storybook/theming": 6.5.12 - core-js: ^3.8.2 - fast-deep-equal: ^3.1.3 - global: ^4.4.0 - lodash: ^4.17.21 - memoizerific: ^1.11.3 - regenerator-runtime: ^0.13.7 - store2: ^2.12.0 - telejson: ^6.0.8 - ts-dedent: ^2.0.0 - util-deprecate: ^1.0.2 - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 3982cea5aaf851ccc19ff97ef82b7590d47839f0ebee28399e3b9381578edc130b4e46fe36431c62fa281949b2e0d5da2b1feafab0c2d24f70c4097d800b2679 - languageName: node - linkType: hard - -"@storybook/api@npm:6.5.13": +"@storybook/api@npm:6.5.13, @storybook/api@npm:^6.0.0": version: 6.5.13 resolution: "@storybook/api@npm:6.5.13" dependencies: @@ -2265,17 +2204,6 @@ __metadata: languageName: node linkType: hard -"@storybook/channels@npm:6.5.12": - version: 6.5.12 - resolution: "@storybook/channels@npm:6.5.12" - dependencies: - core-js: ^3.8.2 - ts-dedent: ^2.0.0 - util-deprecate: ^1.0.2 - checksum: e6b240a6c62a68a485bf8f4db536df0504cfcbe9685654e5a5712b833917b9a620e91994bf2283a420e413511c967e92ead522c98ad7e6c0e88b3830ddfd4e30 - languageName: node - linkType: hard - "@storybook/channels@npm:6.5.13": version: 6.5.13 resolution: "@storybook/channels@npm:6.5.13" @@ -2318,7 +2246,7 @@ __metadata: languageName: node linkType: hard -"@storybook/client-logger@npm:6.5.10, @storybook/client-logger@npm:^6.4.0": +"@storybook/client-logger@npm:6.5.10": version: 6.5.10 resolution: "@storybook/client-logger@npm:6.5.10" dependencies: @@ -2328,17 +2256,7 @@ __metadata: languageName: node linkType: hard -"@storybook/client-logger@npm:6.5.12": - version: 6.5.12 - resolution: "@storybook/client-logger@npm:6.5.12" - dependencies: - core-js: ^3.8.2 - global: ^4.4.0 - checksum: bd11bc25115f9b4a965e378d7dac28f9152038173ab5debb1e116a7aba69c814752d2c8aa4092dd1fc3f60cd99d4896c9e74d5e6f3c85768e7633adaf5bd2bf2 - languageName: node - linkType: hard - -"@storybook/client-logger@npm:6.5.13": +"@storybook/client-logger@npm:6.5.13, @storybook/client-logger@npm:^6.4.0": version: 6.5.13 resolution: "@storybook/client-logger@npm:6.5.13" dependencies: @@ -2348,7 +2266,7 @@ __metadata: languageName: node linkType: hard -"@storybook/components@npm:6.5.13": +"@storybook/components@npm:6.5.13, @storybook/components@npm:^6.0.0": version: 6.5.13 resolution: "@storybook/components@npm:6.5.13" dependencies: @@ -2367,25 +2285,6 @@ __metadata: languageName: node linkType: hard -"@storybook/components@npm:^6.0.0": - version: 6.5.12 - resolution: "@storybook/components@npm:6.5.12" - dependencies: - "@storybook/client-logger": 6.5.12 - "@storybook/csf": 0.0.2--canary.4566f4d.1 - "@storybook/theming": 6.5.12 - core-js: ^3.8.2 - memoizerific: ^1.11.3 - qs: ^6.10.0 - regenerator-runtime: ^0.13.7 - util-deprecate: ^1.0.2 - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: fa469ae615d9146df7e23f01b85731d27e6400e2d94035db172deb1f61903d86c121d858558dd12307ecc6344d21b496db020731e73eff6ace3f82672b953a93 - languageName: node - linkType: hard - "@storybook/core-client@npm:6.5.13": version: 6.5.13 resolution: "@storybook/core-client@npm:6.5.13" @@ -2494,16 +2393,7 @@ __metadata: languageName: node linkType: hard -"@storybook/core-events@npm:6.5.12, @storybook/core-events@npm:^6.0.0": - version: 6.5.12 - resolution: "@storybook/core-events@npm:6.5.12" - dependencies: - core-js: ^3.8.2 - checksum: 82a4b9cb2a8599f3916db84b08b4cfbde8f56cb96a7afe641b3f144676fc7dc5a705e65f8844430b36ee6e6e14d6b2cb741622a8b39411276682219df5e04271 - languageName: node - linkType: hard - -"@storybook/core-events@npm:6.5.13": +"@storybook/core-events@npm:6.5.13, @storybook/core-events@npm:^6.0.0": version: 6.5.13 resolution: "@storybook/core-events@npm:6.5.13" dependencies: @@ -2913,22 +2803,6 @@ __metadata: languageName: node linkType: hard -"@storybook/router@npm:6.5.12": - version: 6.5.12 - resolution: "@storybook/router@npm:6.5.12" - dependencies: - "@storybook/client-logger": 6.5.12 - core-js: ^3.8.2 - memoizerific: ^1.11.3 - qs: ^6.10.0 - regenerator-runtime: ^0.13.7 - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 545f4b767021b88f82eac69b9356fa5fa3a5866285c3a34fa762abc5743e3280895858aa5c820195d95a04c6768299191d4dd788a7d9fd3f17ff1d8236c0ba75 - languageName: node - linkType: hard - "@storybook/router@npm:6.5.13": version: 6.5.13 resolution: "@storybook/router@npm:6.5.13" @@ -3052,22 +2926,7 @@ __metadata: languageName: node linkType: hard -"@storybook/theming@npm:6.5.12, @storybook/theming@npm:^6.0.0": - version: 6.5.12 - resolution: "@storybook/theming@npm:6.5.12" - dependencies: - "@storybook/client-logger": 6.5.12 - core-js: ^3.8.2 - memoizerific: ^1.11.3 - regenerator-runtime: ^0.13.7 - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: a982ebf88c7e1e21127febd17feebf26ac8d655f0c868bf110cbcaaef87eedb257300087618c525cb654808b590dc4b7b98dd6fec92fd76a040441d86c4b8289 - languageName: node - linkType: hard - -"@storybook/theming@npm:6.5.13": +"@storybook/theming@npm:6.5.13, @storybook/theming@npm:^6.0.0": version: 6.5.13 resolution: "@storybook/theming@npm:6.5.13" dependencies: @@ -3107,90 +2966,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-darwin-arm64@npm:1.3.16" +"@swc/core-darwin-arm64@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-darwin-arm64@npm:1.3.19" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-darwin-x64@npm:1.3.16" +"@swc/core-darwin-x64@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-darwin-x64@npm:1.3.19" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.16" +"@swc/core-linux-arm-gnueabihf@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.19" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.16" +"@swc/core-linux-arm64-gnu@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.19" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.16" +"@swc/core-linux-arm64-musl@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.19" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.16" +"@swc/core-linux-x64-gnu@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.19" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-x64-musl@npm:1.3.16" +"@swc/core-linux-x64-musl@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-x64-musl@npm:1.3.19" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.16" +"@swc/core-win32-arm64-msvc@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.19" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.16" +"@swc/core-win32-ia32-msvc@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.19" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.16" +"@swc/core-win32-x64-msvc@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.19" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.9": - version: 1.3.16 - resolution: "@swc/core@npm:1.3.16" + version: 1.3.19 + resolution: "@swc/core@npm:1.3.19" dependencies: - "@swc/core-darwin-arm64": 1.3.16 - "@swc/core-darwin-x64": 1.3.16 - "@swc/core-linux-arm-gnueabihf": 1.3.16 - "@swc/core-linux-arm64-gnu": 1.3.16 - "@swc/core-linux-arm64-musl": 1.3.16 - "@swc/core-linux-x64-gnu": 1.3.16 - "@swc/core-linux-x64-musl": 1.3.16 - "@swc/core-win32-arm64-msvc": 1.3.16 - "@swc/core-win32-ia32-msvc": 1.3.16 - "@swc/core-win32-x64-msvc": 1.3.16 + "@swc/core-darwin-arm64": 1.3.19 + "@swc/core-darwin-x64": 1.3.19 + "@swc/core-linux-arm-gnueabihf": 1.3.19 + "@swc/core-linux-arm64-gnu": 1.3.19 + "@swc/core-linux-arm64-musl": 1.3.19 + "@swc/core-linux-x64-gnu": 1.3.19 + "@swc/core-linux-x64-musl": 1.3.19 + "@swc/core-win32-arm64-msvc": 1.3.19 + "@swc/core-win32-ia32-msvc": 1.3.19 + "@swc/core-win32-x64-msvc": 1.3.19 dependenciesMeta: "@swc/core-darwin-arm64": optional: true @@ -3214,7 +3073,7 @@ __metadata: optional: true bin: swcx: run_swcx.js - checksum: 4361252c928c487a02f526aecd8f3072b923244234c2701916944cf13c252b6d5ce2466caf3e0797d3e92e71a89d4a044f8577e51e8f7fe3af0fe30d94e94b13 + checksum: 752499e18f81df789a9737936b2a83ea1db34bb2f983c80b394766796d853fa7d30701b52b9cb30ba943ad15286d8316603149683f4c553a5493aa78d89a76e0 languageName: node linkType: hard @@ -8409,16 +8268,7 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^3.0.0, minipass@npm:^3.1.1": - version: 3.3.4 - resolution: "minipass@npm:3.3.4" - dependencies: - yallist: ^4.0.0 - checksum: 5d95a7738c54852ba78d484141e850c792e062666a2d0c681a5ac1021275beb7e1acb077e59f9523ff1defb80901aea4e30fac10ded9a20a25d819a42916ef1b - languageName: node - linkType: hard - -"minipass@npm:^3.1.6": +"minipass@npm:^3.0.0, minipass@npm:^3.1.1, minipass@npm:^3.1.6": version: 3.3.5 resolution: "minipass@npm:3.3.5" dependencies: diff --git a/yarn.lock b/yarn.lock index b1531e5db9..36b4f60c60 100644 --- a/yarn.lock +++ b/yarn.lock @@ -373,647 +373,719 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/abort-controller@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/abort-controller@npm:3.183.0" +"@aws-sdk/abort-controller@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/abort-controller@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 28f5bc16b5aba414e354df603bc8becd010aa8c9b3c79e527130723a39f21d5a3df98c3bafb3cea733d415a5b042af90098ffa9dfa29d400114c2f6f5292524e + checksum: ba69d926f6939fe91d4d169c64106683fc467a0b1778d48ddae194f8abaf484a2c2e9f997b7de9d4bab9dcb30532551aec88c4e52aeed71f43eb7335bd159adc languageName: node linkType: hard "@aws-sdk/client-sqs@npm:^3.0.0": - version: 3.183.0 - resolution: "@aws-sdk/client-sqs@npm:3.183.0" + version: 3.213.0 + resolution: "@aws-sdk/client-sqs@npm:3.213.0" dependencies: "@aws-crypto/sha256-browser": 2.0.0 "@aws-crypto/sha256-js": 2.0.0 - "@aws-sdk/client-sts": 3.183.0 - "@aws-sdk/config-resolver": 3.183.0 - "@aws-sdk/credential-provider-node": 3.183.0 - "@aws-sdk/fetch-http-handler": 3.183.0 - "@aws-sdk/hash-node": 3.183.0 - "@aws-sdk/invalid-dependency": 3.183.0 - "@aws-sdk/md5-js": 3.183.0 - "@aws-sdk/middleware-content-length": 3.183.0 - "@aws-sdk/middleware-host-header": 3.183.0 - "@aws-sdk/middleware-logger": 3.183.0 - "@aws-sdk/middleware-recursion-detection": 3.183.0 - "@aws-sdk/middleware-retry": 3.183.0 - "@aws-sdk/middleware-sdk-sqs": 3.183.0 - "@aws-sdk/middleware-serde": 3.183.0 - "@aws-sdk/middleware-signing": 3.183.0 - "@aws-sdk/middleware-stack": 3.183.0 - "@aws-sdk/middleware-user-agent": 3.183.0 - "@aws-sdk/node-config-provider": 3.183.0 - "@aws-sdk/node-http-handler": 3.183.0 - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/smithy-client": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/url-parser": 3.183.0 - "@aws-sdk/util-base64-browser": 3.183.0 - "@aws-sdk/util-base64-node": 3.183.0 - "@aws-sdk/util-body-length-browser": 3.183.0 - "@aws-sdk/util-body-length-node": 3.183.0 - "@aws-sdk/util-defaults-mode-browser": 3.183.0 - "@aws-sdk/util-defaults-mode-node": 3.183.0 - "@aws-sdk/util-user-agent-browser": 3.183.0 - "@aws-sdk/util-user-agent-node": 3.183.0 - "@aws-sdk/util-utf8-browser": 3.183.0 - "@aws-sdk/util-utf8-node": 3.183.0 - entities: 2.2.0 - fast-xml-parser: 3.19.0 + "@aws-sdk/client-sts": 3.213.0 + "@aws-sdk/config-resolver": 3.212.0 + "@aws-sdk/credential-provider-node": 3.212.0 + "@aws-sdk/fetch-http-handler": 3.212.0 + "@aws-sdk/hash-node": 3.212.0 + "@aws-sdk/invalid-dependency": 3.212.0 + "@aws-sdk/md5-js": 3.212.0 + "@aws-sdk/middleware-content-length": 3.212.0 + "@aws-sdk/middleware-endpoint": 3.212.0 + "@aws-sdk/middleware-host-header": 3.212.0 + "@aws-sdk/middleware-logger": 3.212.0 + "@aws-sdk/middleware-recursion-detection": 3.212.0 + "@aws-sdk/middleware-retry": 3.212.0 + "@aws-sdk/middleware-sdk-sqs": 3.212.0 + "@aws-sdk/middleware-serde": 3.212.0 + "@aws-sdk/middleware-signing": 3.212.0 + "@aws-sdk/middleware-stack": 3.212.0 + "@aws-sdk/middleware-user-agent": 3.212.0 + "@aws-sdk/node-config-provider": 3.212.0 + "@aws-sdk/node-http-handler": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/smithy-client": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/url-parser": 3.212.0 + "@aws-sdk/util-base64": 3.208.0 + "@aws-sdk/util-body-length-browser": 3.188.0 + "@aws-sdk/util-body-length-node": 3.208.0 + "@aws-sdk/util-defaults-mode-browser": 3.212.0 + "@aws-sdk/util-defaults-mode-node": 3.212.0 + "@aws-sdk/util-endpoints": 3.212.0 + "@aws-sdk/util-user-agent-browser": 3.212.0 + "@aws-sdk/util-user-agent-node": 3.212.0 + "@aws-sdk/util-utf8-browser": 3.188.0 + "@aws-sdk/util-utf8-node": 3.208.0 + fast-xml-parser: 4.0.11 tslib: ^2.3.1 - checksum: fe0a7ba53cac4d52c25a4a4f1a1bb16438d5011347fcfec037df1d73379d750cd47b437f7a45aea2d4e29bc92587b51dd7631a06f5d6881e3b67b2d4c0dd2633 + checksum: baa9d4c816cae23874f14fe4c6c6da26b8d52b65d459167ebacbc3ec207ef2a83860f452df8b47e11231587e9b1d7a1aedc74051bb79473037756e4d6e492b36 languageName: node linkType: hard -"@aws-sdk/client-sso@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/client-sso@npm:3.183.0" +"@aws-sdk/client-sso-oidc@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/client-sso-oidc@npm:3.212.0" dependencies: "@aws-crypto/sha256-browser": 2.0.0 "@aws-crypto/sha256-js": 2.0.0 - "@aws-sdk/config-resolver": 3.183.0 - "@aws-sdk/fetch-http-handler": 3.183.0 - "@aws-sdk/hash-node": 3.183.0 - "@aws-sdk/invalid-dependency": 3.183.0 - "@aws-sdk/middleware-content-length": 3.183.0 - "@aws-sdk/middleware-host-header": 3.183.0 - "@aws-sdk/middleware-logger": 3.183.0 - "@aws-sdk/middleware-recursion-detection": 3.183.0 - "@aws-sdk/middleware-retry": 3.183.0 - "@aws-sdk/middleware-serde": 3.183.0 - "@aws-sdk/middleware-stack": 3.183.0 - "@aws-sdk/middleware-user-agent": 3.183.0 - "@aws-sdk/node-config-provider": 3.183.0 - "@aws-sdk/node-http-handler": 3.183.0 - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/smithy-client": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/url-parser": 3.183.0 - "@aws-sdk/util-base64-browser": 3.183.0 - "@aws-sdk/util-base64-node": 3.183.0 - "@aws-sdk/util-body-length-browser": 3.183.0 - "@aws-sdk/util-body-length-node": 3.183.0 - "@aws-sdk/util-defaults-mode-browser": 3.183.0 - "@aws-sdk/util-defaults-mode-node": 3.183.0 - "@aws-sdk/util-user-agent-browser": 3.183.0 - "@aws-sdk/util-user-agent-node": 3.183.0 - "@aws-sdk/util-utf8-browser": 3.183.0 - "@aws-sdk/util-utf8-node": 3.183.0 + "@aws-sdk/config-resolver": 3.212.0 + "@aws-sdk/fetch-http-handler": 3.212.0 + "@aws-sdk/hash-node": 3.212.0 + "@aws-sdk/invalid-dependency": 3.212.0 + "@aws-sdk/middleware-content-length": 3.212.0 + "@aws-sdk/middleware-endpoint": 3.212.0 + "@aws-sdk/middleware-host-header": 3.212.0 + "@aws-sdk/middleware-logger": 3.212.0 + "@aws-sdk/middleware-recursion-detection": 3.212.0 + "@aws-sdk/middleware-retry": 3.212.0 + "@aws-sdk/middleware-serde": 3.212.0 + "@aws-sdk/middleware-stack": 3.212.0 + "@aws-sdk/middleware-user-agent": 3.212.0 + "@aws-sdk/node-config-provider": 3.212.0 + "@aws-sdk/node-http-handler": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/smithy-client": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/url-parser": 3.212.0 + "@aws-sdk/util-base64": 3.208.0 + "@aws-sdk/util-body-length-browser": 3.188.0 + "@aws-sdk/util-body-length-node": 3.208.0 + "@aws-sdk/util-defaults-mode-browser": 3.212.0 + "@aws-sdk/util-defaults-mode-node": 3.212.0 + "@aws-sdk/util-endpoints": 3.212.0 + "@aws-sdk/util-user-agent-browser": 3.212.0 + "@aws-sdk/util-user-agent-node": 3.212.0 + "@aws-sdk/util-utf8-browser": 3.188.0 + "@aws-sdk/util-utf8-node": 3.208.0 tslib: ^2.3.1 - checksum: 6909329cf87c1a0c830fa9657e04c7e1ae496d7d79cc0a6813999503ae809f245f0b879af1d5564a5d97c2c69583c8a4df702d29ec7dc75934d98a0cdf017ad3 + checksum: 45f32b8c0a39d3d6788d5a359aff1bcd42c4ada3edb4d6ef072741a9128fc726090fb7e4b5770307f106eb6470f1a29c70843b628ef1652a944cd8dcbe4e8112 languageName: node linkType: hard -"@aws-sdk/client-sts@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/client-sts@npm:3.183.0" +"@aws-sdk/client-sso@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/client-sso@npm:3.212.0" dependencies: "@aws-crypto/sha256-browser": 2.0.0 "@aws-crypto/sha256-js": 2.0.0 - "@aws-sdk/config-resolver": 3.183.0 - "@aws-sdk/credential-provider-node": 3.183.0 - "@aws-sdk/fetch-http-handler": 3.183.0 - "@aws-sdk/hash-node": 3.183.0 - "@aws-sdk/invalid-dependency": 3.183.0 - "@aws-sdk/middleware-content-length": 3.183.0 - "@aws-sdk/middleware-host-header": 3.183.0 - "@aws-sdk/middleware-logger": 3.183.0 - "@aws-sdk/middleware-recursion-detection": 3.183.0 - "@aws-sdk/middleware-retry": 3.183.0 - "@aws-sdk/middleware-sdk-sts": 3.183.0 - "@aws-sdk/middleware-serde": 3.183.0 - "@aws-sdk/middleware-signing": 3.183.0 - "@aws-sdk/middleware-stack": 3.183.0 - "@aws-sdk/middleware-user-agent": 3.183.0 - "@aws-sdk/node-config-provider": 3.183.0 - "@aws-sdk/node-http-handler": 3.183.0 - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/smithy-client": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/url-parser": 3.183.0 - "@aws-sdk/util-base64-browser": 3.183.0 - "@aws-sdk/util-base64-node": 3.183.0 - "@aws-sdk/util-body-length-browser": 3.183.0 - "@aws-sdk/util-body-length-node": 3.183.0 - "@aws-sdk/util-defaults-mode-browser": 3.183.0 - "@aws-sdk/util-defaults-mode-node": 3.183.0 - "@aws-sdk/util-user-agent-browser": 3.183.0 - "@aws-sdk/util-user-agent-node": 3.183.0 - "@aws-sdk/util-utf8-browser": 3.183.0 - "@aws-sdk/util-utf8-node": 3.183.0 - entities: 2.2.0 - fast-xml-parser: 3.19.0 + "@aws-sdk/config-resolver": 3.212.0 + "@aws-sdk/fetch-http-handler": 3.212.0 + "@aws-sdk/hash-node": 3.212.0 + "@aws-sdk/invalid-dependency": 3.212.0 + "@aws-sdk/middleware-content-length": 3.212.0 + "@aws-sdk/middleware-endpoint": 3.212.0 + "@aws-sdk/middleware-host-header": 3.212.0 + "@aws-sdk/middleware-logger": 3.212.0 + "@aws-sdk/middleware-recursion-detection": 3.212.0 + "@aws-sdk/middleware-retry": 3.212.0 + "@aws-sdk/middleware-serde": 3.212.0 + "@aws-sdk/middleware-stack": 3.212.0 + "@aws-sdk/middleware-user-agent": 3.212.0 + "@aws-sdk/node-config-provider": 3.212.0 + "@aws-sdk/node-http-handler": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/smithy-client": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/url-parser": 3.212.0 + "@aws-sdk/util-base64": 3.208.0 + "@aws-sdk/util-body-length-browser": 3.188.0 + "@aws-sdk/util-body-length-node": 3.208.0 + "@aws-sdk/util-defaults-mode-browser": 3.212.0 + "@aws-sdk/util-defaults-mode-node": 3.212.0 + "@aws-sdk/util-endpoints": 3.212.0 + "@aws-sdk/util-user-agent-browser": 3.212.0 + "@aws-sdk/util-user-agent-node": 3.212.0 + "@aws-sdk/util-utf8-browser": 3.188.0 + "@aws-sdk/util-utf8-node": 3.208.0 tslib: ^2.3.1 - checksum: d4492e537803d64e5fc0c5db8ac3a3788204aadc14b21e4fb407188feb58b35689c4a6878b4f13123a5f35f7e9fa01d95d427be168aeec9072aea881f2a92fb7 + checksum: 14426e200171d3ee596f946744d8ee325b3f8fbfcfb3f266482142df20ee418752b64be1131b473cf0b60ed8615dab1cd9350f93a547dca34ca84c07c47fc697 languageName: node linkType: hard -"@aws-sdk/config-resolver@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/config-resolver@npm:3.183.0" +"@aws-sdk/client-sts@npm:3.213.0": + version: 3.213.0 + resolution: "@aws-sdk/client-sts@npm:3.213.0" dependencies: - "@aws-sdk/signature-v4": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-config-provider": 3.183.0 - "@aws-sdk/util-middleware": 3.183.0 + "@aws-crypto/sha256-browser": 2.0.0 + "@aws-crypto/sha256-js": 2.0.0 + "@aws-sdk/config-resolver": 3.212.0 + "@aws-sdk/credential-provider-node": 3.212.0 + "@aws-sdk/fetch-http-handler": 3.212.0 + "@aws-sdk/hash-node": 3.212.0 + "@aws-sdk/invalid-dependency": 3.212.0 + "@aws-sdk/middleware-content-length": 3.212.0 + "@aws-sdk/middleware-endpoint": 3.212.0 + "@aws-sdk/middleware-host-header": 3.212.0 + "@aws-sdk/middleware-logger": 3.212.0 + "@aws-sdk/middleware-recursion-detection": 3.212.0 + "@aws-sdk/middleware-retry": 3.212.0 + "@aws-sdk/middleware-sdk-sts": 3.212.0 + "@aws-sdk/middleware-serde": 3.212.0 + "@aws-sdk/middleware-signing": 3.212.0 + "@aws-sdk/middleware-stack": 3.212.0 + "@aws-sdk/middleware-user-agent": 3.212.0 + "@aws-sdk/node-config-provider": 3.212.0 + "@aws-sdk/node-http-handler": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/smithy-client": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/url-parser": 3.212.0 + "@aws-sdk/util-base64": 3.208.0 + "@aws-sdk/util-body-length-browser": 3.188.0 + "@aws-sdk/util-body-length-node": 3.208.0 + "@aws-sdk/util-defaults-mode-browser": 3.212.0 + "@aws-sdk/util-defaults-mode-node": 3.212.0 + "@aws-sdk/util-endpoints": 3.212.0 + "@aws-sdk/util-user-agent-browser": 3.212.0 + "@aws-sdk/util-user-agent-node": 3.212.0 + "@aws-sdk/util-utf8-browser": 3.188.0 + "@aws-sdk/util-utf8-node": 3.208.0 + fast-xml-parser: 4.0.11 tslib: ^2.3.1 - checksum: 1be0ab79d5019f7502a7aee8c740f4e381ab7501ed639569526a4a7e1c70ad6bb36d642a8a2b18afe58c34497c6f2f8244dbf05211a0f6bc9a57e5e379c70b2b + checksum: 6d375d8d22bdd08130ea624d6b980e366ce556b3178a4dbfcfd7ec7b9ab629dd57d34b350129231bd96be078778df0de6c61783c49a739142b055882f943f7d2 languageName: node linkType: hard -"@aws-sdk/credential-provider-env@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/credential-provider-env@npm:3.183.0" +"@aws-sdk/config-resolver@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/config-resolver@npm:3.212.0" dependencies: - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/signature-v4": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-config-provider": 3.208.0 + "@aws-sdk/util-middleware": 3.212.0 tslib: ^2.3.1 - checksum: 4281e14c286cc5c57c69ba88c49fe1c1ff61f27923db5e8f852a84cce02b40fdc938f876ad08e72005deae9a28fab7400f747f63ca50f9c4d60e04e6c2705205 + checksum: 2d2844b93ca27a60d311d6998582bba99ac922299db76309f6136625ffaeecba172b906dee2598c381aebd5c88ca5b5442e5dfdcaa932ddaaf88d7257953022a languageName: node linkType: hard -"@aws-sdk/credential-provider-imds@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/credential-provider-imds@npm:3.183.0" +"@aws-sdk/credential-provider-env@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/credential-provider-env@npm:3.212.0" dependencies: - "@aws-sdk/node-config-provider": 3.183.0 - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/url-parser": 3.183.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: e9fe1007b22825dc0c033beb2057b9f759d8cafec6e65d08ba4915bccb9b7d737eeccb3d06e9768a084b6726d9ea10778172b1bec695353c60276c1a5c21370e + checksum: da7af6765a551b9c55c56d5e1f287877839aec8cfbe301d4b483180a72f62113e15b87c8b4dbbe608e06db96bcad52b3becc9f02f3664607f0c477d03fef1bac languageName: node linkType: hard -"@aws-sdk/credential-provider-ini@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/credential-provider-ini@npm:3.183.0" +"@aws-sdk/credential-provider-imds@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/credential-provider-imds@npm:3.212.0" dependencies: - "@aws-sdk/credential-provider-env": 3.183.0 - "@aws-sdk/credential-provider-imds": 3.183.0 - "@aws-sdk/credential-provider-sso": 3.183.0 - "@aws-sdk/credential-provider-web-identity": 3.183.0 - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/shared-ini-file-loader": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/node-config-provider": 3.212.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/url-parser": 3.212.0 tslib: ^2.3.1 - checksum: c7784a3674c863dacc1305a08dd63cb16d1e7374250b0ceafa9feee8344de4228d7c32e84fbaa9d963adab69eb3938cd75b99554756da120cd9beae4693eded9 + checksum: 4b3c31bc88a1bf15d0721c7e124795361c6f532ee1b33400f2f71393d60ee5c14b0bf2b4e2abc6c3ef22aa8304aa19e5d9e3e733e17da454e3371615567440dc languageName: node linkType: hard -"@aws-sdk/credential-provider-node@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/credential-provider-node@npm:3.183.0" +"@aws-sdk/credential-provider-ini@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.212.0" dependencies: - "@aws-sdk/credential-provider-env": 3.183.0 - "@aws-sdk/credential-provider-imds": 3.183.0 - "@aws-sdk/credential-provider-ini": 3.183.0 - "@aws-sdk/credential-provider-process": 3.183.0 - "@aws-sdk/credential-provider-sso": 3.183.0 - "@aws-sdk/credential-provider-web-identity": 3.183.0 - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/shared-ini-file-loader": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/credential-provider-env": 3.212.0 + "@aws-sdk/credential-provider-imds": 3.212.0 + "@aws-sdk/credential-provider-sso": 3.212.0 + "@aws-sdk/credential-provider-web-identity": 3.212.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/shared-ini-file-loader": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: f23e1148054a96d68632284501ba01c4285cce1a2f4562817ec4b7fa315ff56e2f6b97fae0d2ae26fd52fca8c9f000be5d72a3582cff6c32ba10010f02a30977 + checksum: de60d4729ed6e162628563990b08be88486131c09ec372190a9b38faea72d8969d1e277b2e01a28fa3432c34e92bd39057fc6b923de9fcd0fcabd5b9ec69425e languageName: node linkType: hard -"@aws-sdk/credential-provider-process@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/credential-provider-process@npm:3.183.0" +"@aws-sdk/credential-provider-node@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.212.0" dependencies: - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/shared-ini-file-loader": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/credential-provider-env": 3.212.0 + "@aws-sdk/credential-provider-imds": 3.212.0 + "@aws-sdk/credential-provider-ini": 3.212.0 + "@aws-sdk/credential-provider-process": 3.212.0 + "@aws-sdk/credential-provider-sso": 3.212.0 + "@aws-sdk/credential-provider-web-identity": 3.212.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/shared-ini-file-loader": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 3109e79ec2b4647f5e7f78c6dc2e10cd8a34a70147e7e90549153b7ff3ff12eda3499b3189d6fc1f3b99f461d5445e8017b44cd50029d30b0fc7437ad88a5f38 + checksum: 421a65b57cfd280a14707c8563da285263a55471d1c3fbfe66305f0d42dcc59fb2884e84d380b8bcf2fea0ccdd419e92b5361b5b2a5832cf45ca0c86127a0137 languageName: node linkType: hard -"@aws-sdk/credential-provider-sso@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/credential-provider-sso@npm:3.183.0" +"@aws-sdk/credential-provider-process@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/credential-provider-process@npm:3.212.0" dependencies: - "@aws-sdk/client-sso": 3.183.0 - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/shared-ini-file-loader": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/shared-ini-file-loader": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 8775894ef50499361a4b49d846417c2c1e1ea90383730a32428154628358e7847f6f29dddd3cd04d3a4e15c1343fe10b051a92863055989ac3c4cb241e68f63a + checksum: 4842577d8c7b2daa80db20f5a21e56a42a57de9c5d27c50806a2c76e5b0eb0fa9d6b5aed9dcae1d0820ce410999e139f81b0440d33dc104cbc357a3f59680d8f languageName: node linkType: hard -"@aws-sdk/credential-provider-web-identity@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/credential-provider-web-identity@npm:3.183.0" +"@aws-sdk/credential-provider-sso@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.212.0" dependencies: - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/client-sso": 3.212.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/shared-ini-file-loader": 3.212.0 + "@aws-sdk/token-providers": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: c39576c8f35137284fc69db8d94adcf07fa89d51153ee9710481262e205d485ff089a796e8a253aa14a0434dde91eadb65602fc614167a4f14301201ee840c08 + checksum: b575af71e26343536997261ac97820afa697b789b152106f64fea1e24fe6a21437a195f46a23828b92dbd82a32d7c4339d834a0aa52dd2e075974363c1f8837f languageName: node linkType: hard -"@aws-sdk/fetch-http-handler@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/fetch-http-handler@npm:3.183.0" +"@aws-sdk/credential-provider-web-identity@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/credential-provider-web-identity@npm:3.212.0" dependencies: - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/querystring-builder": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-base64-browser": 3.183.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 87c51d1dad8c469e32b99f6ed3729e753097cd4a9f8f417ec8681cf01e18b9a5dc6571f3cd1220293216bd4527b8499bb2ea1f821a240fde6d9560817c120066 + checksum: 1bef8b133fed07802e82406d3e46d7bdd110197b1416debd8953f7192814ebf2a6300520adfe62a9b68bc84fab70fd97fd3f4126855c297b8bbb947ce97d4184 languageName: node linkType: hard -"@aws-sdk/hash-node@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/hash-node@npm:3.183.0" +"@aws-sdk/fetch-http-handler@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/fetch-http-handler@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-buffer-from": 3.183.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/querystring-builder": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-base64": 3.208.0 tslib: ^2.3.1 - checksum: fbed8757df633620807594535e3bb7d387576e0c2f89ad16dc7c98405a9d1d9743bea50148186de2f19cac67144b30f7f89e6c15317ddd7b8c0c8f61ca9556ef + checksum: 477601560df9a58c8e6bb50cdc313205fc75681699275f153e96ce6c62cf1c55ec8c394bb4774ba69a483987094b7416aa8e3fb5c89cdd530adb25e1cd909cb9 languageName: node linkType: hard -"@aws-sdk/invalid-dependency@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/invalid-dependency@npm:3.183.0" +"@aws-sdk/hash-node@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/hash-node@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-buffer-from": 3.208.0 tslib: ^2.3.1 - checksum: d928ca26d4390ad4f5e2c8198d16e14b675face6ad8c4a4eef699b54ce3f6cba584456659287b0e57e9f9019bc456b51dfa474671e11bf555fe04ed16174c10c + checksum: f5855ff20e37a0779da515fa78f26413762a5246c1b06a4e35f11f7aa4fd742a639a2b3d00f63004a5de659f233c5d503b469a744bb6911f9159602a79676789 languageName: node linkType: hard -"@aws-sdk/is-array-buffer@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/is-array-buffer@npm:3.183.0" +"@aws-sdk/invalid-dependency@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/invalid-dependency@npm:3.212.0" dependencies: + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 717e508989821434a2ace4f690fc599f67b3f28ee9de5f06fce65a296fe11a68a5b97f30d66bfbf490d4a83259e4ffa1f0321231e4a54ce2440a9c6fdbacde24 + checksum: c71f32895f2a7760a916d8b8abba8b3d2c40869422d083da13abb096931ed04d67a65695f024ce74efced33745a10c9f904779f1414d2fc9a80be3d1d95284f9 languageName: node linkType: hard -"@aws-sdk/md5-js@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/md5-js@npm:3.183.0" +"@aws-sdk/is-array-buffer@npm:3.201.0": + version: 3.201.0 + resolution: "@aws-sdk/is-array-buffer@npm:3.201.0" dependencies: - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-utf8-browser": 3.183.0 - "@aws-sdk/util-utf8-node": 3.183.0 tslib: ^2.3.1 - checksum: 5bd92465940ace6b3ca13342bd60eab42ffd5d74dfa65655c28e10bbf4aa0c32b2f9b4deb2050f3d092b8586e558bf248559af9c3cffb37810961169ad0797af + checksum: 295450b417a9ab0b734050afff6c53aaed8a33dccd3ede60bf67fdec21f675d14ab8edc24f4e1d12aa4e99f9ccaf794aaaaff270c296c1ee38f73ea7ba7f59ce languageName: node linkType: hard -"@aws-sdk/middleware-content-length@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-content-length@npm:3.183.0" +"@aws-sdk/md5-js@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/md5-js@npm:3.212.0" dependencies: - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-utf8-browser": 3.188.0 + "@aws-sdk/util-utf8-node": 3.208.0 tslib: ^2.3.1 - checksum: bf3ec6872a925dbafd91e57334b6264e8446ca59eba598ee4bc7b37113c466300ac6b77ab314c4aa2859221f1f4e98beabbd365346e0815d591f276e642d1849 + checksum: d49b7eb9b3b78bbc78c247565041ea4119a6c82765e5b989a716d31def4da4de69f5dcbaf1ed32a89f23a1484e233a51a73f90da877952510250ca0a2c7311a1 languageName: node linkType: hard -"@aws-sdk/middleware-host-header@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-host-header@npm:3.183.0" +"@aws-sdk/middleware-content-length@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-content-length@npm:3.212.0" dependencies: - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: b4c4886976cffec614ff368daa995a69c765e737c8d366daa0033d781cf8e7483565bb3627ecd752698edc23dcf578de6657c9e8cb41c0fcd92631d17b7749b8 + checksum: ef6a5467b84205b94ee90761d72cf14d52d49392086fb6358e07940b062a9f6e514b6649a9e0e87f0b3305cf54f5d85b712f394843a4417c590f630abeba2cdd languageName: node linkType: hard -"@aws-sdk/middleware-logger@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-logger@npm:3.183.0" +"@aws-sdk/middleware-endpoint@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-endpoint@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/middleware-serde": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/signature-v4": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/url-parser": 3.212.0 + "@aws-sdk/util-config-provider": 3.208.0 + "@aws-sdk/util-middleware": 3.212.0 tslib: ^2.3.1 - checksum: 10ffb6af0f81b0144fef91af2bbeaeb680df2beff838f2a6ebcc260971fd6252a166e3bf3501c1fc00f6f8f203117ff6e2b4baebb7a6d8cd8504b7e1f392bd6d + checksum: 0b34bd25dfaf05d009ba327abb00916c1a5ffa9c58b1baecaee120468ddf3aa4b7998b8b1ae33fca789494fe95a3bc0921050d667da396552239d542ae030675 languageName: node linkType: hard -"@aws-sdk/middleware-recursion-detection@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-recursion-detection@npm:3.183.0" +"@aws-sdk/middleware-host-header@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-host-header@npm:3.212.0" dependencies: - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 461117e0dc8c38750942035a07163817bc8e89f377211f5fb52d4314acae4e7e6cb49be04193c508112833535b33814c1609196cb65e38cff1122043398ad89e + checksum: c852de97a8763452b31136fc7ece3ba3026603665204a2982781a600139e346f08ca146331735d377b1f04a662bae9d44b78deef797d5bbee366f56ee91d7bc6 languageName: node linkType: hard -"@aws-sdk/middleware-retry@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-retry@npm:3.183.0" +"@aws-sdk/middleware-logger@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-logger@npm:3.212.0" dependencies: - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/service-error-classification": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-middleware": 3.183.0 + "@aws-sdk/types": 3.212.0 + tslib: ^2.3.1 + checksum: 1bc12d88da134e6cfa9ecf102c7a80727f1f00e09e97f06d44f4a6d1da21711a7abf9f0bdf39ab789736925f97f781574cb1e49f275f7ea1112fc0db443753f2 + languageName: node + linkType: hard + +"@aws-sdk/middleware-recursion-detection@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-recursion-detection@npm:3.212.0" + dependencies: + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/types": 3.212.0 + tslib: ^2.3.1 + checksum: 65cb31460dfd88a5997764d84e23003264c639b7848ad9398f9f362a7214ef175431e833dff4bf501bb81cb9b695e146c042b02986dfa6fab8812058bca7f9c7 + languageName: node + linkType: hard + +"@aws-sdk/middleware-retry@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-retry@npm:3.212.0" + dependencies: + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/service-error-classification": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-middleware": 3.212.0 tslib: ^2.3.1 uuid: ^8.3.2 - checksum: 76fa3f9fe5f1c67bbfa792611289edf0afa2d0271073039931ac9ea46b3f432d74692d2db55443f40b7e97014898454f6d4bd8d2f2f5714a4281428b7464efac + checksum: 1e221bf49585d9fea675f83d8b5d18f4b20de5b4096cc94dc51d265bdd579aa205b11a0735954dc9d7990f33fbe1165304342480ab15b79431d7988b74efcf05 languageName: node linkType: hard -"@aws-sdk/middleware-sdk-sqs@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.183.0" +"@aws-sdk/middleware-sdk-sqs@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-hex-encoding": 3.183.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-hex-encoding": 3.201.0 tslib: ^2.3.1 - checksum: 0bb914b2e7a5391f5b22a5262a5c299fa7e3eb22076d00c4cfffb449e97404b4830a77bb742e8f3110d66f15ae9aad81ea740238799cc35bb8d328eaf13f3fe2 + checksum: 12ab5d80d8025a737aa3e5c7f8025bbf824fe2719618356b011fb0901135828f0ee10eb15b7bf0ad81e4de3159c8a6bba97c3b3cfc186e26bd6dbefab16682a5 languageName: node linkType: hard -"@aws-sdk/middleware-sdk-sts@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-sdk-sts@npm:3.183.0" +"@aws-sdk/middleware-sdk-sts@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-sdk-sts@npm:3.212.0" dependencies: - "@aws-sdk/middleware-signing": 3.183.0 - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/signature-v4": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/middleware-signing": 3.212.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/signature-v4": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 1969d6128d24cc7d8ff199c2900d805281928668f4dc3ffd128065ccfe515f9f7467a265af276592009bd285847cb9298f8ba4b1dfbe33971c3e7de80986b422 + checksum: af3a03802e07ed1375a6d7feb6a9abbec8cba175138c49b8fb4d42843330db65e550a505539765a1f65944a3a4a840259b503d9e4a28f210ea83c0f2683e5e27 languageName: node linkType: hard -"@aws-sdk/middleware-serde@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-serde@npm:3.183.0" +"@aws-sdk/middleware-serde@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-serde@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 7495bb2adceab2c686588697fd9dd50a859706a8d33d98d9907ffc8f9ef78af141f2654cdac8d7b28b1f0544dcf2e52f25d109f5da1348102234ab5b4e79b256 + checksum: 1d9438e1e3a177a30d39787a6493f15a9726dd820552a995ca771ee58df004f6575ed322fd59658a7478c4fe72dfaf40bb61b1fd441a6ec1a245370e4ef17da9 languageName: node linkType: hard -"@aws-sdk/middleware-signing@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-signing@npm:3.183.0" +"@aws-sdk/middleware-signing@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-signing@npm:3.212.0" dependencies: - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/signature-v4": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-middleware": 3.183.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/signature-v4": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-middleware": 3.212.0 tslib: ^2.3.1 - checksum: 8025499acfefc072511adbc5084a1ed3cfe9cd9e514c58045425c87538af72ad3adfc134f7d0a059ff52832da392b4679b62d4ad28e477b866a20e78370f0758 + checksum: f9e2c07a9e80bc67ea05766f505c750652a2d355eb98a2f2296707a3af15bb2d591e8257a9aa3683721c47814e49fd3b266746d95ea8cb362eaaf8ab4afba08d languageName: node linkType: hard -"@aws-sdk/middleware-stack@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-stack@npm:3.183.0" +"@aws-sdk/middleware-stack@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-stack@npm:3.212.0" dependencies: tslib: ^2.3.1 - checksum: 03136847b96b0e4baba9b64f53a6c6b5738b1fa2c645edb3f47c1f85082791549b423ea791af33e9da3507cd56fbcbd052f1ba773a9f45ea42a3647277e85264 + checksum: b9ce8fdae58f68f259aea823e39f22b8f893402bad25d4c87f614e0dc14412d66bc3c08607190856b9122f2d431f94a78dd8ddec7700f5afe8d956ff12913a00 languageName: node linkType: hard -"@aws-sdk/middleware-user-agent@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-user-agent@npm:3.183.0" +"@aws-sdk/middleware-user-agent@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.212.0" dependencies: - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 8d1001d6d30f0f56aa440c541f6dcee9aad9190837e7a9653aef35448ff13e644cb7a441373192134ac86b5741fa64a120add07202cb97674018bd44612acdf7 + checksum: ef18b7b7755190e864946156dd741f7b513efaea160f4a4e2e2088e609d51660bd5dc577fb3e78bebc6105741b3b6e66a06bc77c0e027f4096fca90b918cb8b0 languageName: node linkType: hard -"@aws-sdk/node-config-provider@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/node-config-provider@npm:3.183.0" +"@aws-sdk/node-config-provider@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/node-config-provider@npm:3.212.0" dependencies: - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/shared-ini-file-loader": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/shared-ini-file-loader": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 673fb105de8d11ddd1c3e636c7ed01ef4fbe412a7d96543d9db69149dfad64d33adf5b96b106763c058190841ea497f220e1b75ca438dbda7b34729f00b9e823 + checksum: c8e742c225ea4803d0f3318de8d93f756e5008e76ecc2d88fcb80e6733b29984b40d39a47a08d51fa23c00b0dbf3f0bd3b60eb44768abb48466a2493a61195ed languageName: node linkType: hard -"@aws-sdk/node-http-handler@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/node-http-handler@npm:3.183.0" +"@aws-sdk/node-http-handler@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/node-http-handler@npm:3.212.0" dependencies: - "@aws-sdk/abort-controller": 3.183.0 - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/querystring-builder": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/abort-controller": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/querystring-builder": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: a24ee8cc4e3b63fdec0ebb671caa1e634af488befa7c93a28bb733bb532934c221f6c47707db20264af91ca84336a50b98427b425329b89aeac2a6d2be9537ff + checksum: 052e4a3854823bd3877fc2baeca8fae9cdb2083beb96b4734f08eba70cb73c7abaf5dc6487d5ace0757257debc56f656d9da247d227669196d3e20a1fa97563d languageName: node linkType: hard -"@aws-sdk/property-provider@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/property-provider@npm:3.183.0" +"@aws-sdk/property-provider@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/property-provider@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 6e8616e5f7efa9f46bdf62118dfc586e61dcdf0bc99f82305759020ce3d25fc1d2db71c62cfd4417a757a138b1a539cf305b584e6e7ade9bce9975fbe66fd326 + checksum: ac30dd5777f2cce6aaa9c3ac2672bada2f8423330c545ea66913d731b6e0f48a802b9f15c48ccb7620dd0f9e8c4c3a9d27bd445a7c9d72bb30115c1954207234 languageName: node linkType: hard -"@aws-sdk/protocol-http@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/protocol-http@npm:3.183.0" +"@aws-sdk/protocol-http@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/protocol-http@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 13b255bcb37b70853d93374f972d743c80f33ac16188c41b512536c2dbec4d7beb55226bfac6d809981d63bbc87f6bf345334238f187da2faa9feaef5001e0a7 + checksum: 6330d3259a560e952202ae9b964c210ab1b25b9161d4fc27da1bc081600abe2461854659a3b560764c8e2d1e8c1212bcc061250945f1f9c0ee3a19475fb17464 languageName: node linkType: hard -"@aws-sdk/querystring-builder@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/querystring-builder@npm:3.183.0" +"@aws-sdk/querystring-builder@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/querystring-builder@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-uri-escape": 3.183.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-uri-escape": 3.201.0 tslib: ^2.3.1 - checksum: a3de19863ccee4e8c9d5bbd719688ee1ffea02c3d90aad5e9c3eb04bed942ff8e5be0bfbc21ed03a968481abe2a2b1093e93b986b4111c468a9f3d3466b61f83 + checksum: 0bbd748f548c695d040b9ed225a2eb3fbba65b767acdba8dd8e157988b4ea66d22b994721d6eb5afcbfef7c49b105ab3a779534f4b2ae7e0d266f84159511e23 languageName: node linkType: hard -"@aws-sdk/querystring-parser@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/querystring-parser@npm:3.183.0" +"@aws-sdk/querystring-parser@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/querystring-parser@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: f05a03c46317c97f1b775f7c5be19365cf464396edc6b23c93efd5289ff1a8658883f7c9dde2cc7b74bb42b57f257bc212abae5d62100d23cdec5409fbffe468 + checksum: ab73e0d3a4fd94cc5141fe65940ee700bc6d3497f68518687c196b415cac5f7e44fd71039f6b7abb6eafb7777fe85b7d8f7cb006238b8deaf1248af2a53d7e29 languageName: node linkType: hard -"@aws-sdk/service-error-classification@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/service-error-classification@npm:3.183.0" - checksum: 8c015163aee9ec4d789daa8605e5b9424dfda81d3f609a2c34d800cb36ee78f4a3af02d1738c7e75f9a62cd1ef8ec4307f77457eecba3de4c6565a981484bca4 +"@aws-sdk/service-error-classification@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/service-error-classification@npm:3.212.0" + checksum: 844f149d4a03c72ffa77103c8ce12b58ecb20484350633e88242e8d16cde50e3f35ba6db34ea7e180685d4545e0f48367ab8ce86d76dfb80e5d2a959749556be languageName: node linkType: hard -"@aws-sdk/shared-ini-file-loader@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/shared-ini-file-loader@npm:3.183.0" +"@aws-sdk/shared-ini-file-loader@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/shared-ini-file-loader@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 317fd0d8012559754f82b88fb386d2a01c19be1d3a0b6da3a6436f71701ed8cd8fbe3f5aa869fd7bbfb8dfb148cd9de02aa68e93001200c76ba9e0691e135773 + checksum: 2c4f78b16338090d86ddc701f7ee13974387762b5f9c82b573b405a6e08db9bb979ac27809ac321cd8c33a3392c1576860475a47470cd59a1cb6092889d6ac17 languageName: node linkType: hard -"@aws-sdk/signature-v4@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/signature-v4@npm:3.183.0" +"@aws-sdk/signature-v4@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/signature-v4@npm:3.212.0" dependencies: - "@aws-sdk/is-array-buffer": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-hex-encoding": 3.183.0 - "@aws-sdk/util-middleware": 3.183.0 - "@aws-sdk/util-uri-escape": 3.183.0 + "@aws-sdk/is-array-buffer": 3.201.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-hex-encoding": 3.201.0 + "@aws-sdk/util-middleware": 3.212.0 + "@aws-sdk/util-uri-escape": 3.201.0 tslib: ^2.3.1 - checksum: 6639a4925b194171907fc1cf61101470836f5a8fa363f3fcd7b70bbaa4c756f43e13ad93b575885218a68192c375295d359a7e91db370816bd8f39f9c158ba36 + checksum: dcbdb41fcb53c3bb890c26175716c9edb1da1450333a88c888742cd71b9d47a4d8bd1ce0c183115c5971ce3b152ea420ffd36ab6be6dd8e4ea285d66259a0ae5 languageName: node linkType: hard -"@aws-sdk/smithy-client@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/smithy-client@npm:3.183.0" +"@aws-sdk/smithy-client@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/smithy-client@npm:3.212.0" dependencies: - "@aws-sdk/middleware-stack": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/middleware-stack": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 70866adfd91fb35721bcd8667d9a4638e306de6ddc22be37a5b3ea19dfc2a1404a7325992f7f56d635a3bfd3b0ffa159d32f17df35c47ada4724ad8d5bb8d1ce + checksum: ecaa51cd72e0ab8f92f3989ba7dd5639082ef1db1832b155adc1d6e97d15d7f67ff7d27af165a487d11e862e84010b60e4744561925146dbe1d7307a7f50b00b languageName: node linkType: hard -"@aws-sdk/types@npm:3.183.0, @aws-sdk/types@npm:^3.1.0, @aws-sdk/types@npm:^3.110.0": - version: 3.183.0 - resolution: "@aws-sdk/types@npm:3.183.0" - checksum: ab6e888ef8f6d5f5c047dbe0899ac94ad4aa83b9ae1b1fd7a3b88eacec61ab912f215fda65c71f783418d569d1088641806f6b98c9a28ddae04cc70d0eeccea7 - languageName: node - linkType: hard - -"@aws-sdk/url-parser@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/url-parser@npm:3.183.0" +"@aws-sdk/token-providers@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/token-providers@npm:3.212.0" dependencies: - "@aws-sdk/querystring-parser": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/client-sso-oidc": 3.212.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/shared-ini-file-loader": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 54c99d47e927368db12b6aa1fe4331a27461502355e0c18e99d8e05d9a114529d0b15965ff885cae8e19e49692ad44bf3c323ffdc535d7129deac900f286d1d5 + checksum: be11407716a1f2891b64201475c085cfb8236df0075c22f3381c3e7ed9abe0f231017826935afd76c6b41ec1907d24e3c5e1e4537fd4c0d9c743ca1e6d127768 languageName: node linkType: hard -"@aws-sdk/util-base64-browser@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-base64-browser@npm:3.183.0" +"@aws-sdk/types@npm:3.212.0, @aws-sdk/types@npm:^3.1.0, @aws-sdk/types@npm:^3.110.0": + version: 3.212.0 + resolution: "@aws-sdk/types@npm:3.212.0" + checksum: ba085aa2f70930adc250b4d142563cf886b9cbd98c011abe9d1d8dbad8b9cc8558a54d34bbeabed62a1a958a871c8d9d6cdaf43d57c31af8fc03a35a93ae5fb4 + languageName: node + linkType: hard + +"@aws-sdk/url-parser@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/url-parser@npm:3.212.0" + dependencies: + "@aws-sdk/querystring-parser": 3.212.0 + "@aws-sdk/types": 3.212.0 + tslib: ^2.3.1 + checksum: cd1c837a255d38a84d453226bab8af749a5a1c7075f70eb80124cc92c3b7301c3bc11eec624ee0d68b3daa68162676eabccfae7d291f0e3ad916c735b531cf87 + languageName: node + linkType: hard + +"@aws-sdk/util-base64@npm:3.208.0": + version: 3.208.0 + resolution: "@aws-sdk/util-base64@npm:3.208.0" + dependencies: + "@aws-sdk/util-buffer-from": 3.208.0 + tslib: ^2.3.1 + checksum: 2ccab3453a3a3636f3f1397441574b3adb984e1ba3865030393108327ed7304cf80c9b31d69691e6aba85cfe6a611a881bbb724e544324240763bb4e96630ed9 + languageName: node + linkType: hard + +"@aws-sdk/util-body-length-browser@npm:3.188.0": + version: 3.188.0 + resolution: "@aws-sdk/util-body-length-browser@npm:3.188.0" dependencies: tslib: ^2.3.1 - checksum: 2c3a4dc7e285b146285ac23de155de5e885d96aa46b29f9c830b6f12541032c6a42be7fd412ec019ddb83e6f155f561d04065b99159515d26d28c0968965dcf2 + checksum: 1b08bd1e63ec843ee336f51d894c49bf3c4c2f96e50d1711a12f7d0c5b6f7a15b490e366fec55b63e77036002994bac12927b29de2eb9ac91e4f152b1af78e58 languageName: node linkType: hard -"@aws-sdk/util-base64-node@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-base64-node@npm:3.183.0" - dependencies: - "@aws-sdk/util-buffer-from": 3.183.0 - tslib: ^2.3.1 - checksum: d8d0330aa768f816411a7b2343c500728501775d2f8d7c121d52d8484cbdd938102f3049e69e28574c838416ead0cb742494140609287b8d462bfb041218600d - languageName: node - linkType: hard - -"@aws-sdk/util-body-length-browser@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-body-length-browser@npm:3.183.0" +"@aws-sdk/util-body-length-node@npm:3.208.0": + version: 3.208.0 + resolution: "@aws-sdk/util-body-length-node@npm:3.208.0" dependencies: tslib: ^2.3.1 - checksum: 294074e20e4eb46ea992fc9d50fac0b97875d1d09efa706d5298e34f4200226c90bb33bbe96c41c7e91a43fbe44e6cfc0654d09fcdb823fe579545ada65701d9 + checksum: 986b42b358656dec4e75c231213331c4f01785f9ab17c8b87b6e268b6880818a96117f1785cef9786e6c0f7e2c1332c80e8388a43bfd83e8c7224ad059a72733 languageName: node linkType: hard -"@aws-sdk/util-body-length-node@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-body-length-node@npm:3.183.0" +"@aws-sdk/util-buffer-from@npm:3.208.0": + version: 3.208.0 + resolution: "@aws-sdk/util-buffer-from@npm:3.208.0" + dependencies: + "@aws-sdk/is-array-buffer": 3.201.0 + tslib: ^2.3.1 + checksum: 00bfa4d4494d3a1eb128e19104994d1aca8b3802e9aa218cecafb1ed3ff2ecf5c946485e06aa97ae312458842b0f31a6484dc945232f7cb0e357ba341cb2e53e + languageName: node + linkType: hard + +"@aws-sdk/util-config-provider@npm:3.208.0": + version: 3.208.0 + resolution: "@aws-sdk/util-config-provider@npm:3.208.0" dependencies: tslib: ^2.3.1 - checksum: 60697959c63731d6b4b334bed91d08c4e38e09c0ad95000509641370249c76c5df1ffa0a7cab7418d250a526f49e660f0c67cea28547e442ac47feb7623413b8 + checksum: 97b0414b120b4eb53001f3ab2135ee94937e47bd7bd0d0de7c6a7e00a282eaa78cd84be2bfd3e389340f0c0b2f7ba60da9a403f084721970ee55b779ecf7a451 languageName: node linkType: hard -"@aws-sdk/util-buffer-from@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-buffer-from@npm:3.183.0" +"@aws-sdk/util-defaults-mode-browser@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/util-defaults-mode-browser@npm:3.212.0" dependencies: - "@aws-sdk/is-array-buffer": 3.183.0 - tslib: ^2.3.1 - checksum: 5f16145cf68b0756f0930990cfba57ac343c83c6e9dc48bc8d4f488cd66548a9e186e702fa94ba33b69d852c2d019eedf4116ac676cf0dfb701081e39e0776dd - languageName: node - linkType: hard - -"@aws-sdk/util-config-provider@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-config-provider@npm:3.183.0" - dependencies: - tslib: ^2.3.1 - checksum: 8af6bf5ba31ec0a2fcf994f0a13bde5ff948bcdba02d153cdeea2105ea5eaf934368456bc92f35bfa42a7475cd4d2d2b9a644dfb8b4c2670ae2f103c045dc8b4 - languageName: node - linkType: hard - -"@aws-sdk/util-defaults-mode-browser@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-defaults-mode-browser@npm:3.183.0" - dependencies: - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/types": 3.212.0 bowser: ^2.11.0 tslib: ^2.3.1 - checksum: 8ff54acf1684dd9d82ca13a522c5d2d7ee305511a2748f6e8ef7dc43e668798269e51455ac81b905a5b000a704fd5536f178c84425b8c431500e6a8dc1418135 + checksum: 75fd275426a1708b0d353e497951a78d8b36297aa644342f063fcf26e321e6387944fe3d60beaa4d5fc40fa877b14277db0368a9d6120e6b83d56c84972c36b3 languageName: node linkType: hard -"@aws-sdk/util-defaults-mode-node@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-defaults-mode-node@npm:3.183.0" +"@aws-sdk/util-defaults-mode-node@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/util-defaults-mode-node@npm:3.212.0" dependencies: - "@aws-sdk/config-resolver": 3.183.0 - "@aws-sdk/credential-provider-imds": 3.183.0 - "@aws-sdk/node-config-provider": 3.183.0 - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/config-resolver": 3.212.0 + "@aws-sdk/credential-provider-imds": 3.212.0 + "@aws-sdk/node-config-provider": 3.212.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 40665d251844c4add23e358d9171add7180e0f1fcd4bd99601f4bdd03daa2259fed2567fa200152e14c4d7b1acc1126997f5b4e74c569ee1032f9fee16e6b582 + checksum: ab2151cce3561378c3f07d78a898cdd627d097cc388785fe9e0df6f836f265bde98307ce800faaf8832a3723eff2258c00db2cc523425713b67c75ddc941b29e languageName: node linkType: hard -"@aws-sdk/util-hex-encoding@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-hex-encoding@npm:3.183.0" +"@aws-sdk/util-endpoints@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/util-endpoints@npm:3.212.0" + dependencies: + "@aws-sdk/types": 3.212.0 + tslib: ^2.3.1 + checksum: 6c1d9e47dee896e35b5a2b11c0c028d71d16ab707929901403997d30ca639a202ed0f9c19d7ff1c5affa2966813226925608c15695d0d540a593294e40afc4ef + languageName: node + linkType: hard + +"@aws-sdk/util-hex-encoding@npm:3.201.0": + version: 3.201.0 + resolution: "@aws-sdk/util-hex-encoding@npm:3.201.0" dependencies: tslib: ^2.3.1 - checksum: 1bf9577d96d01c13a3118e5d767c1e16b1f4f1c574168d78e128e0342edac30c87776340e9e14deb1e9adf539a6b2f953338adabc097fb143e870889698a6308 + checksum: a27f3365dfb1e6ece79ea34fd6e2c4540eb0084536d7300ff0ff42a7334ddf07f21958c6cfd0bbeb71361ee408e16deae2c82b7c7378b048b8e81a52c75f190a languageName: node linkType: hard @@ -1026,67 +1098,67 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-middleware@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-middleware@npm:3.183.0" +"@aws-sdk/util-middleware@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/util-middleware@npm:3.212.0" dependencies: tslib: ^2.3.1 - checksum: e1a433aebae5e74b365ecb4a57e3c583e7b89697f24b0c9edde93b8ba358e92256550329efb7c6924c0a5037e1bad2b7a1c8ed1b6d02e1aaaa35bca502923752 + checksum: d4bd527193fef871b480636aa99ab7b71a576fc2659e0a12f2ed14e48464c5267a4c6a24bb140ba88cd8205f224326479a548dc63de24b1b9b43ff3098041f7c languageName: node linkType: hard -"@aws-sdk/util-uri-escape@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-uri-escape@npm:3.183.0" +"@aws-sdk/util-uri-escape@npm:3.201.0": + version: 3.201.0 + resolution: "@aws-sdk/util-uri-escape@npm:3.201.0" dependencies: tslib: ^2.3.1 - checksum: 9d5f30821cdad15ed6912f7406f83b5b5a45358b208a9374ea944313d9853da8f4ec9881aefa5d6fe78caeaefbb0d103c8485a32f7c25e8b2cae193ea2233bb8 + checksum: 8bd751459eaab75a9b61801f3484cfa5c4e0133381ace6ec901cb9b92b1fee99beb4ef9c0f87ade59425a882ed3a280255d9b2fd8da6a6286e49efb9af8f0d55 languageName: node linkType: hard -"@aws-sdk/util-user-agent-browser@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-user-agent-browser@npm:3.183.0" +"@aws-sdk/util-user-agent-browser@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/util-user-agent-browser@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 bowser: ^2.11.0 tslib: ^2.3.1 - checksum: 21349881a4069a09fb0f8c0f5a6d69e6659255008861817c5d9dada61bf032f0bd7d5735f01dbddbc310d228ea843fbec6ada9f83a4197909efd6ebadb364f6b + checksum: e55abbde07b550ff6b061376b00839ffb184e4e3238c307ce85bcf44166941cf74fc59fbd97c2e6923923b88eb5df5bbad482048271b91ca3b46e6988e85be7b languageName: node linkType: hard -"@aws-sdk/util-user-agent-node@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-user-agent-node@npm:3.183.0" +"@aws-sdk/util-user-agent-node@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.212.0" dependencies: - "@aws-sdk/node-config-provider": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/node-config-provider": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 peerDependencies: aws-crt: ">=1.0.0" peerDependenciesMeta: aws-crt: optional: true - checksum: 727d46842053f8f19789fee1550e235adff399559ef6c9b8dd0fe9edd7ac629a9b5c5849377553bfff37deba1ba9889d0c313f059f27cfc40a33895990c1f894 + checksum: 6d5f0bb7f66c9135c23f6db5f6d58122db130d4b4261278f11a6db627057e03c76e237a98d91eef05aa328202f55e3fb7f70eba690640df025dfa07f948bf678 languageName: node linkType: hard -"@aws-sdk/util-utf8-browser@npm:3.183.0, @aws-sdk/util-utf8-browser@npm:^3.0.0": - version: 3.183.0 - resolution: "@aws-sdk/util-utf8-browser@npm:3.183.0" +"@aws-sdk/util-utf8-browser@npm:3.188.0, @aws-sdk/util-utf8-browser@npm:^3.0.0": + version: 3.188.0 + resolution: "@aws-sdk/util-utf8-browser@npm:3.188.0" dependencies: tslib: ^2.3.1 - checksum: f1c9cbafc3dcf5b04264c3708509117d5e6f6b373fb30712a0cd9d09966a2cdead0a91067a8e9513abac9b082971fd40519e421a2e046b3653ce1ede136197c2 + checksum: dacd27164aa0835888434e080b67f04510e2281560540ff73496f2d0aa73b0b7f830ec08491b35c3a51bf6214615579182aff8727e151e54a74a97a197a2ac31 languageName: node linkType: hard -"@aws-sdk/util-utf8-node@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-utf8-node@npm:3.183.0" +"@aws-sdk/util-utf8-node@npm:3.208.0": + version: 3.208.0 + resolution: "@aws-sdk/util-utf8-node@npm:3.208.0" dependencies: - "@aws-sdk/util-buffer-from": 3.183.0 + "@aws-sdk/util-buffer-from": 3.208.0 tslib: ^2.3.1 - checksum: c37f2bd388783c33c981afcd5a6a80481cc4e019f42bc5ab10ba2dfb039a4684acb4fa30df579cb3a5b509f61b1261870b30cd966c179029adb2a42672c0a871 + checksum: 0f4c1d0d3f6eb40d92bc7163c6dcf97f90b3a8be05df46ba4f9bb7723a3e6acabf2cb0f2486411b03d22e8fcccae9e757ed02afad384ff5f44a3a659a78c5752 languageName: node linkType: hard @@ -1100,17 +1172,17 @@ __metadata: linkType: hard "@azure/arm-appservice@npm:^13.0.1": - version: 13.0.2 - resolution: "@azure/arm-appservice@npm:13.0.2" + version: 13.0.3 + resolution: "@azure/arm-appservice@npm:13.0.3" dependencies: "@azure/abort-controller": ^1.0.0 "@azure/core-auth": ^1.3.0 - "@azure/core-client": ^1.5.0 + "@azure/core-client": ^1.6.1 "@azure/core-lro": ^2.2.0 "@azure/core-paging": ^1.2.0 "@azure/core-rest-pipeline": ^1.8.0 tslib: ^2.2.0 - checksum: 6d604c492a70a6cdd4d3ad3f634a74228dc081ed4f49128873e76bfa5e65ec8cce18b7b19319fd2b1b4edbd390dbd66c9ac48646f0ed80cae09cd2f1d05fb279 + checksum: 9cd73985a31744fd7da56654c1de2d1396e93fa0cc3527b7c2843e56913e2b0abd81d996a319447f2f289421c45ede6a87f820ed0f4414e7cd2afeb383ee8a0a languageName: node linkType: hard @@ -1143,7 +1215,7 @@ __metadata: languageName: node linkType: hard -"@azure/core-client@npm:^1.4.0, @azure/core-client@npm:^1.5.0": +"@azure/core-client@npm:^1.4.0, @azure/core-client@npm:^1.6.1": version: 1.6.1 resolution: "@azure/core-client@npm:1.6.1" dependencies: @@ -3379,7 +3451,7 @@ __metadata: pluralize: ^8.0.0 prop-types: ^15.7.2 qs: ^6.9.4 - rc-progress: 3.4.0 + rc-progress: 3.4.1 react-helmet: 6.1.0 react-hook-form: ^7.12.2 react-markdown: ^8.0.0 @@ -4750,6 +4822,7 @@ __metadata: cross-fetch: ^3.1.5 history: ^5.0.0 lodash: ^4.17.21 + pluralize: ^8.0.0 react-helmet: 6.1.0 react-use: ^17.2.4 zen-observable: ^0.9.0 @@ -4987,7 +5060,7 @@ __metadata: "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.48.0 - rc-progress: 3.4.0 + rc-progress: 3.4.1 react-use: ^17.2.4 peerDependencies: react: ^16.13.1 || ^17.0.0 @@ -5232,8 +5305,6 @@ __metadata: dependencies: "@backstage/backend-plugin-api": "workspace:^" "@backstage/cli": "workspace:^" - "@types/express": ^4.17.6 - express: ^4.17.1 languageName: unknown linkType: soft @@ -6933,7 +7004,7 @@ __metadata: "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.48.0 - rc-progress: 3.4.0 + rc-progress: 3.4.1 react-use: ^17.2.4 peerDependencies: react: ^16.13.1 || ^17.0.0 @@ -7946,11 +8017,11 @@ __metadata: linkType: hard "@codemirror/legacy-modes@npm:^6.1.0": - version: 6.2.0 - resolution: "@codemirror/legacy-modes@npm:6.2.0" + version: 6.3.0 + resolution: "@codemirror/legacy-modes@npm:6.3.0" dependencies: "@codemirror/language": ^6.0.0 - checksum: c4449ad4e9b80fad982956ace705af5e21cd8c8bb248930bcb394ec019babf1a7b23e1e2894428b67d28a0c5bee99592320673690d19039392ddbc77075ef132 + checksum: d4da9b338376b72c1936e6780b4b53ec6a9581ac3d47ad2c1ec26a0068183b77489bbffde747837c89a5a92c69e9724ddebedc7790975a22f783fb13ede94403 languageName: node linkType: hard @@ -7976,10 +8047,10 @@ __metadata: languageName: node linkType: hard -"@codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.1.1": - version: 6.1.2 - resolution: "@codemirror/state@npm:6.1.2" - checksum: 7eda4b3eb6a777fecaa5a62e95a162aa163ccff01913d21a88d1157247390d9b57e6bcf54738be0b98aff8ba3a6ec3cb7aeb3b9714b019cb6c05242da9d9fdfe +"@codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.1.1, @codemirror/state@npm:^6.1.4": + version: 6.1.4 + resolution: "@codemirror/state@npm:6.1.4" + checksum: ef6bc495d3b89d2f0202b6b1c0fc3e3610f8d815f4ca91f86153d6093c9b93ddbe2cfb787802d07514a7114f0b200cf2eeb54c1cd34d7e00eff774ecea97d845 languageName: node linkType: hard @@ -7996,13 +8067,13 @@ __metadata: linkType: hard "@codemirror/view@npm:^6.0.0": - version: 6.4.2 - resolution: "@codemirror/view@npm:6.4.2" + version: 6.5.1 + resolution: "@codemirror/view@npm:6.5.1" dependencies: - "@codemirror/state": ^6.0.0 + "@codemirror/state": ^6.1.4 style-mod: ^4.0.0 w3c-keyname: ^2.2.4 - checksum: c79982563ad90adf1d7504b28bb1304a73f669c69634a1630b51521e4023b3630c15601daf2857683b34a86cac4ca24dc5b538a9d944616617f99c1a7da7faa6 + checksum: 1bd66b4f174642c559a521b546e80a67556fdc1b7cc7d1c7baf1cac37fed83e0e63d7a9ff6a88f3991a82d11bbbcfcca6e7b3491dcdd536c4c114de142b0300d languageName: node linkType: hard @@ -8177,16 +8248,16 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.15.13": - version: 0.15.13 - resolution: "@esbuild/android-arm@npm:0.15.13" +"@esbuild/android-arm@npm:0.15.15": + version: 0.15.15 + resolution: "@esbuild/android-arm@npm:0.15.15" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.15.13": - version: 0.15.13 - resolution: "@esbuild/linux-loong64@npm:0.15.13" +"@esbuild/linux-loong64@npm:0.15.15": + version: 0.15.15 + resolution: "@esbuild/linux-loong64@npm:0.15.15" conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -8225,61 +8296,61 @@ __metadata: linkType: hard "@gitbeaker/browser@npm:^35.6.0": - version: 35.7.0 - resolution: "@gitbeaker/browser@npm:35.7.0" + version: 35.8.0 + resolution: "@gitbeaker/browser@npm:35.8.0" dependencies: - "@gitbeaker/core": ^35.7.0 - "@gitbeaker/requester-utils": ^35.7.0 + "@gitbeaker/core": ^35.8.0 + "@gitbeaker/requester-utils": ^35.8.0 delay: ^5.0.0 ky: 0.28.7 - checksum: 35b1afbd047ba172a21e548259fb38764c50e4794d64e33e6be7ae16552dc41882dc2107df70da35a3431ccb5014604efd4b189d16db2251349d0d0b01d65618 + checksum: 23fa14d64661108c03a5d141123037aebb68e257d84e26bd8f6241df65a0958eaa79b8b491e0f08159e325cdc3244f79b2426dedcc2354ad106316ea499415ac languageName: node linkType: hard -"@gitbeaker/core@npm:^35.6.0, @gitbeaker/core@npm:^35.7.0": - version: 35.7.0 - resolution: "@gitbeaker/core@npm:35.7.0" +"@gitbeaker/core@npm:^35.6.0, @gitbeaker/core@npm:^35.8.0": + version: 35.8.0 + resolution: "@gitbeaker/core@npm:35.8.0" dependencies: - "@gitbeaker/requester-utils": ^35.7.0 + "@gitbeaker/requester-utils": ^35.8.0 form-data: ^4.0.0 li: ^1.3.0 mime: ^3.0.0 query-string: ^7.0.0 xcase: ^2.0.1 - checksum: 5039557ba432a2478edb2cf767cef9d5f5f46f1de78219166eb6ec1bf6ad8e1dbf090085e31526fa58010ae4966bad9232af368474d9765b54bb3334173cb3cb + checksum: b894e1ac86dc8ac85f2db51d83d392899c10bb555b2448900f502ffeb49d9f36c1063c7151128ca96951ac472c11be6320584c4bec873501e96607bd7c25a37b languageName: node linkType: hard "@gitbeaker/node@npm:^35.1.0": - version: 35.7.0 - resolution: "@gitbeaker/node@npm:35.7.0" + version: 35.8.0 + resolution: "@gitbeaker/node@npm:35.8.0" dependencies: - "@gitbeaker/core": ^35.7.0 - "@gitbeaker/requester-utils": ^35.7.0 + "@gitbeaker/core": ^35.8.0 + "@gitbeaker/requester-utils": ^35.8.0 delay: ^5.0.0 got: ^11.8.3 xcase: ^2.0.1 - checksum: 879595b065f40027b6191f609bddba05988c308c924323aa0cf30220801c21ba01e62c3395fd9cd0072ff6bc330f211e7aadcf63d80e893d7d33149c09ba221e + checksum: af75fa2348ec6fcdd1605f269df10c13bd7d53f9a5c42da7e013db920a1129075072de3a0ada97d800be732575bb838b8a462472faebcfaf9fe243b93d29a464 languageName: node linkType: hard -"@gitbeaker/requester-utils@npm:^35.7.0": - version: 35.7.0 - resolution: "@gitbeaker/requester-utils@npm:35.7.0" +"@gitbeaker/requester-utils@npm:^35.8.0": + version: 35.8.0 + resolution: "@gitbeaker/requester-utils@npm:35.8.0" dependencies: form-data: ^4.0.0 qs: ^6.10.1 xcase: ^2.0.1 - checksum: f928affa8c5cd6b1b2811d77e14e2d60611bc9b93767fce936125b59e75b9786e4d760b5dc624719fbfdb8638905d957817845bcee01b147bbe5aebf60a635c1 + checksum: 6bd49b821fb6f262c6306c06fc4812f41902c864a7d941b0d112381b352f680e50ebd6180840e21a89d761427cb1326d9829b74c98c95bf9fa2a5d53872a400a languageName: node linkType: hard "@google-cloud/container@npm:^4.0.0": - version: 4.3.0 - resolution: "@google-cloud/container@npm:4.3.0" + version: 4.4.0 + resolution: "@google-cloud/container@npm:4.4.0" dependencies: - google-gax: ^3.3.0 - checksum: cc1dc2ca7f6b47bac0d374013e78b05372f25dce7a3f84d799899af274d26fa22f23d2554a66ca1db528c3bbbec1cfbac86dfa9fc72cc78f175bcb737900b3ed + google-gax: ^3.5.2 + checksum: 39e7ba3b8cd7e50908315eee5c42461b8cee909c5653ffc817503757e867d63815c5ca8f540b7566f38be9ae0454ed89d2efe95b64961a8692a9348ddf92074f languageName: node linkType: hard @@ -8443,18 +8514,18 @@ __metadata: linkType: hard "@graphql-codegen/graphql-modules-preset@npm:^2.3.2": - version: 2.5.5 - resolution: "@graphql-codegen/graphql-modules-preset@npm:2.5.5" + version: 2.5.6 + resolution: "@graphql-codegen/graphql-modules-preset@npm:2.5.6" dependencies: "@graphql-codegen/plugin-helpers": ^2.7.2 - "@graphql-codegen/visitor-plugin-common": 2.13.1 + "@graphql-codegen/visitor-plugin-common": 2.13.2 "@graphql-tools/utils": ^8.8.0 change-case-all: 1.0.14 parse-filepath: ^1.0.2 tslib: ~2.4.0 peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: d499001af49d0a829c6adcbc211d252e8d5a42ba1635c32265ad99d187666425150c99815d48e591ca66e811a2b09860a5f59ba02484a3a3f0dcf4dcba8a6fb8 + checksum: e446c4277f66304d34e9883c37dd241559ba47695716d8ef911514de998c927e2f37770d70b87b56238e3fed1bf43eff9ec7bce679e06fb4eb0b759c98f6cb25 languageName: node linkType: hard @@ -8488,39 +8559,39 @@ __metadata: linkType: hard "@graphql-codegen/typescript-resolvers@npm:^2.4.3": - version: 2.7.6 - resolution: "@graphql-codegen/typescript-resolvers@npm:2.7.6" + version: 2.7.7 + resolution: "@graphql-codegen/typescript-resolvers@npm:2.7.7" dependencies: "@graphql-codegen/plugin-helpers": ^2.7.2 - "@graphql-codegen/typescript": ^2.8.1 - "@graphql-codegen/visitor-plugin-common": 2.13.1 + "@graphql-codegen/typescript": ^2.8.2 + "@graphql-codegen/visitor-plugin-common": 2.13.2 "@graphql-tools/utils": ^8.8.0 auto-bind: ~4.0.0 tslib: ~2.4.0 peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: f552f74aa264c0f9024834d6d1c126f9f1a69dccf7278525c7609ad1820daf87607447f30351506fe3728a855690928ba7775edbbbff0fee610a2ce02913a98e + checksum: f7b4fcda7fa3d73a48bf93e32ee73fdd8426d1d89951b22555702a98445b110aeb0715a1ed29dc1cdee7cbe0264510d021e98b57798a51bcf07eab34c4445cf5 languageName: node linkType: hard -"@graphql-codegen/typescript@npm:^2.4.2, @graphql-codegen/typescript@npm:^2.8.1": - version: 2.8.1 - resolution: "@graphql-codegen/typescript@npm:2.8.1" +"@graphql-codegen/typescript@npm:^2.4.2, @graphql-codegen/typescript@npm:^2.8.2": + version: 2.8.2 + resolution: "@graphql-codegen/typescript@npm:2.8.2" dependencies: "@graphql-codegen/plugin-helpers": ^2.7.2 "@graphql-codegen/schema-ast": ^2.5.1 - "@graphql-codegen/visitor-plugin-common": 2.13.1 + "@graphql-codegen/visitor-plugin-common": 2.13.2 auto-bind: ~4.0.0 tslib: ~2.4.0 peerDependencies: graphql: ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: 2b5466190e385c557eed6210b861fb9e632fb61b90bd0f97ff983f359c0ad8106228667c9ff2201842100417a07cfaffa91794ef03058a151df256d733363fa2 + checksum: 7e3beda7a0a8b9b84ee7c40c54913ef496bd8716ddd68028a55b06743657bc1100c4a30cc2932745520f5e008aa3f386731bf09a88e05cc92005e066b57b21c8 languageName: node linkType: hard -"@graphql-codegen/visitor-plugin-common@npm:2.13.1": - version: 2.13.1 - resolution: "@graphql-codegen/visitor-plugin-common@npm:2.13.1" +"@graphql-codegen/visitor-plugin-common@npm:2.13.2": + version: 2.13.2 + resolution: "@graphql-codegen/visitor-plugin-common@npm:2.13.2" dependencies: "@graphql-codegen/plugin-helpers": ^2.7.2 "@graphql-tools/optimize": ^1.3.0 @@ -8534,7 +8605,7 @@ __metadata: tslib: ~2.4.0 peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: 0c329aa6e435602f2f6c1569ec2091b7850f58cc5dca7ac763c38c82588545ec1110c1de587f5f3949b11ff96f94401d1e63e329607d78424583b276fd08f1ae + checksum: b4b00f9d0cd8be221da69480d0d861f025eb7bec13aa9897acb9a3a485b5e0ca04df17745fa9850efcafefe4de54dab2e4d50dafc534df3fecd9095e6122eaa7 languageName: node linkType: hard @@ -8723,15 +8794,15 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/merge@npm:8.3.11, @graphql-tools/merge@npm:^8.2.6": - version: 8.3.11 - resolution: "@graphql-tools/merge@npm:8.3.11" +"@graphql-tools/merge@npm:8.3.12, @graphql-tools/merge@npm:^8.2.6": + version: 8.3.12 + resolution: "@graphql-tools/merge@npm:8.3.12" dependencies: - "@graphql-tools/utils": 9.1.0 + "@graphql-tools/utils": 9.1.1 tslib: ^2.4.0 peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 3cd16577f1ea1bcc389d232fa620adc0fefc91a6b42b48e7bb5e7bb67b67d206d409a9a8d60cac4d8cd5f0f1c5353369f7c5eb4054b44ce2277545e1fd1de17f + checksum: aab6f6edbaa2f63c7f44e4a1832d8028570d09ea85bb4a9525016879fad2ea2f8ea260c0b78ab1f6b7a418d92a3debfbbedecb3f5a36a5590edd0b2ac1cd2012 languageName: node linkType: hard @@ -8857,16 +8928,16 @@ __metadata: linkType: hard "@graphql-tools/schema@npm:^9.0.0": - version: 9.0.9 - resolution: "@graphql-tools/schema@npm:9.0.9" + version: 9.0.10 + resolution: "@graphql-tools/schema@npm:9.0.10" dependencies: - "@graphql-tools/merge": 8.3.11 - "@graphql-tools/utils": 9.1.0 + "@graphql-tools/merge": 8.3.12 + "@graphql-tools/utils": 9.1.1 tslib: ^2.4.0 value-or-promise: 1.0.11 peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 23620a993eff57723302315100d947b81664d6a1e00010ff293b3649f51ab423946d73920cce0ce822b7f7f42b99ea23240902ff47df3d252639613c0df7b971 + checksum: 4c1c7fc1c84a2faf2310a110ed72125b511371f1ea2869442fd55b5bcdb2c431dd60235ff77f32860235ee99dd12746e23dace35a18a8ffd339fad4f51004321 languageName: node linkType: hard @@ -8939,18 +9010,7 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/utils@npm:9.1.0": - version: 9.1.0 - resolution: "@graphql-tools/utils@npm:9.1.0" - dependencies: - tslib: ^2.4.0 - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: dcf08df3bd1715e25e3b80bf2dd332376239ebcd2a9b93f4d6b72a7bd3feffe2207b9683bb8fcaed9d0d1286f701d0686fe99ac803b91c0df54509b869c0554d - languageName: node - linkType: hard - -"@graphql-tools/utils@npm:^9.1.1": +"@graphql-tools/utils@npm:9.1.1, @graphql-tools/utils@npm:^9.1.1": version: 9.1.1 resolution: "@graphql-tools/utils@npm:9.1.1" dependencies: @@ -11584,16 +11644,16 @@ __metadata: linkType: hard "@rjsf/validator-ajv8@npm:^5.0.0-beta.12": - version: 5.0.0-beta.12 - resolution: "@rjsf/validator-ajv8@npm:5.0.0-beta.12" + version: 5.0.0-beta.13 + resolution: "@rjsf/validator-ajv8@npm:5.0.0-beta.13" dependencies: - ajv: ^8.11.0 ajv-formats: ^2.1.1 + ajv8: "npm:ajv@^8.11.0" lodash: ^4.17.15 lodash-es: ^4.17.15 peerDependencies: - "@rjsf/utils": ^5.0.0-beta.1 - checksum: 2acd420fdd099b35e534c4086ce5d3759574a8b4dc4b4a21ca35e85b023e6d9688aab1dbfca9381bdbf4aecbe3eb195e27453e8dd9cc70712fe2652e1496840c + "@rjsf/utils": ^5.0.0-beta.12 + checksum: e0a51e25845745da628794dadef2c7ffc910d26cb99a90fa763d4a5593b554b084c578a73ed688d02defbeb5ddec38066caae527f5dc8dabba7f012cec31e8a3 languageName: node linkType: hard @@ -12150,90 +12210,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-darwin-arm64@npm:1.3.16" +"@swc/core-darwin-arm64@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-darwin-arm64@npm:1.3.19" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-darwin-x64@npm:1.3.16" +"@swc/core-darwin-x64@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-darwin-x64@npm:1.3.19" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.16" +"@swc/core-linux-arm-gnueabihf@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.19" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.16" +"@swc/core-linux-arm64-gnu@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.19" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.16" +"@swc/core-linux-arm64-musl@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.19" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.16" +"@swc/core-linux-x64-gnu@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.19" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-x64-musl@npm:1.3.16" +"@swc/core-linux-x64-musl@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-x64-musl@npm:1.3.19" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.16" +"@swc/core-win32-arm64-msvc@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.19" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.16" +"@swc/core-win32-ia32-msvc@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.19" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.16" +"@swc/core-win32-x64-msvc@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.19" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.9": - version: 1.3.16 - resolution: "@swc/core@npm:1.3.16" + version: 1.3.19 + resolution: "@swc/core@npm:1.3.19" dependencies: - "@swc/core-darwin-arm64": 1.3.16 - "@swc/core-darwin-x64": 1.3.16 - "@swc/core-linux-arm-gnueabihf": 1.3.16 - "@swc/core-linux-arm64-gnu": 1.3.16 - "@swc/core-linux-arm64-musl": 1.3.16 - "@swc/core-linux-x64-gnu": 1.3.16 - "@swc/core-linux-x64-musl": 1.3.16 - "@swc/core-win32-arm64-msvc": 1.3.16 - "@swc/core-win32-ia32-msvc": 1.3.16 - "@swc/core-win32-x64-msvc": 1.3.16 + "@swc/core-darwin-arm64": 1.3.19 + "@swc/core-darwin-x64": 1.3.19 + "@swc/core-linux-arm-gnueabihf": 1.3.19 + "@swc/core-linux-arm64-gnu": 1.3.19 + "@swc/core-linux-arm64-musl": 1.3.19 + "@swc/core-linux-x64-gnu": 1.3.19 + "@swc/core-linux-x64-musl": 1.3.19 + "@swc/core-win32-arm64-msvc": 1.3.19 + "@swc/core-win32-ia32-msvc": 1.3.19 + "@swc/core-win32-x64-msvc": 1.3.19 dependenciesMeta: "@swc/core-darwin-arm64": optional: true @@ -12257,16 +12317,16 @@ __metadata: optional: true bin: swcx: run_swcx.js - checksum: 4361252c928c487a02f526aecd8f3072b923244234c2701916944cf13c252b6d5ce2466caf3e0797d3e92e71a89d4a044f8577e51e8f7fe3af0fe30d94e94b13 + checksum: 752499e18f81df789a9737936b2a83ea1db34bb2f983c80b394766796d853fa7d30701b52b9cb30ba943ad15286d8316603149683f4c553a5493aa78d89a76e0 languageName: node linkType: hard "@swc/helpers@npm:^0.4.7": - version: 0.4.12 - resolution: "@swc/helpers@npm:0.4.12" + version: 0.4.14 + resolution: "@swc/helpers@npm:0.4.14" dependencies: tslib: ^2.4.0 - checksum: 3f9112f37d87815b6d4270137fc78d22bb98c75138e9b0eac7cac203ec2cf2bffbf13b20a713067c292affd5e9e70a724eb245b8daf0963e7fe528b901771c28 + checksum: 273fd3f3fc461a92f3790cc551ea054745c6d6959afbe1232e6d7aa1c722bbc114d308aab96bef5c78fc0303c85c7b472ef00e2253251cc89737f3b1af56e5a5 languageName: node linkType: hard @@ -12353,14 +12413,14 @@ __metadata: linkType: soft "@testing-library/cypress@npm:^8.0.2": - version: 8.0.3 - resolution: "@testing-library/cypress@npm:8.0.3" + version: 8.0.7 + resolution: "@testing-library/cypress@npm:8.0.7" dependencies: "@babel/runtime": ^7.14.6 "@testing-library/dom": ^8.1.0 peerDependencies: - cypress: ^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 - checksum: 319f6c7297f85e5673882a71009b5d5b576006f986d87a6ca6f2f8c679d96d9779f3ddbc01654b5a943407fa91192814f250835ce01f153e33ef24818cf1bf20 + cypress: ^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + checksum: e005bc1a7ec808706c57e95ed312069fb5be39ea7362900dc2a32c09f124d478ade69ebcd7df88c076e3867ab328ae6e6ce13791bdf042621ff66b56552bf74b languageName: node linkType: hard @@ -12971,12 +13031,12 @@ __metadata: linkType: hard "@types/dockerode@npm:^3.3.0, @types/dockerode@npm:^3.3.8": - version: 3.3.13 - resolution: "@types/dockerode@npm:3.3.13" + version: 3.3.14 + resolution: "@types/dockerode@npm:3.3.14" dependencies: "@types/docker-modem": "*" "@types/node": "*" - checksum: dbcf3e33f5d8b731a1540d8b72c91d12583eb87d47adf8defb71f8bc1b328a26a035c66068255929ef99a7906da53ac6e4be76099e8fa7e9720c134b1ab3e153 + checksum: 6a8472622861fb0c97908963a8236d6e439c9feefc845b866326272ad0e5c33f537206fb4633424c5e3c8ec63559cb14bc737e2f88f0fcdf11496a9162d5b139 languageName: node linkType: hard @@ -13990,10 +14050,10 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.3.8": - version: 7.3.12 - resolution: "@types/semver@npm:7.3.12" - checksum: 35536b2fc5602904f21cae681f6c9498e177dab3f54ae37c92f9a1b7e43c35f18bcd81e1c98c1cf0d33ee046bb06c771e9928c1c00a401d56a03f56549252a15 +"@types/semver@npm:^7.3.12, @types/semver@npm:^7.3.8": + version: 7.3.13 + resolution: "@types/semver@npm:7.3.13" + checksum: 00c0724d54757c2f4bc60b5032fe91cda6410e48689633d5f35ece8a0a66445e3e57fa1d6e07eb780f792e82ac542948ec4d0b76eb3484297b79bd18b8cf1cb0 languageName: node linkType: hard @@ -14446,6 +14506,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:5.43.0": + version: 5.43.0 + resolution: "@typescript-eslint/scope-manager@npm:5.43.0" + dependencies: + "@typescript-eslint/types": 5.43.0 + "@typescript-eslint/visitor-keys": 5.43.0 + checksum: e594c7a32c3fa29e46dd0b0bc62f97f154bd864682ae7da87a14b6f4336f4cb02f6ed0602bbdb15783e4230ecdf8a0ccc6f7c5820850e8f11240c9e4fb0e388d + languageName: node + linkType: hard + "@typescript-eslint/scope-manager@npm:5.9.0": version: 5.9.0 resolution: "@typescript-eslint/scope-manager@npm:5.9.0" @@ -14480,6 +14550,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:5.43.0": + version: 5.43.0 + resolution: "@typescript-eslint/types@npm:5.43.0" + checksum: fc5e5431c305feee4a3faae84f34df482e08d74b910a6f9376b01326c682ceefeeb0e270d03d7778787bc94ef05b3b85ee6d3c9d732290fbdb4a67ae1b110226 + languageName: node + linkType: hard + "@typescript-eslint/types@npm:5.9.0": version: 5.9.0 resolution: "@typescript-eslint/types@npm:5.9.0" @@ -14505,6 +14582,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:5.43.0": + version: 5.43.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.43.0" + dependencies: + "@typescript-eslint/types": 5.43.0 + "@typescript-eslint/visitor-keys": 5.43.0 + debug: ^4.3.4 + globby: ^11.1.0 + is-glob: ^4.0.3 + semver: ^7.3.7 + tsutils: ^3.21.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 3479f9413d73369ab3d574580c90a72f74d2ae1ec4afe485eebfad054c3d15c89f23a137bb9d6197dfdae33e444a76a99f6832688787feabbb064e09d39a3f55 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:5.9.0": version: 5.9.0 resolution: "@typescript-eslint/typescript-estree@npm:5.9.0" @@ -14523,7 +14618,7 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.36.2, @typescript-eslint/utils@npm:^5.10.0": +"@typescript-eslint/utils@npm:5.36.2": version: 5.36.2 resolution: "@typescript-eslint/utils@npm:5.36.2" dependencies: @@ -14539,6 +14634,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:^5.10.0, @typescript-eslint/utils@npm:^5.13.0": + version: 5.43.0 + resolution: "@typescript-eslint/utils@npm:5.43.0" + dependencies: + "@types/json-schema": ^7.0.9 + "@types/semver": ^7.3.12 + "@typescript-eslint/scope-manager": 5.43.0 + "@typescript-eslint/types": 5.43.0 + "@typescript-eslint/typescript-estree": 5.43.0 + eslint-scope: ^5.1.1 + eslint-utils: ^3.0.0 + semver: ^7.3.7 + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: 4c6b383b51506b57230f2624f883ae21e5d8411d138587c04fe3145f915bf8c289cc2a9f7b0b3faba98345ba230504e5014922bcc578aa0badd594d9eaa8f9ef + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:5.36.2": version: 5.36.2 resolution: "@typescript-eslint/visitor-keys@npm:5.36.2" @@ -14549,6 +14662,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:5.43.0": + version: 5.43.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.43.0" + dependencies: + "@typescript-eslint/types": 5.43.0 + eslint-visitor-keys: ^3.3.0 + checksum: 4820679e50096dcdaadc7c95d32e5dca3ba8510acf1a865e283822bae3940a2faec02ad8abe793f8a25f75b600f1e7215e1fd3b3ba73779eff737fa90d092550 + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:5.9.0": version: 5.9.0 resolution: "@typescript-eslint/visitor-keys@npm:5.9.0" @@ -15038,6 +15161,18 @@ __metadata: languageName: node linkType: hard +"ajv8@npm:ajv@^8.11.0, ajv@npm:^8.0.0, ajv@npm:^8.10.0, ajv@npm:^8.6.3, ajv@npm:^8.8.0": + version: 8.11.2 + resolution: "ajv@npm:8.11.2" + dependencies: + fast-deep-equal: ^3.1.1 + json-schema-traverse: ^1.0.0 + require-from-string: ^2.0.2 + uri-js: ^4.2.2 + checksum: 53435bf79ee7d1eabba8085962dba4c08d08593334b304db7772887f0b7beebc1b3d957432f7437ed4b60e53b5d966a57b439869890209c50fed610459999e3e + languageName: node + linkType: hard + "ajv@npm:^6.10.0, ajv@npm:^6.10.1, ajv@npm:^6.12.2, ajv@npm:^6.12.4, ajv@npm:^6.12.5, ajv@npm:^6.5.5, ajv@npm:^6.7.0, ajv@npm:~6.12.6": version: 6.12.6 resolution: "ajv@npm:6.12.6" @@ -15050,18 +15185,6 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.0, ajv@npm:^8.10.0, ajv@npm:^8.11.0, ajv@npm:^8.6.3, ajv@npm:^8.8.0": - version: 8.11.2 - resolution: "ajv@npm:8.11.2" - dependencies: - fast-deep-equal: ^3.1.1 - json-schema-traverse: ^1.0.0 - require-from-string: ^2.0.2 - uri-js: ^4.2.2 - checksum: 53435bf79ee7d1eabba8085962dba4c08d08593334b304db7772887f0b7beebc1b3d957432f7437ed4b60e53b5d966a57b439869890209c50fed610459999e3e - languageName: node - linkType: hard - "alphanum-sort@npm:^1.0.2": version: 1.0.2 resolution: "alphanum-sort@npm:1.0.2" @@ -15489,16 +15612,16 @@ __metadata: languageName: node linkType: hard -"array-includes@npm:^3.1.4, array-includes@npm:^3.1.5": - version: 3.1.5 - resolution: "array-includes@npm:3.1.5" +"array-includes@npm:^3.1.4, array-includes@npm:^3.1.5, array-includes@npm:^3.1.6": + version: 3.1.6 + resolution: "array-includes@npm:3.1.6" dependencies: call-bind: ^1.0.2 define-properties: ^1.1.4 - es-abstract: ^1.19.5 - get-intrinsic: ^1.1.1 + es-abstract: ^1.20.4 + get-intrinsic: ^1.1.3 is-string: ^1.0.7 - checksum: f6f24d834179604656b7bec3e047251d5cc87e9e87fab7c175c61af48e80e75acd296017abcde21fb52292ab6a2a449ab2ee37213ee48c8709f004d75983f9c5 + checksum: f22f8cd8ba8a6448d91eebdc69f04e4e55085d09232b5216ee2d476dab3ef59984e8d1889e662c6a0ed939dcb1b57fd05b2c0209c3370942fc41b752c82a2ca5 languageName: node linkType: hard @@ -15537,15 +15660,28 @@ __metadata: languageName: node linkType: hard -"array.prototype.flatmap@npm:^1.3.0": - version: 1.3.0 - resolution: "array.prototype.flatmap@npm:1.3.0" +"array.prototype.flatmap@npm:^1.3.1": + version: 1.3.1 + resolution: "array.prototype.flatmap@npm:1.3.1" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.2 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 es-shim-unscopables: ^1.0.0 - checksum: 818538f39409c4045d874be85df0dbd195e1446b14d22f95bdcfefea44ae77db44e42dcd89a559254ec5a7c8b338cfc986cc6d641e3472f9a5326b21eb2976a2 + checksum: 8c1c43a4995f12cf12523436da28515184c753807b3f0bc2ca6c075f71c470b099e2090cc67dba8e5280958fea401c1d0c59e1db0143272aef6cd1103921a987 + languageName: node + linkType: hard + +"array.prototype.tosorted@npm:^1.1.1": + version: 1.1.1 + resolution: "array.prototype.tosorted@npm:1.1.1" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + es-shim-unscopables: ^1.0.0 + get-intrinsic: ^1.1.3 + checksum: 7923324a67e70a2fc0a6e40237405d92395e45ebd76f5cb89c2a5cf1e66b47aca6baacd0cd628ffd88830b90d47fff268071493d09c9ae123645613dac2c2ca3 languageName: node linkType: hard @@ -18319,20 +18455,20 @@ __metadata: linkType: hard "css-loader@npm:^6.5.1": - version: 6.7.1 - resolution: "css-loader@npm:6.7.1" + version: 6.7.2 + resolution: "css-loader@npm:6.7.2" dependencies: icss-utils: ^5.1.0 - postcss: ^8.4.7 + postcss: ^8.4.18 postcss-modules-extract-imports: ^3.0.0 postcss-modules-local-by-default: ^4.0.0 postcss-modules-scope: ^3.0.0 postcss-modules-values: ^4.0.0 postcss-value-parser: ^4.2.0 - semver: ^7.3.5 + semver: ^7.3.8 peerDependencies: webpack: ^5.0.0 - checksum: 170fdbc630a05a43679ef60fa97694766b568dbde37adccc0faafa964fc675f08b976bc68837bb73b61d60240e8d2cbcbf51540fe94ebc9dafc56e7c46ba5527 + checksum: f3c980cc9c033a02e60df7e5a2f33a1e8c2c3dd552f017485d2d81b383be623ae8c4189404e7a4a7403b52744683ae4b516def0f7ccf125c2b198cb647e46543 languageName: node linkType: hard @@ -19864,7 +20000,7 @@ __metadata: languageName: node linkType: hard -"entities@npm:2.2.0, entities@npm:^2.0.0": +"entities@npm:^2.0.0": version: 2.2.0 resolution: "entities@npm:2.2.0" checksum: 19010dacaf0912c895ea262b4f6128574f9ccf8d4b3b65c7e8334ad0079b3706376360e28d8843ff50a78aabcb8f08f0a32dbfacdc77e47ed77ca08b713669b3 @@ -19931,34 +20067,35 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.18.0-next.1, es-abstract@npm:^1.18.0-next.2, es-abstract@npm:^1.19.0, es-abstract@npm:^1.19.1, es-abstract@npm:^1.19.2, es-abstract@npm:^1.19.5": - version: 1.20.1 - resolution: "es-abstract@npm:1.20.1" +"es-abstract@npm:^1.18.0-next.1, es-abstract@npm:^1.18.0-next.2, es-abstract@npm:^1.19.0, es-abstract@npm:^1.19.2, es-abstract@npm:^1.19.5, es-abstract@npm:^1.20.4": + version: 1.20.4 + resolution: "es-abstract@npm:1.20.4" dependencies: call-bind: ^1.0.2 es-to-primitive: ^1.2.1 function-bind: ^1.1.1 function.prototype.name: ^1.1.5 - get-intrinsic: ^1.1.1 + get-intrinsic: ^1.1.3 get-symbol-description: ^1.0.0 has: ^1.0.3 has-property-descriptors: ^1.0.0 has-symbols: ^1.0.3 internal-slot: ^1.0.3 - is-callable: ^1.2.4 + is-callable: ^1.2.7 is-negative-zero: ^2.0.2 is-regex: ^1.1.4 is-shared-array-buffer: ^1.0.2 is-string: ^1.0.7 is-weakref: ^1.0.2 - object-inspect: ^1.12.0 + object-inspect: ^1.12.2 object-keys: ^1.1.1 - object.assign: ^4.1.2 + object.assign: ^4.1.4 regexp.prototype.flags: ^1.4.3 + safe-regex-test: ^1.0.0 string.prototype.trimend: ^1.0.5 string.prototype.trimstart: ^1.0.5 unbox-primitive: ^1.0.2 - checksum: 28da27ae0ed9c76df7ee8ef5c278df79dcfdb554415faf7068bb7c58f8ba8e2a16bfb59e586844be6429ab4c302ca7748979d48442224cb1140b051866d74b7f + checksum: 89297cc785c31aedf961a603d5a07ed16471e435d3a1b6d070b54f157cf48454b95cda2ac55e4b86ff4fe3276e835fcffd2771578e6fa634337da49b26826141 languageName: node linkType: hard @@ -19996,100 +20133,100 @@ __metadata: languageName: node linkType: hard -"esbuild-android-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-android-64@npm:0.15.13" +"esbuild-android-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-android-64@npm:0.15.15" conditions: os=android & cpu=x64 languageName: node linkType: hard -"esbuild-android-arm64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-android-arm64@npm:0.15.13" +"esbuild-android-arm64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-android-arm64@npm:0.15.15" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"esbuild-darwin-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-darwin-64@npm:0.15.13" +"esbuild-darwin-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-darwin-64@npm:0.15.15" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"esbuild-darwin-arm64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-darwin-arm64@npm:0.15.13" +"esbuild-darwin-arm64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-darwin-arm64@npm:0.15.15" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"esbuild-freebsd-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-freebsd-64@npm:0.15.13" +"esbuild-freebsd-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-freebsd-64@npm:0.15.15" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"esbuild-freebsd-arm64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-freebsd-arm64@npm:0.15.13" +"esbuild-freebsd-arm64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-freebsd-arm64@npm:0.15.15" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"esbuild-linux-32@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-32@npm:0.15.13" +"esbuild-linux-32@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-32@npm:0.15.15" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"esbuild-linux-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-64@npm:0.15.13" +"esbuild-linux-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-64@npm:0.15.15" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"esbuild-linux-arm64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-arm64@npm:0.15.13" +"esbuild-linux-arm64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-arm64@npm:0.15.15" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"esbuild-linux-arm@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-arm@npm:0.15.13" +"esbuild-linux-arm@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-arm@npm:0.15.15" conditions: os=linux & cpu=arm languageName: node linkType: hard -"esbuild-linux-mips64le@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-mips64le@npm:0.15.13" +"esbuild-linux-mips64le@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-mips64le@npm:0.15.15" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"esbuild-linux-ppc64le@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-ppc64le@npm:0.15.13" +"esbuild-linux-ppc64le@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-ppc64le@npm:0.15.15" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"esbuild-linux-riscv64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-riscv64@npm:0.15.13" +"esbuild-linux-riscv64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-riscv64@npm:0.15.15" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"esbuild-linux-s390x@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-s390x@npm:0.15.13" +"esbuild-linux-s390x@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-s390x@npm:0.15.15" conditions: os=linux & cpu=s390x languageName: node linkType: hard @@ -20110,74 +20247,74 @@ __metadata: languageName: node linkType: hard -"esbuild-netbsd-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-netbsd-64@npm:0.15.13" +"esbuild-netbsd-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-netbsd-64@npm:0.15.15" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"esbuild-openbsd-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-openbsd-64@npm:0.15.13" +"esbuild-openbsd-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-openbsd-64@npm:0.15.15" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"esbuild-sunos-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-sunos-64@npm:0.15.13" +"esbuild-sunos-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-sunos-64@npm:0.15.15" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"esbuild-windows-32@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-windows-32@npm:0.15.13" +"esbuild-windows-32@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-windows-32@npm:0.15.15" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"esbuild-windows-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-windows-64@npm:0.15.13" +"esbuild-windows-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-windows-64@npm:0.15.15" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"esbuild-windows-arm64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-windows-arm64@npm:0.15.13" +"esbuild-windows-arm64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-windows-arm64@npm:0.15.15" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard "esbuild@npm:^0.15.0, esbuild@npm:^0.15.6": - version: 0.15.13 - resolution: "esbuild@npm:0.15.13" + version: 0.15.15 + resolution: "esbuild@npm:0.15.15" dependencies: - "@esbuild/android-arm": 0.15.13 - "@esbuild/linux-loong64": 0.15.13 - esbuild-android-64: 0.15.13 - esbuild-android-arm64: 0.15.13 - esbuild-darwin-64: 0.15.13 - esbuild-darwin-arm64: 0.15.13 - esbuild-freebsd-64: 0.15.13 - esbuild-freebsd-arm64: 0.15.13 - esbuild-linux-32: 0.15.13 - esbuild-linux-64: 0.15.13 - esbuild-linux-arm: 0.15.13 - esbuild-linux-arm64: 0.15.13 - esbuild-linux-mips64le: 0.15.13 - esbuild-linux-ppc64le: 0.15.13 - esbuild-linux-riscv64: 0.15.13 - esbuild-linux-s390x: 0.15.13 - esbuild-netbsd-64: 0.15.13 - esbuild-openbsd-64: 0.15.13 - esbuild-sunos-64: 0.15.13 - esbuild-windows-32: 0.15.13 - esbuild-windows-64: 0.15.13 - esbuild-windows-arm64: 0.15.13 + "@esbuild/android-arm": 0.15.15 + "@esbuild/linux-loong64": 0.15.15 + esbuild-android-64: 0.15.15 + esbuild-android-arm64: 0.15.15 + esbuild-darwin-64: 0.15.15 + esbuild-darwin-arm64: 0.15.15 + esbuild-freebsd-64: 0.15.15 + esbuild-freebsd-arm64: 0.15.15 + esbuild-linux-32: 0.15.15 + esbuild-linux-64: 0.15.15 + esbuild-linux-arm: 0.15.15 + esbuild-linux-arm64: 0.15.15 + esbuild-linux-mips64le: 0.15.15 + esbuild-linux-ppc64le: 0.15.15 + esbuild-linux-riscv64: 0.15.15 + esbuild-linux-s390x: 0.15.15 + esbuild-netbsd-64: 0.15.15 + esbuild-openbsd-64: 0.15.15 + esbuild-sunos-64: 0.15.15 + esbuild-windows-32: 0.15.15 + esbuild-windows-64: 0.15.15 + esbuild-windows-arm64: 0.15.15 dependenciesMeta: "@esbuild/android-arm": optional: true @@ -20225,7 +20362,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: ef5f339fae7e2abc4ec5484d4b301efdf40f580e043cbf8a66e19d6c91df82368a810abec61fd5e5db226f0c354f49c36616c9ea04c5412a142a050c10239bf7 + checksum: 9a194a310ac7d6989d1fe7354901d531d3b0d4cc1897a85a7cb9f1ae0fe8d82eb59747e63c91f676bab7bed8d6a1f28f620d49f043e7bd6203f9afe34031f739 languageName: node linkType: hard @@ -20479,26 +20616,38 @@ __metadata: linkType: hard "eslint-plugin-react@npm:^7.28.0": - version: 7.31.10 - resolution: "eslint-plugin-react@npm:7.31.10" + version: 7.31.11 + resolution: "eslint-plugin-react@npm:7.31.11" dependencies: - array-includes: ^3.1.5 - array.prototype.flatmap: ^1.3.0 + array-includes: ^3.1.6 + array.prototype.flatmap: ^1.3.1 + array.prototype.tosorted: ^1.1.1 doctrine: ^2.1.0 estraverse: ^5.3.0 jsx-ast-utils: ^2.4.1 || ^3.0.0 minimatch: ^3.1.2 - object.entries: ^1.1.5 - object.fromentries: ^2.0.5 - object.hasown: ^1.1.1 - object.values: ^1.1.5 + object.entries: ^1.1.6 + object.fromentries: ^2.0.6 + object.hasown: ^1.1.2 + object.values: ^1.1.6 prop-types: ^15.8.1 resolve: ^2.0.0-next.3 semver: ^6.3.0 - string.prototype.matchall: ^4.0.7 + string.prototype.matchall: ^4.0.8 peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - checksum: f013669c296483559a760648fa06425f161b1aff93c668f14c4561c933d22a7836b745b88a795c53cab929c71513d5fd1f2ffdddff915709f01b77ac25f5b71b + checksum: a3d612f6647bef33cf2a67c81a6b37b42c075300ed079cffecf5fb475c0d6ab855c1de340d1cbf361a0126429fb906dda597527235d2d12c4404453dbc712fc6 + languageName: node + linkType: hard + +"eslint-plugin-testing-library@npm:^5.9.1": + version: 5.9.1 + resolution: "eslint-plugin-testing-library@npm:5.9.1" + dependencies: + "@typescript-eslint/utils": ^5.13.0 + peerDependencies: + eslint: ^7.5.0 || ^8.0.0 + checksum: d09f9486945807e9587d52b6979117bc41b750df741567381a06219671096afb318696a0e0db63e253e150fead40e77ef9653ee00f1dda83fc8920e3b3c47107 languageName: node linkType: hard @@ -21347,12 +21496,14 @@ __metadata: languageName: node linkType: hard -"fast-xml-parser@npm:3.19.0": - version: 3.19.0 - resolution: "fast-xml-parser@npm:3.19.0" +"fast-xml-parser@npm:4.0.11": + version: 4.0.11 + resolution: "fast-xml-parser@npm:4.0.11" + dependencies: + strnum: ^1.0.5 bin: - xml2js: cli.js - checksum: d9da9145f73d90c05ee2746d80c78eca4da0249dea8c81ea8f1a6e1245e62988ed4a040dbd1c7229b1e0bdcbf69d33c882e0ac337d10c7eedb159a4dc9779327 + fxparser: src/cli/cli.js + checksum: d8a08e4d5597e0fc00a86735195872eeb03008913e298830941516f3766e16ee555e2d431acc92e1dda887938edc445252ec5b59494aab60a8389888bd13719c languageName: node linkType: hard @@ -22143,14 +22294,14 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.0, get-intrinsic@npm:^1.1.1": - version: 1.1.2 - resolution: "get-intrinsic@npm:1.1.2" +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.0, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3": + version: 1.1.3 + resolution: "get-intrinsic@npm:1.1.3" dependencies: function-bind: ^1.1.1 has: ^1.0.3 has-symbols: ^1.0.3 - checksum: 252f45491f2ba88ebf5b38018020c7cc3279de54b1d67ffb70c0cdf1dfa8ab31cd56467b5d117a8b4275b7a4dde91f86766b163a17a850f036528a7b2faafb2b + checksum: 152d79e87251d536cf880ba75cfc3d6c6c50e12b3a64e1ea960e73a3752b47c69f46034456eae1b0894359ce3bc64c55c186f2811f8a788b75b638b06fab228a languageName: node linkType: hard @@ -22458,9 +22609,9 @@ __metadata: languageName: node linkType: hard -"google-gax@npm:^3.3.0, google-gax@npm:^3.5.1": - version: 3.5.1 - resolution: "google-gax@npm:3.5.1" +"google-gax@npm:^3.5.1, google-gax@npm:^3.5.2": + version: 3.5.2 + resolution: "google-gax@npm:3.5.2" dependencies: "@grpc/grpc-js": ~1.7.0 "@grpc/proto-loader": ^0.7.0 @@ -22473,13 +22624,13 @@ __metadata: node-fetch: ^2.6.1 object-hash: ^3.0.0 proto3-json-serializer: ^1.0.0 - protobufjs: 7.1.1 + protobufjs: 7.1.2 protobufjs-cli: 1.0.2 retry-request: ^5.0.0 bin: compileProtos: build/tools/compileProtos.js minifyProtoJson: build/tools/minify.js - checksum: 81ecd10981554944a117ace753b2fc8f6d82b26b244db005d01fc5e127181571de9a71ec02b28aa2e233c6fe696108617512a8b090c8304e753913f1ca441767 + checksum: 619a961acba742c30eaba85f5e0fb5903f8fc6713e26bf1736549cbbd43d7fac3ba3379dbc96850117263199ea8e3e74635b078e9464c7fadbbcba8822949135 languageName: node linkType: hard @@ -23885,10 +24036,10 @@ __metadata: languageName: node linkType: hard -"is-callable@npm:^1.1.4, is-callable@npm:^1.2.4": - version: 1.2.4 - resolution: "is-callable@npm:1.2.4" - checksum: 1a28d57dc435797dae04b173b65d6d1e77d4f16276e9eff973f994eadcfdc30a017e6a597f092752a083c1103cceb56c91e3dadc6692fedb9898dfaba701575f +"is-callable@npm:^1.1.4, is-callable@npm:^1.2.7": + version: 1.2.7 + resolution: "is-callable@npm:1.2.7" + checksum: 61fd57d03b0d984e2ed3720fb1c7a897827ea174bd44402878e059542ea8c4aeedee0ea0985998aa5cc2736b2fa6e271c08587addb5b3959ac52cf665173d1ac languageName: node linkType: hard @@ -25173,9 +25324,9 @@ __metadata: linkType: hard "js-base64@npm:^3.6.0": - version: 3.7.2 - resolution: "js-base64@npm:3.7.2" - checksum: 573f28e9a27c3df60096d4d3f551bcb4fcb6d49161cf83396e9bad9b76f94736a70bb70b8808fe834dff2a388f76604ba09d6e153bbf181646e407720139fa5b + version: 3.7.3 + resolution: "js-base64@npm:3.7.3" + checksum: ee19bed9ba21693e4583a47773dd701938de63db82e7c324d4c19093046157f0d87905cc20540f194f1d0ec88d6e1bfc4056aea916bcb873ebcf95942d26aa5a languageName: node linkType: hard @@ -26955,11 +27106,11 @@ __metadata: linkType: hard "marked@npm:^4.0.10, marked@npm:^4.0.14": - version: 4.1.1 - resolution: "marked@npm:4.1.1" + version: 4.2.3 + resolution: "marked@npm:4.2.3" bin: marked: bin/marked.js - checksum: 717e3357952ee53de831bf0eb110ed075bebca2376c58bcdf7ee523ef540d45308ad6d51b2c933da0968832ea4386f31c142ca65443e77c098e84f6cce73e418 + checksum: 21b9f6cc4e7695b5e1f251b7b6c98fdc29c25f7ee6c8f4d5c51654856ea36d6222e9a5a227a9b89ccba717dcf7a722022f099aa0dc7caad8e041c996906c23d0 languageName: node linkType: hard @@ -28800,7 +28951,7 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.12.0, object-inspect@npm:^1.12.2, object-inspect@npm:^1.9.0": +"object-inspect@npm:^1.12.2, object-inspect@npm:^1.9.0": version: 1.12.2 resolution: "object-inspect@npm:1.12.2" checksum: a534fc1b8534284ed71f25ce3a496013b7ea030f3d1b77118f6b7b1713829262be9e6243acbcb3ef8c626e2b64186112cb7f6db74e37b2789b9c789ca23048b2 @@ -28814,7 +28965,7 @@ __metadata: languageName: node linkType: hard -"object.assign@npm:^4.1.0, object.assign@npm:^4.1.2": +"object.assign@npm:^4.1.0, object.assign@npm:^4.1.2, object.assign@npm:^4.1.4": version: 4.1.4 resolution: "object.assign@npm:4.1.4" dependencies: @@ -28826,46 +28977,46 @@ __metadata: languageName: node linkType: hard -"object.entries@npm:^1.1.5": - version: 1.1.5 - resolution: "object.entries@npm:1.1.5" +"object.entries@npm:^1.1.6": + version: 1.1.6 + resolution: "object.entries@npm:1.1.6" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.1 - checksum: d658696f74fd222060d8428d2a9fda2ce736b700cb06f6bdf4a16a1892d145afb746f453502b2fa55d1dca8ead6f14ddbcf66c545df45adadea757a6c4cd86c7 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: 0f8c47517e6a9a980241eafe3b73de11e59511883173c2b93d67424a008e47e11b77c80e431ad1d8a806f6108b225a1cab9223e53e555776c612a24297117d28 languageName: node linkType: hard -"object.fromentries@npm:^2.0.5": - version: 2.0.5 - resolution: "object.fromentries@npm:2.0.5" +"object.fromentries@npm:^2.0.6": + version: 2.0.6 + resolution: "object.fromentries@npm:2.0.6" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.1 - checksum: 61a0b565ded97b76df9e30b569729866e1824cce902f98e90bb106e84f378aea20163366f66dc75c9000e2aad2ed0caf65c6f530cb2abc4c0c0f6c982102db4b + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: 453c6d694180c0c30df451b60eaf27a5b9bca3fb43c37908fd2b78af895803dc631242bcf05582173afa40d8d0e9c96e16e8874b39471aa53f3ac1f98a085d85 languageName: node linkType: hard -"object.hasown@npm:^1.1.1": - version: 1.1.1 - resolution: "object.hasown@npm:1.1.1" +"object.hasown@npm:^1.1.2": + version: 1.1.2 + resolution: "object.hasown@npm:1.1.2" dependencies: define-properties: ^1.1.4 - es-abstract: ^1.19.5 - checksum: d8ed4907ce57f48b93e3b53c418fd6787bf226a51e8d698c91e39b78e80fe5b124cb6282f6a9d5be21cf9e2c7829ab10206dcc6112b7748860eefe641880c793 + es-abstract: ^1.20.4 + checksum: b936572536db0cdf38eb30afd2f1026a8b6f2cc5d2c4497c9d9bbb01eaf3e980dead4fd07580cfdd098e6383e5a9db8212d3ea0c6bdd2b5e68c60aa7e3b45566 languageName: node linkType: hard -"object.values@npm:^1.1.5": - version: 1.1.5 - resolution: "object.values@npm:1.1.5" +"object.values@npm:^1.1.5, object.values@npm:^1.1.6": + version: 1.1.6 + resolution: "object.values@npm:1.1.6" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.1 - checksum: 0f17e99741ebfbd0fa55ce942f6184743d3070c61bd39221afc929c8422c4907618c8da694c6915bc04a83ab3224260c779ba37fc07bb668bdc5f33b66a902a4 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: f6fff9fd817c24cfd8107f50fb33061d81cd11bacc4e3dbb3852e9ff7692fde4dbce823d4333ea27cd9637ef1b6690df5fbb61f1ed314fa2959598dc3ae23d8e languageName: node linkType: hard @@ -30630,7 +30781,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.1.0, postcss@npm:^8.4.7": +"postcss@npm:^8.1.0, postcss@npm:^8.4.18": version: 8.4.19 resolution: "postcss@npm:8.4.19" dependencies: @@ -30976,9 +31127,9 @@ __metadata: languageName: node linkType: hard -"protobufjs@npm:7.1.1, protobufjs@npm:^7.0.0": - version: 7.1.1 - resolution: "protobufjs@npm:7.1.1" +"protobufjs@npm:7.1.2, protobufjs@npm:^7.0.0": + version: 7.1.2 + resolution: "protobufjs@npm:7.1.2" dependencies: "@protobufjs/aspromise": ^1.1.2 "@protobufjs/base64": ^1.1.2 @@ -30992,7 +31143,7 @@ __metadata: "@protobufjs/utf8": ^1.1.0 "@types/node": ">=13.7.0" long: ^5.0.0 - checksum: f6b0e7cec29056cefb83902009b39d24ac14a6117b52bc9b291b29778dad010e42d5563a20045e4de89b880114f4214f984396c8296a0d1b22f5dc7d9fe6705e + checksum: ae41669b1b0372fb1d49f506f2d1f2b0fb3dc3cece85987b17bcb544e4cef7c8d27f480486cdec324146ad0a5d22a327166a7ea864a9b3e49cc3c92a5d3f6500 languageName: node linkType: hard @@ -31330,6 +31481,20 @@ __metadata: languageName: node linkType: hard +"rc-progress@npm:3.4.1": + version: 3.4.1 + resolution: "rc-progress@npm:3.4.1" + dependencies: + "@babel/runtime": ^7.10.1 + classnames: ^2.2.6 + rc-util: ^5.16.1 + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: d4dce5231ea29bfa866935a59f05473711f9dfc944f95cc78b0fdcd1508a063983cc58973f54c2b20797f0b0f480c4a2b84aaa2ca185158c14800ec659163880 + languageName: node + linkType: hard + "rc-util@npm:^5.16.1": version: 5.16.1 resolution: "rc-util@npm:5.16.1" @@ -32227,7 +32392,7 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.4.1, regexp.prototype.flags@npm:^1.4.3": +"regexp.prototype.flags@npm:^1.4.3": version: 1.4.3 resolution: "regexp.prototype.flags@npm:1.4.3" dependencies: @@ -32907,6 +33072,7 @@ __metadata: e2e-test: "workspace:*" eslint: ^8.6.0 eslint-plugin-notice: ^0.9.10 + eslint-plugin-testing-library: ^5.9.1 fs-extra: 10.1.0 husky: ^8.0.0 lint-staged: ^13.0.0 @@ -33009,6 +33175,17 @@ __metadata: languageName: node linkType: hard +"safe-regex-test@npm:^1.0.0": + version: 1.0.0 + resolution: "safe-regex-test@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.1.3 + is-regex: ^1.1.4 + checksum: bc566d8beb8b43c01b94e67de3f070fd2781685e835959bbbaaec91cc53381145ca91f69bd837ce6ec244817afa0a5e974fc4e40a2957f0aca68ac3add1ddd34 + languageName: node + linkType: hard + "safe-regex2@npm:^2.0.0": version: 2.0.0 resolution: "safe-regex2@npm:2.0.0" @@ -33212,14 +33389,14 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:~7.3.0": - version: 7.3.7 - resolution: "semver@npm:7.3.7" +"semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:~7.3.0": + version: 7.3.8 + resolution: "semver@npm:7.3.8" dependencies: lru-cache: ^6.0.0 bin: semver: bin/semver.js - checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 + checksum: ba9c7cbbf2b7884696523450a61fee1a09930d888b7a8d7579025ad93d459b2d1949ee5bbfeb188b2be5f4ac163544c5e98491ad6152df34154feebc2cc337c1 languageName: node linkType: hard @@ -34333,19 +34510,19 @@ __metadata: languageName: node linkType: hard -"string.prototype.matchall@npm:^4.0.7": - version: 4.0.7 - resolution: "string.prototype.matchall@npm:4.0.7" +"string.prototype.matchall@npm:^4.0.8": + version: 4.0.8 + resolution: "string.prototype.matchall@npm:4.0.8" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.1 - get-intrinsic: ^1.1.1 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + get-intrinsic: ^1.1.3 has-symbols: ^1.0.3 internal-slot: ^1.0.3 - regexp.prototype.flags: ^1.4.1 + regexp.prototype.flags: ^1.4.3 side-channel: ^1.0.4 - checksum: fc09f3ccbfb325de0472bcc87a6be0598a7499e0b4a31db5789676155b15754a4cc4bb83924f15fc9ed48934dac7366ee52c8b9bd160bed6fd072c93b489e75c + checksum: 952da3a818de42ad1c10b576140a5e05b4de7b34b8d9dbf00c3ac8c1293e9c0f533613a39c5cda53e0a8221f2e710bc2150e730b1c2278d60004a8a35726efb6 languageName: node linkType: hard @@ -34504,6 +34681,13 @@ __metadata: languageName: node linkType: hard +"strnum@npm:^1.0.5": + version: 1.0.5 + resolution: "strnum@npm:1.0.5" + checksum: 651b2031db5da1bf4a77fdd2f116a8ac8055157c5420f5569f64879133825915ad461513e7202a16d7fec63c54fd822410d0962f8ca12385c4334891b9ae6dd2 + languageName: node + linkType: hard + "strtok3@npm:^6.2.4": version: 6.2.4 resolution: "strtok3@npm:6.2.4"