@backstage/frontend-test-utils 0.5.3-next.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @backstage/frontend-test-utils
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 44d77e9: **BREAKING**: `renderInTestApp` no longer renders a sidebar or legacy `nav-item` extensions. The app nav extension is now disabled in the minimal test app shell, along with the layout and routes extensions.
8
+
9
+ If your tests passed `features` containing `nav-item` extensions and asserted on links or labels in that stub sidebar, switch to `renderTestApp` instead — it uses the real app shell and discovers nav items from page extensions.
10
+
11
+ If you only use `renderInTestApp` to mount a component with APIs or route refs, there is no change.
12
+
13
+ ### Patch Changes
14
+
15
+ - 0c298f7: Removed internal `mockWithApiFactory` helper in favor of using `attachMockApiFactory` directly.
16
+ - fa363f9: Added support for `ExternalRouteRef` in the `mountedRoutes` option of `renderInTestApp` and `renderTestApp`.
17
+ - 9279ea8: Added explicit type annotations to `.map()` callback parameters in `renderInTestApp` to avoid implicit `any` errors with newer TypeScript versions.
18
+ - Updated dependencies
19
+ - @backstage/plugin-app@0.4.6
20
+ - @backstage/frontend-app-api@0.16.3
21
+ - @backstage/frontend-plugin-api@0.17.0
22
+ - @backstage/core-plugin-api@1.12.6
23
+ - @backstage/filter-predicates@0.1.3
24
+ - @backstage/plugin-permission-common@0.9.9
25
+ - @backstage/plugin-app-react@0.2.3
26
+ - @backstage/config@1.3.8
27
+ - @backstage/core-app-api@1.20.1
28
+ - @backstage/test-utils@1.7.18
29
+ - @backstage/plugin-permission-react@0.5.1
30
+
3
31
  ## 0.5.3-next.1
4
32
 
5
33
  ### Patch Changes
@@ -1,10 +1,10 @@
1
- import { jsx, jsxs } from 'react/jsx-runtime';
1
+ import { jsx } from 'react/jsx-runtime';
2
2
  import { Fragment } from 'react';
3
- import { Link, MemoryRouter } from 'react-router-dom';
3
+ import { MemoryRouter } from 'react-router-dom';
4
4
  import { prepareSpecializedApp } from '@backstage/frontend-app-api';
5
5
  import { render } from '@testing-library/react';
6
6
  import { ConfigReader } from '@backstage/config';
7
- import { coreExtensionData, NavItemBlueprint, useRouteRef, createExtension, createRouteRef, createFrontendModule, createFrontendPlugin, createApiFactory } from '@backstage/frontend-plugin-api';
7
+ import { createExtension, coreExtensionData, createRouteRef, createFrontendModule, createFrontendPlugin, createApiFactory } from '@backstage/frontend-plugin-api';
8
8
  import { RouterBlueprint } from '@backstage/plugin-app-react';
9
9
  import appPlugin from '@backstage/plugin-app';
10
10
  import { getMockApiFactory } from '../apis/MockWithApiFactory.esm.js';
@@ -16,18 +16,6 @@ const DEFAULT_MOCK_CONFIG = {
16
16
  app: { baseUrl: "http://localhost:3000" },
17
17
  backend: { baseUrl: "http://localhost:7007" }
18
18
  };
19
- const NavItem = (props) => {
20
- const { routeRef, title, icon: Icon } = props;
21
- const link = useRouteRef(routeRef);
22
- if (!link) {
23
- return null;
24
- }
25
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Link, { to: link(), children: [
26
- /* @__PURE__ */ jsx(Icon, {}),
27
- " ",
28
- title
29
- ] }) });
30
- };
31
19
  const appPluginOverride = appPlugin.withOverrides({
32
20
  extensions: [
33
21
  appPlugin.getExtension("sign-in-page:app").override({
@@ -40,29 +28,7 @@ const appPluginOverride = appPlugin.withOverrides({
40
28
  disabled: true
41
29
  }),
42
30
  appPlugin.getExtension("app/nav").override({
43
- output: [coreExtensionData.reactElement],
44
- factory(_originalFactory, { inputs }) {
45
- return [
46
- coreExtensionData.reactElement(
47
- /* @__PURE__ */ jsx("nav", { children: /* @__PURE__ */ jsx("ul", { children: inputs.items.map(
48
- (item, index) => {
49
- const { icon, title, routeRef } = item.get(
50
- NavItemBlueprint.dataRefs.target
51
- );
52
- return /* @__PURE__ */ jsx(
53
- NavItem,
54
- {
55
- icon,
56
- title,
57
- routeRef
58
- },
59
- index
60
- );
61
- }
62
- ) }) })
63
- )
64
- ];
65
- }
31
+ disabled: true
66
32
  })
67
33
  ]
68
34
  });
@@ -1 +1 @@
1
- {"version":3,"file":"renderInTestApp.esm.js","sources":["../../src/app/renderInTestApp.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Fragment } from 'react';\nimport { Link, MemoryRouter } from 'react-router-dom';\nimport { prepareSpecializedApp } from '@backstage/frontend-app-api';\nimport { RenderResult, render } from '@testing-library/react';\nimport { ConfigReader } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\nimport {\n createExtension,\n ExtensionDefinition,\n coreExtensionData,\n RouteRef,\n useRouteRef,\n IconComponent,\n NavItemBlueprint,\n createFrontendPlugin,\n FrontendFeature,\n createFrontendModule,\n createApiFactory,\n createRouteRef,\n ExternalRouteRef,\n type ApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { RouterBlueprint } from '@backstage/plugin-app-react';\nimport appPlugin from '@backstage/plugin-app';\nimport { getMockApiFactory } from '../apis/MockWithApiFactory';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport type { CreateSpecializedAppInternalOptions } from '../../../frontend-app-api/src/wiring/createSpecializedApp';\nimport { TestApiPairs } from '../apis/TestApiProvider';\nimport { OpaqueExternalRouteRef } from '@internal/frontend';\n\nconst DEFAULT_MOCK_CONFIG = {\n app: { baseUrl: 'http://localhost:3000' },\n backend: { baseUrl: 'http://localhost:7007' },\n};\n\n/**\n * Options to customize the behavior of the test app.\n * @public\n */\nexport type TestAppOptions<TApiPairs extends any[] = any[]> = {\n /**\n * An object of paths to mount route ref on, with the key being the path and the value\n * being the route ref that the path will be bound to. This allows the route refs to be\n * used by `useRouteRef` in the rendered elements.\n *\n * @example\n * ```ts\n * renderInTestApp(<MyComponent />, {\n * mountedRoutes: {\n * '/my-path': myRouteRef,\n * }\n * })\n * // ...\n * const link = useRouteRef(myRouteRef)\n * ```\n */\n mountedRoutes?: { [path: string]: RouteRef | ExternalRouteRef };\n\n /**\n * Additional configuration passed to the app when rendering elements inside it.\n */\n config?: JsonObject;\n\n /**\n * Additional features to add to the test app.\n */\n features?: FrontendFeature[];\n\n /**\n * Initial route entries to use for the router.\n */\n initialRouteEntries?: string[];\n\n /**\n * API overrides to provide to the test app. Use `mockApis` helpers\n * from `@backstage/frontend-test-utils` to create mock implementations.\n *\n * @example\n * ```ts\n * import { mockApis } from '@backstage/frontend-test-utils';\n *\n * renderInTestApp(<MyComponent />, {\n * apis: [mockApis.identity({ userEntityRef: 'user:default/guest' })],\n * })\n * ```\n */\n apis?: readonly [...TestApiPairs<TApiPairs>];\n};\n\nconst NavItem = (props: {\n routeRef: RouteRef<undefined>;\n title: string;\n icon: IconComponent;\n}) => {\n const { routeRef, title, icon: Icon } = props;\n const link = useRouteRef(routeRef);\n if (!link) {\n return null;\n }\n return (\n <li>\n <Link to={link()}>\n <Icon /> {title}\n </Link>\n </li>\n );\n};\n\nconst appPluginOverride = appPlugin.withOverrides({\n extensions: [\n appPlugin.getExtension('sign-in-page:app').override({\n disabled: true,\n }),\n appPlugin.getExtension('app/layout').override({\n disabled: true,\n }),\n appPlugin.getExtension('app/routes').override({\n disabled: true,\n }),\n appPlugin.getExtension('app/nav').override({\n output: [coreExtensionData.reactElement],\n factory(_originalFactory, { inputs }) {\n return [\n coreExtensionData.reactElement(\n <nav>\n <ul>\n {inputs.items.map(\n (item: (typeof inputs.items)[number], index: number) => {\n const { icon, title, routeRef } = item.get(\n NavItemBlueprint.dataRefs.target,\n );\n\n return (\n <NavItem\n key={index}\n icon={icon}\n title={title}\n routeRef={routeRef}\n />\n );\n },\n )}\n </ul>\n </nav>,\n ),\n ];\n },\n }),\n ],\n});\n\n/**\n * @public\n * Renders the given element in a test app, for use in unit tests.\n */\nexport function renderInTestApp<const TApiPairs extends any[] = any[]>(\n element: JSX.Element,\n options?: TestAppOptions<TApiPairs>,\n): RenderResult {\n const extensions: Array<ExtensionDefinition> = [\n createExtension({\n attachTo: { id: 'app/root', input: 'children' },\n output: [coreExtensionData.reactElement],\n factory: () => {\n return [coreExtensionData.reactElement(element)];\n },\n }),\n ];\n\n const externalBindings = new Map<ExternalRouteRef, RouteRef>();\n\n if (options?.mountedRoutes) {\n for (const [path, optionRef] of Object.entries(options.mountedRoutes)) {\n let routeRef: RouteRef;\n\n if (OpaqueExternalRouteRef.isType(optionRef)) {\n // Create an actual route ref for the external route, then bind the external ref to it\n routeRef = createRouteRef();\n externalBindings.set(optionRef, routeRef);\n } else {\n routeRef = optionRef;\n }\n\n extensions.push(\n createExtension({\n kind: 'test-route',\n name: path,\n attachTo: { id: 'app/root', input: 'elements' },\n output: [\n coreExtensionData.reactElement,\n coreExtensionData.routePath,\n coreExtensionData.routeRef,\n ],\n factory: () => [\n coreExtensionData.reactElement(<Fragment />),\n coreExtensionData.routePath(path),\n coreExtensionData.routeRef(routeRef),\n ],\n }),\n );\n }\n }\n\n const features: FrontendFeature[] = [\n createFrontendModule({\n pluginId: 'app',\n extensions: [\n RouterBlueprint.make({\n params: {\n component: ({ children }) => (\n <MemoryRouter\n initialEntries={options?.initialRouteEntries}\n future={{\n v7_relativeSplatPath: false,\n v7_startTransition: false,\n }}\n >\n {children}\n </MemoryRouter>\n ),\n },\n }),\n ],\n }),\n createFrontendPlugin({\n pluginId: 'test',\n extensions,\n }),\n appPluginOverride,\n ];\n\n if (options?.features) {\n features.push(...options.features);\n }\n\n const app = prepareSpecializedApp({\n features,\n config: ConfigReader.fromConfigs([\n {\n context: 'render-config',\n data: options?.config ?? DEFAULT_MOCK_CONFIG,\n },\n ]),\n __internal: options?.apis && {\n apiFactoryOverrides: options.apis.map(entry => {\n const mockFactory = getMockApiFactory(entry);\n if (mockFactory) {\n return mockFactory;\n }\n const [apiRef, implementation] = entry as readonly [ApiRef<any>, any];\n return createApiFactory(apiRef, implementation);\n }),\n },\n bindRoutes:\n externalBindings.size > 0\n ? ({ bind }) => {\n for (const [externalRef, targetRef] of externalBindings) {\n bind({ ref: externalRef }, { ref: targetRef });\n }\n }\n : undefined,\n } as CreateSpecializedAppInternalOptions).finalize();\n\n return render(\n app.tree.root.instance!.getData(coreExtensionData.reactElement),\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"renderInTestApp.esm.js","sources":["../../src/app/renderInTestApp.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Fragment } from 'react';\nimport { MemoryRouter } from 'react-router-dom';\nimport { prepareSpecializedApp } from '@backstage/frontend-app-api';\nimport { RenderResult, render } from '@testing-library/react';\nimport { ConfigReader } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\nimport {\n createExtension,\n ExtensionDefinition,\n coreExtensionData,\n RouteRef,\n createFrontendPlugin,\n FrontendFeature,\n createFrontendModule,\n createApiFactory,\n createRouteRef,\n ExternalRouteRef,\n type ApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { RouterBlueprint } from '@backstage/plugin-app-react';\nimport appPlugin from '@backstage/plugin-app';\nimport { getMockApiFactory } from '../apis/MockWithApiFactory';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport type { CreateSpecializedAppInternalOptions } from '../../../frontend-app-api/src/wiring/createSpecializedApp';\nimport { TestApiPairs } from '../apis/TestApiProvider';\nimport { OpaqueExternalRouteRef } from '@internal/frontend';\n\nconst DEFAULT_MOCK_CONFIG = {\n app: { baseUrl: 'http://localhost:3000' },\n backend: { baseUrl: 'http://localhost:7007' },\n};\n\n/**\n * Options to customize the behavior of the test app.\n * @public\n */\nexport type TestAppOptions<TApiPairs extends any[] = any[]> = {\n /**\n * An object of paths to mount route ref on, with the key being the path and the value\n * being the route ref that the path will be bound to. This allows the route refs to be\n * used by `useRouteRef` in the rendered elements.\n *\n * @example\n * ```ts\n * renderInTestApp(<MyComponent />, {\n * mountedRoutes: {\n * '/my-path': myRouteRef,\n * }\n * })\n * // ...\n * const link = useRouteRef(myRouteRef)\n * ```\n */\n mountedRoutes?: { [path: string]: RouteRef | ExternalRouteRef };\n\n /**\n * Additional configuration passed to the app when rendering elements inside it.\n */\n config?: JsonObject;\n\n /**\n * Additional features to add to the test app.\n */\n features?: FrontendFeature[];\n\n /**\n * Initial route entries to use for the router.\n */\n initialRouteEntries?: string[];\n\n /**\n * API overrides to provide to the test app. Use `mockApis` helpers\n * from `@backstage/frontend-test-utils` to create mock implementations.\n *\n * @example\n * ```ts\n * import { mockApis } from '@backstage/frontend-test-utils';\n *\n * renderInTestApp(<MyComponent />, {\n * apis: [mockApis.identity({ userEntityRef: 'user:default/guest' })],\n * })\n * ```\n */\n apis?: readonly [...TestApiPairs<TApiPairs>];\n};\n\nconst appPluginOverride = appPlugin.withOverrides({\n extensions: [\n appPlugin.getExtension('sign-in-page:app').override({\n disabled: true,\n }),\n appPlugin.getExtension('app/layout').override({\n disabled: true,\n }),\n appPlugin.getExtension('app/routes').override({\n disabled: true,\n }),\n appPlugin.getExtension('app/nav').override({\n disabled: true,\n }),\n ],\n});\n\n/**\n * @public\n * Renders the given element in a test app, for use in unit tests.\n */\nexport function renderInTestApp<const TApiPairs extends any[] = any[]>(\n element: JSX.Element,\n options?: TestAppOptions<TApiPairs>,\n): RenderResult {\n const extensions: Array<ExtensionDefinition> = [\n createExtension({\n attachTo: { id: 'app/root', input: 'children' },\n output: [coreExtensionData.reactElement],\n factory: () => {\n return [coreExtensionData.reactElement(element)];\n },\n }),\n ];\n\n const externalBindings = new Map<ExternalRouteRef, RouteRef>();\n\n if (options?.mountedRoutes) {\n for (const [path, optionRef] of Object.entries(options.mountedRoutes)) {\n let routeRef: RouteRef;\n\n if (OpaqueExternalRouteRef.isType(optionRef)) {\n // Create an actual route ref for the external route, then bind the external ref to it\n routeRef = createRouteRef();\n externalBindings.set(optionRef, routeRef);\n } else {\n routeRef = optionRef;\n }\n\n extensions.push(\n createExtension({\n kind: 'test-route',\n name: path,\n attachTo: { id: 'app/root', input: 'elements' },\n output: [\n coreExtensionData.reactElement,\n coreExtensionData.routePath,\n coreExtensionData.routeRef,\n ],\n factory: () => [\n coreExtensionData.reactElement(<Fragment />),\n coreExtensionData.routePath(path),\n coreExtensionData.routeRef(routeRef),\n ],\n }),\n );\n }\n }\n\n const features: FrontendFeature[] = [\n createFrontendModule({\n pluginId: 'app',\n extensions: [\n RouterBlueprint.make({\n params: {\n component: ({ children }) => (\n <MemoryRouter\n initialEntries={options?.initialRouteEntries}\n future={{\n v7_relativeSplatPath: false,\n v7_startTransition: false,\n }}\n >\n {children}\n </MemoryRouter>\n ),\n },\n }),\n ],\n }),\n createFrontendPlugin({\n pluginId: 'test',\n extensions,\n }),\n appPluginOverride,\n ];\n\n if (options?.features) {\n features.push(...options.features);\n }\n\n const app = prepareSpecializedApp({\n features,\n config: ConfigReader.fromConfigs([\n {\n context: 'render-config',\n data: options?.config ?? DEFAULT_MOCK_CONFIG,\n },\n ]),\n __internal: options?.apis && {\n apiFactoryOverrides: options.apis.map(entry => {\n const mockFactory = getMockApiFactory(entry);\n if (mockFactory) {\n return mockFactory;\n }\n const [apiRef, implementation] = entry as readonly [ApiRef<any>, any];\n return createApiFactory(apiRef, implementation);\n }),\n },\n bindRoutes:\n externalBindings.size > 0\n ? ({ bind }) => {\n for (const [externalRef, targetRef] of externalBindings) {\n bind({ ref: externalRef }, { ref: targetRef });\n }\n }\n : undefined,\n } as CreateSpecializedAppInternalOptions).finalize();\n\n return render(\n app.tree.root.instance!.getData(coreExtensionData.reactElement),\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;AA2CA,MAAM,mBAAA,GAAsB;AAAA,EAC1B,GAAA,EAAK,EAAE,OAAA,EAAS,uBAAA,EAAwB;AAAA,EACxC,OAAA,EAAS,EAAE,OAAA,EAAS,uBAAA;AACtB,CAAA;AAwDA,MAAM,iBAAA,GAAoB,UAAU,aAAA,CAAc;AAAA,EAChD,UAAA,EAAY;AAAA,IACV,SAAA,CAAU,YAAA,CAAa,kBAAkB,CAAA,CAAE,QAAA,CAAS;AAAA,MAClD,QAAA,EAAU;AAAA,KACX,CAAA;AAAA,IACD,SAAA,CAAU,YAAA,CAAa,YAAY,CAAA,CAAE,QAAA,CAAS;AAAA,MAC5C,QAAA,EAAU;AAAA,KACX,CAAA;AAAA,IACD,SAAA,CAAU,YAAA,CAAa,YAAY,CAAA,CAAE,QAAA,CAAS;AAAA,MAC5C,QAAA,EAAU;AAAA,KACX,CAAA;AAAA,IACD,SAAA,CAAU,YAAA,CAAa,SAAS,CAAA,CAAE,QAAA,CAAS;AAAA,MACzC,QAAA,EAAU;AAAA,KACX;AAAA;AAEL,CAAC,CAAA;AAMM,SAAS,eAAA,CACd,SACA,OAAA,EACc;AACd,EAAA,MAAM,UAAA,GAAyC;AAAA,IAC7C,eAAA,CAAgB;AAAA,MACd,QAAA,EAAU,EAAE,EAAA,EAAI,UAAA,EAAY,OAAO,UAAA,EAAW;AAAA,MAC9C,MAAA,EAAQ,CAAC,iBAAA,CAAkB,YAAY,CAAA;AAAA,MACvC,SAAS,MAAM;AACb,QAAA,OAAO,CAAC,iBAAA,CAAkB,YAAA,CAAa,OAAO,CAAC,CAAA;AAAA,MACjD;AAAA,KACD;AAAA,GACH;AAEA,EAAA,MAAM,gBAAA,uBAAuB,GAAA,EAAgC;AAE7D,EAAA,IAAI,SAAS,aAAA,EAAe;AAC1B,IAAA,KAAA,MAAW,CAAC,MAAM,SAAS,CAAA,IAAK,OAAO,OAAA,CAAQ,OAAA,CAAQ,aAAa,CAAA,EAAG;AACrE,MAAA,IAAI,QAAA;AAEJ,MAAA,IAAI,sBAAA,CAAuB,MAAA,CAAO,SAAS,CAAA,EAAG;AAE5C,QAAA,QAAA,GAAW,cAAA,EAAe;AAC1B,QAAA,gBAAA,CAAiB,GAAA,CAAI,WAAW,QAAQ,CAAA;AAAA,MAC1C,CAAA,MAAO;AACL,QAAA,QAAA,GAAW,SAAA;AAAA,MACb;AAEA,MAAA,UAAA,CAAW,IAAA;AAAA,QACT,eAAA,CAAgB;AAAA,UACd,IAAA,EAAM,YAAA;AAAA,UACN,IAAA,EAAM,IAAA;AAAA,UACN,QAAA,EAAU,EAAE,EAAA,EAAI,UAAA,EAAY,OAAO,UAAA,EAAW;AAAA,UAC9C,MAAA,EAAQ;AAAA,YACN,iBAAA,CAAkB,YAAA;AAAA,YAClB,iBAAA,CAAkB,SAAA;AAAA,YAClB,iBAAA,CAAkB;AAAA,WACpB;AAAA,UACA,SAAS,MAAM;AAAA,YACb,iBAAA,CAAkB,YAAA,iBAAa,GAAA,CAAC,QAAA,EAAA,EAAS,CAAE,CAAA;AAAA,YAC3C,iBAAA,CAAkB,UAAU,IAAI,CAAA;AAAA,YAChC,iBAAA,CAAkB,SAAS,QAAQ;AAAA;AACrC,SACD;AAAA,OACH;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAA8B;AAAA,IAClC,oBAAA,CAAqB;AAAA,MACnB,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY;AAAA,QACV,gBAAgB,IAAA,CAAK;AAAA,UACnB,MAAA,EAAQ;AAAA,YACN,SAAA,EAAW,CAAC,EAAE,QAAA,EAAS,qBACrB,GAAA;AAAA,cAAC,YAAA;AAAA,cAAA;AAAA,gBACC,gBAAgB,OAAA,EAAS,mBAAA;AAAA,gBACzB,MAAA,EAAQ;AAAA,kBACN,oBAAA,EAAsB,KAAA;AAAA,kBACtB,kBAAA,EAAoB;AAAA,iBACtB;AAAA,gBAEC;AAAA;AAAA;AACH;AAEJ,SACD;AAAA;AACH,KACD,CAAA;AAAA,IACD,oBAAA,CAAqB;AAAA,MACnB,QAAA,EAAU,MAAA;AAAA,MACV;AAAA,KACD,CAAA;AAAA,IACD;AAAA,GACF;AAEA,EAAA,IAAI,SAAS,QAAA,EAAU;AACrB,IAAA,QAAA,CAAS,IAAA,CAAK,GAAG,OAAA,CAAQ,QAAQ,CAAA;AAAA,EACnC;AAEA,EAAA,MAAM,MAAM,qBAAA,CAAsB;AAAA,IAChC,QAAA;AAAA,IACA,MAAA,EAAQ,aAAa,WAAA,CAAY;AAAA,MAC/B;AAAA,QACE,OAAA,EAAS,eAAA;AAAA,QACT,IAAA,EAAM,SAAS,MAAA,IAAU;AAAA;AAC3B,KACD,CAAA;AAAA,IACD,UAAA,EAAY,SAAS,IAAA,IAAQ;AAAA,MAC3B,mBAAA,EAAqB,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,KAAA,KAAS;AAC7C,QAAA,MAAM,WAAA,GAAc,kBAAkB,KAAK,CAAA;AAC3C,QAAA,IAAI,WAAA,EAAa;AACf,UAAA,OAAO,WAAA;AAAA,QACT;AACA,QAAA,MAAM,CAAC,MAAA,EAAQ,cAAc,CAAA,GAAI,KAAA;AACjC,QAAA,OAAO,gBAAA,CAAiB,QAAQ,cAAc,CAAA;AAAA,MAChD,CAAC;AAAA,KACH;AAAA,IACA,YACE,gBAAA,CAAiB,IAAA,GAAO,IACpB,CAAC,EAAE,MAAK,KAAM;AACZ,MAAA,KAAA,MAAW,CAAC,WAAA,EAAa,SAAS,CAAA,IAAK,gBAAA,EAAkB;AACvD,QAAA,IAAA,CAAK,EAAE,GAAA,EAAK,WAAA,IAAe,EAAE,GAAA,EAAK,WAAW,CAAA;AAAA,MAC/C;AAAA,IACF,CAAA,GACA;AAAA,GACgC,EAAE,QAAA,EAAS;AAEnD,EAAA,OAAO,MAAA;AAAA,IACL,IAAI,IAAA,CAAK,IAAA,CAAK,QAAA,CAAU,OAAA,CAAQ,kBAAkB,YAAY;AAAA,GAChE;AACF;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/frontend-test-utils",
3
- "version": "0.5.3-next.1",
3
+ "version": "0.6.0",
4
4
  "backstage": {
5
5
  "role": "web-library"
6
6
  },
@@ -31,25 +31,25 @@
31
31
  "test": "backstage-cli package test"
32
32
  },
33
33
  "dependencies": {
34
- "@backstage/config": "1.3.8-next.0",
35
- "@backstage/core-app-api": "1.20.1-next.0",
36
- "@backstage/core-plugin-api": "1.12.6-next.1",
37
- "@backstage/filter-predicates": "0.1.3-next.0",
38
- "@backstage/frontend-app-api": "0.16.3-next.1",
39
- "@backstage/frontend-plugin-api": "0.17.0-next.1",
40
- "@backstage/plugin-app": "0.4.6-next.1",
41
- "@backstage/plugin-app-react": "0.2.3-next.0",
42
- "@backstage/plugin-permission-common": "0.9.9-next.1",
43
- "@backstage/plugin-permission-react": "0.5.1-next.0",
44
- "@backstage/test-utils": "1.7.18-next.0",
45
- "@backstage/types": "1.2.2",
46
- "@backstage/version-bridge": "1.0.12",
34
+ "@backstage/config": "^1.3.8",
35
+ "@backstage/core-app-api": "^1.20.1",
36
+ "@backstage/core-plugin-api": "^1.12.6",
37
+ "@backstage/filter-predicates": "^0.1.3",
38
+ "@backstage/frontend-app-api": "^0.16.3",
39
+ "@backstage/frontend-plugin-api": "^0.17.0",
40
+ "@backstage/plugin-app": "^0.4.6",
41
+ "@backstage/plugin-app-react": "^0.2.3",
42
+ "@backstage/plugin-permission-common": "^0.9.9",
43
+ "@backstage/plugin-permission-react": "^0.5.1",
44
+ "@backstage/test-utils": "^1.7.18",
45
+ "@backstage/types": "^1.2.2",
46
+ "@backstage/version-bridge": "^1.0.12",
47
47
  "i18next": "^22.4.15",
48
48
  "zen-observable": "^0.10.0",
49
49
  "zod": "^3.25.76 || ^4.0.0"
50
50
  },
51
51
  "devDependencies": {
52
- "@backstage/cli": "0.36.2-next.1",
52
+ "@backstage/cli": "^0.36.2",
53
53
  "@testing-library/jest-dom": "^6.0.0",
54
54
  "@types/jest": "*",
55
55
  "@types/react": "^18.0.0",