@backstage/frontend-test-utils 0.5.2 → 0.5.3-next.1
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 +33 -0
- package/dist/apis/MockWithApiFactory.esm.js +1 -10
- package/dist/apis/MockWithApiFactory.esm.js.map +1 -1
- package/dist/apis/mockApis.esm.js +15 -12
- package/dist/apis/mockApis.esm.js.map +1 -1
- package/dist/app/renderInTestApp.esm.js +35 -17
- package/dist/app/renderInTestApp.esm.js.map +1 -1
- package/dist/app/renderTestApp.esm.js +19 -3
- package/dist/app/renderTestApp.esm.js.map +1 -1
- package/dist/frontend-app-api/src/wiring/createErrorCollector.esm.js.map +1 -1
- package/dist/frontend-internal/src/routing/OpaqueExternalRouteRef.esm.js +9 -0
- package/dist/frontend-internal/src/routing/OpaqueExternalRouteRef.esm.js.map +1 -0
- package/dist/frontend-internal/src/routing/OpaqueRouteRef.esm.js +7 -0
- package/dist/frontend-internal/src/routing/OpaqueRouteRef.esm.js.map +1 -0
- package/dist/frontend-internal/src/routing/OpaqueSubRouteRef.esm.js +7 -0
- package/dist/frontend-internal/src/routing/OpaqueSubRouteRef.esm.js.map +1 -0
- package/dist/index.d.ts +5 -5
- package/package.json +15 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @backstage/frontend-test-utils
|
|
2
2
|
|
|
3
|
+
## 0.5.3-next.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fa363f9: Added support for `ExternalRouteRef` in the `mountedRoutes` option of `renderInTestApp` and `renderTestApp`.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/frontend-plugin-api@0.17.0-next.1
|
|
10
|
+
- @backstage/core-plugin-api@1.12.6-next.1
|
|
11
|
+
- @backstage/frontend-app-api@0.16.3-next.1
|
|
12
|
+
- @backstage/plugin-app@0.4.6-next.1
|
|
13
|
+
- @backstage/plugin-permission-common@0.9.9-next.1
|
|
14
|
+
|
|
15
|
+
## 0.5.3-next.0
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 0c298f7: Removed internal `mockWithApiFactory` helper in favor of using `attachMockApiFactory` directly.
|
|
20
|
+
- 9279ea8: Added explicit type annotations to `.map()` callback parameters in `renderInTestApp` to avoid implicit `any` errors with newer TypeScript versions.
|
|
21
|
+
- Updated dependencies
|
|
22
|
+
- @backstage/plugin-app@0.4.6-next.0
|
|
23
|
+
- @backstage/frontend-app-api@0.16.3-next.0
|
|
24
|
+
- @backstage/frontend-plugin-api@0.17.0-next.0
|
|
25
|
+
- @backstage/core-app-api@1.20.1-next.0
|
|
26
|
+
- @backstage/config@1.3.8-next.0
|
|
27
|
+
- @backstage/core-plugin-api@1.12.6-next.0
|
|
28
|
+
- @backstage/filter-predicates@0.1.3-next.0
|
|
29
|
+
- @backstage/plugin-permission-common@0.9.9-next.0
|
|
30
|
+
- @backstage/plugin-app-react@0.2.3-next.0
|
|
31
|
+
- @backstage/test-utils@1.7.18-next.0
|
|
32
|
+
- @backstage/types@1.2.2
|
|
33
|
+
- @backstage/version-bridge@1.0.12
|
|
34
|
+
- @backstage/plugin-permission-react@0.5.1-next.0
|
|
35
|
+
|
|
3
36
|
## 0.5.2
|
|
4
37
|
|
|
5
38
|
### Patch Changes
|
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
const mockApiFactorySymbol = /* @__PURE__ */ Symbol.for("@backstage/mock-api");
|
|
2
|
-
function mockWithApiFactory(apiRef, implementation) {
|
|
3
|
-
const marked = implementation;
|
|
4
|
-
marked[mockApiFactorySymbol] = {
|
|
5
|
-
api: apiRef,
|
|
6
|
-
deps: {},
|
|
7
|
-
factory: () => implementation
|
|
8
|
-
};
|
|
9
|
-
return marked;
|
|
10
|
-
}
|
|
11
2
|
function attachMockApiFactory(apiRef, implementation) {
|
|
12
3
|
const marked = implementation;
|
|
13
4
|
marked[mockApiFactorySymbol] = {
|
|
@@ -24,5 +15,5 @@ function getMockApiFactory(value) {
|
|
|
24
15
|
return void 0;
|
|
25
16
|
}
|
|
26
17
|
|
|
27
|
-
export { attachMockApiFactory, getMockApiFactory, mockApiFactorySymbol
|
|
18
|
+
export { attachMockApiFactory, getMockApiFactory, mockApiFactorySymbol };
|
|
28
19
|
//# sourceMappingURL=MockWithApiFactory.esm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MockWithApiFactory.esm.js","sources":["../../src/apis/MockWithApiFactory.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { ApiRef } from '@backstage/frontend-plugin-api';\nimport { ApiFactory } from '@backstage/frontend-plugin-api';\n\n/**\n * Symbol used to mark mock API instances with their corresponding API factory.\n *\n * @ignore\n */\nexport const mockApiFactorySymbol = Symbol.for('@backstage/mock-api');\n\n/**\n * Symbol used to mark mock API instances with their corresponding API factory.\n * This allows mock APIs to be passed directly to test utilities without\n * needing to explicitly provide the [apiRef, implementation] tuple.\n *\n * @public\n */\nexport type MockApiFactorySymbol = typeof mockApiFactorySymbol;\n\n/**\n * Type for an API instance that has been marked as a mock API.\n *\n * @public\n */\nexport type MockWithApiFactory<TApi> = TApi & {\n [mockApiFactorySymbol]: ApiFactory<TApi, TApi, {}>;\n};\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"MockWithApiFactory.esm.js","sources":["../../src/apis/MockWithApiFactory.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { ApiRef } from '@backstage/frontend-plugin-api';\nimport { ApiFactory } from '@backstage/frontend-plugin-api';\n\n/**\n * Symbol used to mark mock API instances with their corresponding API factory.\n *\n * @ignore\n */\nexport const mockApiFactorySymbol = Symbol.for('@backstage/mock-api');\n\n/**\n * Symbol used to mark mock API instances with their corresponding API factory.\n * This allows mock APIs to be passed directly to test utilities without\n * needing to explicitly provide the [apiRef, implementation] tuple.\n *\n * @public\n */\nexport type MockApiFactorySymbol = typeof mockApiFactorySymbol;\n\n/**\n * Type for an API instance that has been marked as a mock API.\n *\n * @public\n */\nexport type MockWithApiFactory<TApi> = TApi & {\n [mockApiFactorySymbol]: ApiFactory<TApi, TApi, {}>;\n};\n\n/**\n * Attaches mock API factory metadata to an API instance, allowing it to be\n * passed directly to test utilities without needing to explicitly provide\n * the [apiRef, implementation] tuple.\n *\n * @public\n * @example\n * ```ts\n * const catalogApi = attachMockApiFactory(\n * catalogApiRef,\n * new InMemoryCatalogClient()\n * );\n * // Can now be passed directly to TestApiProvider\n * <TestApiProvider apis={[catalogApi]}>\n * ```\n */\nexport function attachMockApiFactory<TApi, TImpl extends TApi = TApi>(\n apiRef: ApiRef<TApi>,\n implementation: TImpl,\n): TImpl & { [mockApiFactorySymbol]: ApiFactory<TApi, TApi, {}> } {\n const marked = implementation as TImpl & {\n [mockApiFactorySymbol]: ApiFactory<TApi, TApi, {}>;\n };\n (marked as any)[mockApiFactorySymbol] = {\n api: apiRef,\n deps: {},\n factory: () => implementation,\n };\n return marked;\n}\n\n/**\n * Retrieves the API factory from a mock API instance.\n * Returns undefined if the value is not a mock API instance.\n *\n * @internal\n */\nexport function getMockApiFactory(\n value: unknown,\n): ApiFactory<any, any, {}> | undefined {\n if (\n typeof value === 'object' &&\n value !== null &&\n mockApiFactorySymbol in value &&\n typeof (value as any)[mockApiFactorySymbol] === 'object'\n ) {\n return (value as any)[mockApiFactorySymbol];\n }\n return undefined;\n}\n"],"names":[],"mappings":"AAwBO,MAAM,oBAAA,mBAAuB,MAAA,CAAO,GAAA,CAAI,qBAAqB;AAoC7D,SAAS,oBAAA,CACd,QACA,cAAA,EACgE;AAChE,EAAA,MAAM,MAAA,GAAS,cAAA;AAGf,EAAC,MAAA,CAAe,oBAAoB,CAAA,GAAI;AAAA,IACtC,GAAA,EAAK,MAAA;AAAA,IACL,MAAM,EAAC;AAAA,IACP,SAAS,MAAM;AAAA,GACjB;AACA,EAAA,OAAO,MAAA;AACT;AAQO,SAAS,kBACd,KAAA,EACsC;AACtC,EAAA,IACE,OAAO,KAAA,KAAU,QAAA,IACjB,KAAA,KAAU,IAAA,IACV,oBAAA,IAAwB,KAAA,IACxB,OAAQ,KAAA,CAAc,oBAAoB,CAAA,KAAM,QAAA,EAChD;AACA,IAAA,OAAQ,MAAc,oBAAoB,CAAA;AAAA,EAC5C;AACA,EAAA,OAAO,MAAA;AACT;;;;"}
|
|
@@ -10,14 +10,14 @@ import { MockFetchApi } from './FetchApi/MockFetchApi.esm.js';
|
|
|
10
10
|
import { MockStorageApi } from './StorageApi/MockStorageApi.esm.js';
|
|
11
11
|
import { MockPermissionApi } from './PermissionApi/MockPermissionApi.esm.js';
|
|
12
12
|
import { MockTranslationApi } from './TranslationApi/MockTranslationApi.esm.js';
|
|
13
|
-
import {
|
|
13
|
+
import { attachMockApiFactory } from './MockWithApiFactory.esm.js';
|
|
14
14
|
import { createApiMock } from './createApiMock.esm.js';
|
|
15
15
|
|
|
16
16
|
var mockApis;
|
|
17
17
|
((mockApis2) => {
|
|
18
18
|
function alert() {
|
|
19
19
|
const instance = new MockAlertApi();
|
|
20
|
-
return
|
|
20
|
+
return attachMockApiFactory(
|
|
21
21
|
alertApiRef,
|
|
22
22
|
instance
|
|
23
23
|
);
|
|
@@ -31,7 +31,7 @@ var mockApis;
|
|
|
31
31
|
})(alert = mockApis2.alert || (mockApis2.alert = {}));
|
|
32
32
|
function featureFlags(options) {
|
|
33
33
|
const instance = new MockFeatureFlagsApi(options);
|
|
34
|
-
return
|
|
34
|
+
return attachMockApiFactory(
|
|
35
35
|
featureFlagsApiRef,
|
|
36
36
|
instance
|
|
37
37
|
);
|
|
@@ -47,7 +47,7 @@ var mockApis;
|
|
|
47
47
|
})(featureFlags = mockApis2.featureFlags || (mockApis2.featureFlags = {}));
|
|
48
48
|
function analytics() {
|
|
49
49
|
const instance = new MockAnalyticsApi();
|
|
50
|
-
return
|
|
50
|
+
return attachMockApiFactory(analyticsApiRef, instance);
|
|
51
51
|
}
|
|
52
52
|
mockApis2.analytics = analytics;
|
|
53
53
|
((analytics2) => {
|
|
@@ -57,7 +57,7 @@ var mockApis;
|
|
|
57
57
|
})(analytics = mockApis2.analytics || (mockApis2.analytics = {}));
|
|
58
58
|
function translation() {
|
|
59
59
|
const instance = MockTranslationApi.create();
|
|
60
|
-
return
|
|
60
|
+
return attachMockApiFactory(
|
|
61
61
|
translationApiRef,
|
|
62
62
|
instance
|
|
63
63
|
);
|
|
@@ -71,7 +71,7 @@ var mockApis;
|
|
|
71
71
|
})(translation = mockApis2.translation || (mockApis2.translation = {}));
|
|
72
72
|
function config(options) {
|
|
73
73
|
const instance = new MockConfigApi({ data: options?.data ?? {} });
|
|
74
|
-
return
|
|
74
|
+
return attachMockApiFactory(configApiRef, instance);
|
|
75
75
|
}
|
|
76
76
|
mockApis2.config = config;
|
|
77
77
|
((config2) => {
|
|
@@ -101,7 +101,7 @@ var mockApis;
|
|
|
101
101
|
return `${baseUrl}/api/${pluginId}`;
|
|
102
102
|
}
|
|
103
103
|
};
|
|
104
|
-
return
|
|
104
|
+
return attachMockApiFactory(discoveryApiRef, instance);
|
|
105
105
|
}
|
|
106
106
|
mockApis2.discovery = discovery;
|
|
107
107
|
((discovery2) => {
|
|
@@ -131,7 +131,7 @@ var mockApis;
|
|
|
131
131
|
async signOut() {
|
|
132
132
|
}
|
|
133
133
|
};
|
|
134
|
-
return
|
|
134
|
+
return attachMockApiFactory(identityApiRef, instance);
|
|
135
135
|
}
|
|
136
136
|
mockApis2.identity = identity;
|
|
137
137
|
((identity2) => {
|
|
@@ -146,7 +146,10 @@ var mockApis;
|
|
|
146
146
|
const authorizeInput = options?.authorize;
|
|
147
147
|
const handler = typeof authorizeInput === "function" ? authorizeInput : () => authorizeInput ?? AuthorizeResult.ALLOW;
|
|
148
148
|
const instance = new MockPermissionApi(handler);
|
|
149
|
-
return
|
|
149
|
+
return attachMockApiFactory(
|
|
150
|
+
permissionApiRef,
|
|
151
|
+
instance
|
|
152
|
+
);
|
|
150
153
|
}
|
|
151
154
|
mockApis2.permission = permission;
|
|
152
155
|
((permission2) => {
|
|
@@ -156,7 +159,7 @@ var mockApis;
|
|
|
156
159
|
})(permission = mockApis2.permission || (mockApis2.permission = {}));
|
|
157
160
|
function storage(options) {
|
|
158
161
|
const instance = MockStorageApi.create(options?.data);
|
|
159
|
-
return
|
|
162
|
+
return attachMockApiFactory(storageApiRef, instance);
|
|
160
163
|
}
|
|
161
164
|
mockApis2.storage = storage;
|
|
162
165
|
((storage2) => {
|
|
@@ -170,7 +173,7 @@ var mockApis;
|
|
|
170
173
|
})(storage = mockApis2.storage || (mockApis2.storage = {}));
|
|
171
174
|
function error(options) {
|
|
172
175
|
const instance = new MockErrorApi(options);
|
|
173
|
-
return
|
|
176
|
+
return attachMockApiFactory(errorApiRef, instance);
|
|
174
177
|
}
|
|
175
178
|
mockApis2.error = error;
|
|
176
179
|
((error2) => {
|
|
@@ -181,7 +184,7 @@ var mockApis;
|
|
|
181
184
|
})(error = mockApis2.error || (mockApis2.error = {}));
|
|
182
185
|
function fetch(options) {
|
|
183
186
|
const instance = new MockFetchApi(options);
|
|
184
|
-
return
|
|
187
|
+
return attachMockApiFactory(fetchApiRef, instance);
|
|
185
188
|
}
|
|
186
189
|
mockApis2.fetch = fetch;
|
|
187
190
|
((fetch2) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mockApis.esm.js","sources":["../../src/apis/mockApis.ts"],"sourcesContent":["/*\n * Copyright 2025 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 {\n alertApiRef,\n analyticsApiRef,\n configApiRef,\n discoveryApiRef,\n errorApiRef,\n fetchApiRef,\n featureFlagsApiRef,\n identityApiRef,\n storageApiRef,\n translationApiRef,\n type AnalyticsApi,\n type ConfigApi,\n type DiscoveryApi,\n type ErrorApi,\n type FetchApi,\n type FeatureFlagState,\n type IdentityApi,\n type StorageApi,\n type TranslationApi,\n} from '@backstage/frontend-plugin-api';\nimport {\n permissionApiRef,\n type PermissionApi,\n} from '@backstage/plugin-permission-react';\nimport { JsonObject } from '@backstage/types';\nimport {\n AuthorizeResult,\n EvaluatePermissionRequest,\n} from '@backstage/plugin-permission-common';\nimport { MockAlertApi } from './AlertApi';\nimport { MockFeatureFlagsApi } from './FeatureFlagsApi';\nimport { MockAnalyticsApi } from './AnalyticsApi';\nimport { MockConfigApi } from './ConfigApi';\nimport { MockErrorApi } from './ErrorApi';\nimport { MockFetchApi, MockFetchApiOptions } from './FetchApi';\nimport { MockStorageApi } from './StorageApi';\nimport { MockPermissionApi } from './PermissionApi';\nimport { MockTranslationApi } from './TranslationApi';\nimport {\n mockWithApiFactory,\n type MockWithApiFactory,\n} from './MockWithApiFactory';\nimport { createApiMock } from './createApiMock';\n\n/**\n * Mock implementations of the core utility APIs, to be used in tests.\n *\n * @public\n * @remarks\n *\n * There are some variations among the APIs depending on what needs tests\n * might have, but overall there are two main usage patterns:\n *\n * 1: Creating an actual fake API instance, often with a simplified version\n * of functionality, by calling the mock API itself as a function.\n *\n * ```ts\n * // The function often accepts parameters that control its behavior\n * const foo = mockApis.foo();\n * ```\n *\n * 2: Creating a mock API, where all methods are replaced with jest mocks, by\n * calling the API's `mock` function.\n *\n * ```ts\n * // You can optionally supply a subset of its methods to implement\n * const foo = mockApis.foo.mock({\n * someMethod: () => 'mocked result',\n * });\n * // After exercising your test, you can make assertions on the mock:\n * expect(foo.someMethod).toHaveBeenCalledTimes(2);\n * expect(foo.otherMethod).toHaveBeenCalledWith(testData);\n * ```\n */\nexport namespace mockApis {\n /**\n * Fake implementation of {@link @backstage/frontend-plugin-api#AlertApi}.\n *\n * @public\n * @example\n *\n * ```tsx\n * const alertApi = mockApis.alert();\n * alertApi.post({ message: 'Test alert' });\n * expect(alertApi.getAlerts()).toHaveLength(1);\n * ```\n */\n export function alert(): MockWithApiFactory<MockAlertApi> {\n const instance = new MockAlertApi();\n return mockWithApiFactory(\n alertApiRef,\n instance,\n ) as MockWithApiFactory<MockAlertApi>;\n }\n /**\n * Mock helpers for {@link @backstage/frontend-plugin-api#AlertApi}.\n *\n * @public\n */\n export namespace alert {\n /**\n * Creates a mock implementation of\n * {@link @backstage/frontend-plugin-api#AlertApi}. All methods are\n * replaced with jest mock functions, and you can optionally pass in a\n * subset of methods with an explicit implementation.\n *\n * @public\n */\n export const mock = createApiMock(alertApiRef, () => ({\n post: jest.fn(),\n alert$: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/frontend-plugin-api#FeatureFlagsApi}.\n *\n * @public\n * @example\n *\n * ```tsx\n * const featureFlagsApi = mockApis.featureFlags({\n * initialStates: { 'my-feature': FeatureFlagState.Active },\n * });\n * expect(featureFlagsApi.isActive('my-feature')).toBe(true);\n * ```\n */\n export function featureFlags(options?: {\n initialStates?: Record<string, FeatureFlagState>;\n }): MockWithApiFactory<MockFeatureFlagsApi> {\n const instance = new MockFeatureFlagsApi(options);\n return mockWithApiFactory(\n featureFlagsApiRef,\n instance,\n ) as MockWithApiFactory<MockFeatureFlagsApi>;\n }\n /**\n * Mock helpers for {@link @backstage/frontend-plugin-api#FeatureFlagsApi}.\n *\n * @public\n */\n export namespace featureFlags {\n /**\n * Creates a mock implementation of\n * {@link @backstage/frontend-plugin-api#FeatureFlagsApi}. All methods are\n * replaced with jest mock functions, and you can optionally pass in a\n * subset of methods with an explicit implementation.\n *\n * @public\n */\n export const mock = createApiMock(featureFlagsApiRef, () => ({\n registerFlag: jest.fn(),\n getRegisteredFlags: jest.fn(),\n isActive: jest.fn(),\n save: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/core-plugin-api#AnalyticsApi}.\n *\n * @public\n */\n export function analytics(): MockAnalyticsApi &\n MockWithApiFactory<AnalyticsApi> {\n const instance = new MockAnalyticsApi();\n return mockWithApiFactory(analyticsApiRef, instance) as MockAnalyticsApi &\n MockWithApiFactory<AnalyticsApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/core-plugin-api#AnalyticsApi}.\n *\n * @public\n */\n export namespace analytics {\n export const mock = createApiMock(analyticsApiRef, () => ({\n captureEvent: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/frontend-plugin-api#TranslationApi}.\n * By default returns the default translation.\n *\n * @public\n */\n export function translation(): MockTranslationApi &\n MockWithApiFactory<TranslationApi> {\n const instance = MockTranslationApi.create();\n return mockWithApiFactory(\n translationApiRef,\n instance,\n ) as MockTranslationApi & MockWithApiFactory<TranslationApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/frontend-plugin-api#TranslationApi}.\n *\n * @public\n */\n export namespace translation {\n /**\n * Creates a mock of {@link @backstage/frontend-plugin-api#TranslationApi}.\n *\n * @public\n */\n export const mock = createApiMock(translationApiRef, () => ({\n getTranslation: jest.fn(),\n translation$: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/core-plugin-api#ConfigApi}.\n *\n * @public\n */\n export function config(options?: {\n data?: JsonObject;\n }): MockConfigApi & MockWithApiFactory<ConfigApi> {\n const instance = new MockConfigApi({ data: options?.data ?? {} });\n return mockWithApiFactory(configApiRef, instance) as MockConfigApi &\n MockWithApiFactory<ConfigApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/core-plugin-api#ConfigApi}.\n *\n * @public\n */\n export namespace config {\n export const mock = createApiMock(configApiRef, () => ({\n has: jest.fn(),\n keys: jest.fn(),\n get: jest.fn(),\n getOptional: jest.fn(),\n getConfig: jest.fn(),\n getOptionalConfig: jest.fn(),\n getConfigArray: jest.fn(),\n getOptionalConfigArray: jest.fn(),\n getNumber: jest.fn(),\n getOptionalNumber: jest.fn(),\n getBoolean: jest.fn(),\n getOptionalBoolean: jest.fn(),\n getString: jest.fn(),\n getOptionalString: jest.fn(),\n getStringArray: jest.fn(),\n getOptionalStringArray: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/core-plugin-api#DiscoveryApi}.\n *\n * @public\n */\n export function discovery(options?: {\n baseUrl?: string;\n }): DiscoveryApi & MockWithApiFactory<DiscoveryApi> {\n const baseUrl = options?.baseUrl ?? 'http://example.com';\n const instance: DiscoveryApi = {\n async getBaseUrl(pluginId: string) {\n return `${baseUrl}/api/${pluginId}`;\n },\n };\n return mockWithApiFactory(discoveryApiRef, instance) as DiscoveryApi &\n MockWithApiFactory<DiscoveryApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/core-plugin-api#DiscoveryApi}.\n *\n * @public\n */\n export namespace discovery {\n export const mock = createApiMock(discoveryApiRef, () => ({\n getBaseUrl: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/core-plugin-api#IdentityApi}.\n *\n * @public\n */\n export function identity(options?: {\n userEntityRef?: string;\n ownershipEntityRefs?: string[];\n token?: string;\n email?: string;\n displayName?: string;\n picture?: string;\n }): MockWithApiFactory<IdentityApi> {\n const {\n userEntityRef = 'user:default/test',\n ownershipEntityRefs = ['user:default/test'],\n token,\n email,\n displayName,\n picture,\n } = options ?? {};\n const instance: IdentityApi = {\n async getBackstageIdentity() {\n return { type: 'user', ownershipEntityRefs, userEntityRef };\n },\n async getCredentials() {\n return { token };\n },\n async getProfileInfo() {\n return { email, displayName, picture };\n },\n async signOut() {},\n };\n return mockWithApiFactory(identityApiRef, instance) as IdentityApi &\n MockWithApiFactory<IdentityApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/core-plugin-api#IdentityApi}.\n *\n * @public\n */\n export namespace identity {\n export const mock = createApiMock(identityApiRef, () => ({\n getBackstageIdentity: jest.fn(),\n getCredentials: jest.fn(),\n getProfileInfo: jest.fn(),\n signOut: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/plugin-permission-react#PermissionApi}.\n *\n * @public\n */\n export function permission(options?: {\n authorize?:\n | AuthorizeResult.ALLOW\n | AuthorizeResult.DENY\n | ((\n request: EvaluatePermissionRequest,\n ) => AuthorizeResult.ALLOW | AuthorizeResult.DENY);\n }): MockPermissionApi & MockWithApiFactory<PermissionApi> {\n const authorizeInput = options?.authorize;\n const handler =\n typeof authorizeInput === 'function'\n ? authorizeInput\n : () => authorizeInput ?? AuthorizeResult.ALLOW;\n const instance = new MockPermissionApi(handler);\n return mockWithApiFactory(permissionApiRef, instance) as MockPermissionApi &\n MockWithApiFactory<PermissionApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/plugin-permission-react#PermissionApi}.\n *\n * @public\n */\n export namespace permission {\n export const mock = createApiMock(permissionApiRef, () => ({\n authorize: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/core-plugin-api#StorageApi}.\n *\n * @public\n */\n export function storage(options?: {\n data?: JsonObject;\n }): MockStorageApi & MockWithApiFactory<StorageApi> {\n const instance = MockStorageApi.create(options?.data);\n return mockWithApiFactory(storageApiRef, instance) as MockStorageApi &\n MockWithApiFactory<StorageApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/core-plugin-api#StorageApi}.\n *\n * @public\n */\n export namespace storage {\n export const mock = createApiMock(storageApiRef, () => ({\n forBucket: jest.fn(),\n snapshot: jest.fn(),\n set: jest.fn(),\n remove: jest.fn(),\n observe$: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/core-plugin-api#ErrorApi}.\n *\n * @public\n */\n export function error(options?: {\n collect?: boolean;\n }): MockErrorApi & MockWithApiFactory<ErrorApi> {\n const instance = new MockErrorApi(options);\n return mockWithApiFactory(errorApiRef, instance) as MockErrorApi &\n MockWithApiFactory<ErrorApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/core-plugin-api#ErrorApi}.\n *\n * @public\n */\n export namespace error {\n export const mock = createApiMock(errorApiRef, () => ({\n post: jest.fn(),\n error$: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/core-plugin-api#FetchApi}.\n *\n * @public\n */\n export function fetch(\n options?: MockFetchApiOptions,\n ): MockFetchApi & MockWithApiFactory<FetchApi> {\n const instance = new MockFetchApi(options);\n return mockWithApiFactory(fetchApiRef, instance) as MockFetchApi &\n MockWithApiFactory<FetchApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/core-plugin-api#FetchApi}.\n *\n * @public\n */\n export namespace fetch {\n export const mock = createApiMock(fetchApiRef, () => ({\n fetch: jest.fn(),\n }));\n }\n}\n"],"names":["mockApis","alert","featureFlags","analytics","translation","config","discovery","identity","permission","storage","error","fetch"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mockApis.esm.js","sources":["../../src/apis/mockApis.ts"],"sourcesContent":["/*\n * Copyright 2025 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 {\n alertApiRef,\n analyticsApiRef,\n configApiRef,\n discoveryApiRef,\n errorApiRef,\n fetchApiRef,\n featureFlagsApiRef,\n identityApiRef,\n storageApiRef,\n translationApiRef,\n type AnalyticsApi,\n type ConfigApi,\n type DiscoveryApi,\n type ErrorApi,\n type FetchApi,\n type FeatureFlagState,\n type IdentityApi,\n type StorageApi,\n type TranslationApi,\n} from '@backstage/frontend-plugin-api';\nimport {\n permissionApiRef,\n type PermissionApi,\n} from '@backstage/plugin-permission-react';\nimport { JsonObject } from '@backstage/types';\nimport {\n AuthorizeResult,\n EvaluatePermissionRequest,\n} from '@backstage/plugin-permission-common';\nimport { MockAlertApi } from './AlertApi';\nimport { MockFeatureFlagsApi } from './FeatureFlagsApi';\nimport { MockAnalyticsApi } from './AnalyticsApi';\nimport { MockConfigApi } from './ConfigApi';\nimport { MockErrorApi } from './ErrorApi';\nimport { MockFetchApi, MockFetchApiOptions } from './FetchApi';\nimport { MockStorageApi } from './StorageApi';\nimport { MockPermissionApi } from './PermissionApi';\nimport { MockTranslationApi } from './TranslationApi';\nimport {\n attachMockApiFactory,\n type MockWithApiFactory,\n} from './MockWithApiFactory';\nimport { createApiMock } from './createApiMock';\n\n/**\n * Mock implementations of the core utility APIs, to be used in tests.\n *\n * @public\n * @remarks\n *\n * There are some variations among the APIs depending on what needs tests\n * might have, but overall there are two main usage patterns:\n *\n * 1: Creating an actual fake API instance, often with a simplified version\n * of functionality, by calling the mock API itself as a function.\n *\n * ```ts\n * // The function often accepts parameters that control its behavior\n * const foo = mockApis.foo();\n * ```\n *\n * 2: Creating a mock API, where all methods are replaced with jest mocks, by\n * calling the API's `mock` function.\n *\n * ```ts\n * // You can optionally supply a subset of its methods to implement\n * const foo = mockApis.foo.mock({\n * someMethod: () => 'mocked result',\n * });\n * // After exercising your test, you can make assertions on the mock:\n * expect(foo.someMethod).toHaveBeenCalledTimes(2);\n * expect(foo.otherMethod).toHaveBeenCalledWith(testData);\n * ```\n */\nexport namespace mockApis {\n /**\n * Fake implementation of {@link @backstage/frontend-plugin-api#AlertApi}.\n *\n * @public\n * @example\n *\n * ```tsx\n * const alertApi = mockApis.alert();\n * alertApi.post({ message: 'Test alert' });\n * expect(alertApi.getAlerts()).toHaveLength(1);\n * ```\n */\n export function alert(): MockWithApiFactory<MockAlertApi> {\n const instance = new MockAlertApi();\n return attachMockApiFactory(\n alertApiRef,\n instance,\n ) as MockWithApiFactory<MockAlertApi>;\n }\n /**\n * Mock helpers for {@link @backstage/frontend-plugin-api#AlertApi}.\n *\n * @public\n */\n export namespace alert {\n /**\n * Creates a mock implementation of\n * {@link @backstage/frontend-plugin-api#AlertApi}. All methods are\n * replaced with jest mock functions, and you can optionally pass in a\n * subset of methods with an explicit implementation.\n *\n * @public\n */\n export const mock = createApiMock(alertApiRef, () => ({\n post: jest.fn(),\n alert$: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/frontend-plugin-api#FeatureFlagsApi}.\n *\n * @public\n * @example\n *\n * ```tsx\n * const featureFlagsApi = mockApis.featureFlags({\n * initialStates: { 'my-feature': FeatureFlagState.Active },\n * });\n * expect(featureFlagsApi.isActive('my-feature')).toBe(true);\n * ```\n */\n export function featureFlags(options?: {\n initialStates?: Record<string, FeatureFlagState>;\n }): MockWithApiFactory<MockFeatureFlagsApi> {\n const instance = new MockFeatureFlagsApi(options);\n return attachMockApiFactory(\n featureFlagsApiRef,\n instance,\n ) as MockWithApiFactory<MockFeatureFlagsApi>;\n }\n /**\n * Mock helpers for {@link @backstage/frontend-plugin-api#FeatureFlagsApi}.\n *\n * @public\n */\n export namespace featureFlags {\n /**\n * Creates a mock implementation of\n * {@link @backstage/frontend-plugin-api#FeatureFlagsApi}. All methods are\n * replaced with jest mock functions, and you can optionally pass in a\n * subset of methods with an explicit implementation.\n *\n * @public\n */\n export const mock = createApiMock(featureFlagsApiRef, () => ({\n registerFlag: jest.fn(),\n getRegisteredFlags: jest.fn(),\n isActive: jest.fn(),\n save: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/core-plugin-api#AnalyticsApi}.\n *\n * @public\n */\n export function analytics(): MockAnalyticsApi &\n MockWithApiFactory<AnalyticsApi> {\n const instance = new MockAnalyticsApi();\n return attachMockApiFactory(analyticsApiRef, instance) as MockAnalyticsApi &\n MockWithApiFactory<AnalyticsApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/core-plugin-api#AnalyticsApi}.\n *\n * @public\n */\n export namespace analytics {\n export const mock = createApiMock(analyticsApiRef, () => ({\n captureEvent: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/frontend-plugin-api#TranslationApi}.\n * By default returns the default translation.\n *\n * @public\n */\n export function translation(): MockTranslationApi &\n MockWithApiFactory<TranslationApi> {\n const instance = MockTranslationApi.create();\n return attachMockApiFactory(\n translationApiRef,\n instance,\n ) as MockTranslationApi & MockWithApiFactory<TranslationApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/frontend-plugin-api#TranslationApi}.\n *\n * @public\n */\n export namespace translation {\n /**\n * Creates a mock of {@link @backstage/frontend-plugin-api#TranslationApi}.\n *\n * @public\n */\n export const mock = createApiMock(translationApiRef, () => ({\n getTranslation: jest.fn(),\n translation$: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/core-plugin-api#ConfigApi}.\n *\n * @public\n */\n export function config(options?: {\n data?: JsonObject;\n }): MockConfigApi & MockWithApiFactory<ConfigApi> {\n const instance = new MockConfigApi({ data: options?.data ?? {} });\n return attachMockApiFactory(configApiRef, instance) as MockConfigApi &\n MockWithApiFactory<ConfigApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/core-plugin-api#ConfigApi}.\n *\n * @public\n */\n export namespace config {\n export const mock = createApiMock(configApiRef, () => ({\n has: jest.fn(),\n keys: jest.fn(),\n get: jest.fn(),\n getOptional: jest.fn(),\n getConfig: jest.fn(),\n getOptionalConfig: jest.fn(),\n getConfigArray: jest.fn(),\n getOptionalConfigArray: jest.fn(),\n getNumber: jest.fn(),\n getOptionalNumber: jest.fn(),\n getBoolean: jest.fn(),\n getOptionalBoolean: jest.fn(),\n getString: jest.fn(),\n getOptionalString: jest.fn(),\n getStringArray: jest.fn(),\n getOptionalStringArray: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/core-plugin-api#DiscoveryApi}.\n *\n * @public\n */\n export function discovery(options?: {\n baseUrl?: string;\n }): DiscoveryApi & MockWithApiFactory<DiscoveryApi> {\n const baseUrl = options?.baseUrl ?? 'http://example.com';\n const instance: DiscoveryApi = {\n async getBaseUrl(pluginId: string) {\n return `${baseUrl}/api/${pluginId}`;\n },\n };\n return attachMockApiFactory(discoveryApiRef, instance) as DiscoveryApi &\n MockWithApiFactory<DiscoveryApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/core-plugin-api#DiscoveryApi}.\n *\n * @public\n */\n export namespace discovery {\n export const mock = createApiMock(discoveryApiRef, () => ({\n getBaseUrl: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/core-plugin-api#IdentityApi}.\n *\n * @public\n */\n export function identity(options?: {\n userEntityRef?: string;\n ownershipEntityRefs?: string[];\n token?: string;\n email?: string;\n displayName?: string;\n picture?: string;\n }): MockWithApiFactory<IdentityApi> {\n const {\n userEntityRef = 'user:default/test',\n ownershipEntityRefs = ['user:default/test'],\n token,\n email,\n displayName,\n picture,\n } = options ?? {};\n const instance: IdentityApi = {\n async getBackstageIdentity() {\n return { type: 'user', ownershipEntityRefs, userEntityRef };\n },\n async getCredentials() {\n return { token };\n },\n async getProfileInfo() {\n return { email, displayName, picture };\n },\n async signOut() {},\n };\n return attachMockApiFactory(identityApiRef, instance) as IdentityApi &\n MockWithApiFactory<IdentityApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/core-plugin-api#IdentityApi}.\n *\n * @public\n */\n export namespace identity {\n export const mock = createApiMock(identityApiRef, () => ({\n getBackstageIdentity: jest.fn(),\n getCredentials: jest.fn(),\n getProfileInfo: jest.fn(),\n signOut: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/plugin-permission-react#PermissionApi}.\n *\n * @public\n */\n export function permission(options?: {\n authorize?:\n | AuthorizeResult.ALLOW\n | AuthorizeResult.DENY\n | ((\n request: EvaluatePermissionRequest,\n ) => AuthorizeResult.ALLOW | AuthorizeResult.DENY);\n }): MockPermissionApi & MockWithApiFactory<PermissionApi> {\n const authorizeInput = options?.authorize;\n const handler =\n typeof authorizeInput === 'function'\n ? authorizeInput\n : () => authorizeInput ?? AuthorizeResult.ALLOW;\n const instance = new MockPermissionApi(handler);\n return attachMockApiFactory(\n permissionApiRef,\n instance,\n ) as MockPermissionApi & MockWithApiFactory<PermissionApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/plugin-permission-react#PermissionApi}.\n *\n * @public\n */\n export namespace permission {\n export const mock = createApiMock(permissionApiRef, () => ({\n authorize: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/core-plugin-api#StorageApi}.\n *\n * @public\n */\n export function storage(options?: {\n data?: JsonObject;\n }): MockStorageApi & MockWithApiFactory<StorageApi> {\n const instance = MockStorageApi.create(options?.data);\n return attachMockApiFactory(storageApiRef, instance) as MockStorageApi &\n MockWithApiFactory<StorageApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/core-plugin-api#StorageApi}.\n *\n * @public\n */\n export namespace storage {\n export const mock = createApiMock(storageApiRef, () => ({\n forBucket: jest.fn(),\n snapshot: jest.fn(),\n set: jest.fn(),\n remove: jest.fn(),\n observe$: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/core-plugin-api#ErrorApi}.\n *\n * @public\n */\n export function error(options?: {\n collect?: boolean;\n }): MockErrorApi & MockWithApiFactory<ErrorApi> {\n const instance = new MockErrorApi(options);\n return attachMockApiFactory(errorApiRef, instance) as MockErrorApi &\n MockWithApiFactory<ErrorApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/core-plugin-api#ErrorApi}.\n *\n * @public\n */\n export namespace error {\n export const mock = createApiMock(errorApiRef, () => ({\n post: jest.fn(),\n error$: jest.fn(),\n }));\n }\n\n /**\n * Fake implementation of {@link @backstage/core-plugin-api#FetchApi}.\n *\n * @public\n */\n export function fetch(\n options?: MockFetchApiOptions,\n ): MockFetchApi & MockWithApiFactory<FetchApi> {\n const instance = new MockFetchApi(options);\n return attachMockApiFactory(fetchApiRef, instance) as MockFetchApi &\n MockWithApiFactory<FetchApi>;\n }\n\n /**\n * Mock helpers for {@link @backstage/core-plugin-api#FetchApi}.\n *\n * @public\n */\n export namespace fetch {\n export const mock = createApiMock(fetchApiRef, () => ({\n fetch: jest.fn(),\n }));\n }\n}\n"],"names":["mockApis","alert","featureFlags","analytics","translation","config","discovery","identity","permission","storage","error","fetch"],"mappings":"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"}
|
|
@@ -4,10 +4,13 @@ import { Link, 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, createFrontendModule, createFrontendPlugin, createApiFactory } from '@backstage/frontend-plugin-api';
|
|
7
|
+
import { coreExtensionData, NavItemBlueprint, useRouteRef, createExtension, 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';
|
|
11
|
+
import { OpaqueExternalRouteRef } from '../frontend-internal/src/routing/OpaqueExternalRouteRef.esm.js';
|
|
12
|
+
import '../frontend-internal/src/routing/OpaqueRouteRef.esm.js';
|
|
13
|
+
import '../frontend-internal/src/routing/OpaqueSubRouteRef.esm.js';
|
|
11
14
|
|
|
12
15
|
const DEFAULT_MOCK_CONFIG = {
|
|
13
16
|
app: { baseUrl: "http://localhost:3000" },
|
|
@@ -41,20 +44,22 @@ const appPluginOverride = appPlugin.withOverrides({
|
|
|
41
44
|
factory(_originalFactory, { inputs }) {
|
|
42
45
|
return [
|
|
43
46
|
coreExtensionData.reactElement(
|
|
44
|
-
/* @__PURE__ */ jsx("nav", { children: /* @__PURE__ */ jsx("ul", { children: inputs.items.map(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
+
) }) })
|
|
58
63
|
)
|
|
59
64
|
];
|
|
60
65
|
}
|
|
@@ -71,8 +76,16 @@ function renderInTestApp(element, options) {
|
|
|
71
76
|
}
|
|
72
77
|
})
|
|
73
78
|
];
|
|
79
|
+
const externalBindings = /* @__PURE__ */ new Map();
|
|
74
80
|
if (options?.mountedRoutes) {
|
|
75
|
-
for (const [path,
|
|
81
|
+
for (const [path, optionRef] of Object.entries(options.mountedRoutes)) {
|
|
82
|
+
let routeRef;
|
|
83
|
+
if (OpaqueExternalRouteRef.isType(optionRef)) {
|
|
84
|
+
routeRef = createRouteRef();
|
|
85
|
+
externalBindings.set(optionRef, routeRef);
|
|
86
|
+
} else {
|
|
87
|
+
routeRef = optionRef;
|
|
88
|
+
}
|
|
76
89
|
extensions.push(
|
|
77
90
|
createExtension({
|
|
78
91
|
kind: "test-route",
|
|
@@ -139,7 +152,12 @@ function renderInTestApp(element, options) {
|
|
|
139
152
|
const [apiRef, implementation] = entry;
|
|
140
153
|
return createApiFactory(apiRef, implementation);
|
|
141
154
|
})
|
|
142
|
-
}
|
|
155
|
+
},
|
|
156
|
+
bindRoutes: externalBindings.size > 0 ? ({ bind }) => {
|
|
157
|
+
for (const [externalRef, targetRef] of externalBindings) {
|
|
158
|
+
bind({ ref: externalRef }, { ref: targetRef });
|
|
159
|
+
}
|
|
160
|
+
} : void 0
|
|
143
161
|
}).finalize();
|
|
144
162
|
return render(
|
|
145
163
|
app.tree.root.instance.getData(coreExtensionData.reactElement)
|
|
@@ -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 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';\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 RouteRef 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 };\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((item, index) => {\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 </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 if (options?.mountedRoutes) {\n for (const [path, routeRef] of Object.entries(options.mountedRoutes)) {\n // TODO(Rugvip): add support for external route refs\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 } 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 { 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,13 +1,16 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { Fragment } from 'react';
|
|
3
3
|
import { prepareSpecializedApp } from '@backstage/frontend-app-api';
|
|
4
|
-
import { createExtension, coreExtensionData, createFrontendModule, createFrontendPlugin, createApiFactory } from '@backstage/frontend-plugin-api';
|
|
4
|
+
import { createRouteRef, createExtension, coreExtensionData, createFrontendModule, createFrontendPlugin, createApiFactory } from '@backstage/frontend-plugin-api';
|
|
5
5
|
import { render } from '@testing-library/react';
|
|
6
6
|
import appPlugin from '@backstage/plugin-app';
|
|
7
7
|
import { ConfigReader } from '@backstage/config';
|
|
8
8
|
import { MemoryRouter } from 'react-router-dom';
|
|
9
9
|
import { RouterBlueprint } from '@backstage/plugin-app-react';
|
|
10
10
|
import { getMockApiFactory } from '../apis/MockWithApiFactory.esm.js';
|
|
11
|
+
import { OpaqueExternalRouteRef } from '../frontend-internal/src/routing/OpaqueExternalRouteRef.esm.js';
|
|
12
|
+
import '../frontend-internal/src/routing/OpaqueRouteRef.esm.js';
|
|
13
|
+
import '../frontend-internal/src/routing/OpaqueSubRouteRef.esm.js';
|
|
11
14
|
|
|
12
15
|
const DEFAULT_MOCK_CONFIG = {
|
|
13
16
|
app: { baseUrl: "http://localhost:3000" },
|
|
@@ -22,8 +25,16 @@ const appPluginOverride = appPlugin.withOverrides({
|
|
|
22
25
|
});
|
|
23
26
|
function renderTestApp(options) {
|
|
24
27
|
const extensions = [...options?.extensions ?? []];
|
|
28
|
+
const externalBindings = /* @__PURE__ */ new Map();
|
|
25
29
|
if (options?.mountedRoutes) {
|
|
26
|
-
for (const [path,
|
|
30
|
+
for (const [path, optionRef] of Object.entries(options.mountedRoutes)) {
|
|
31
|
+
let routeRef;
|
|
32
|
+
if (OpaqueExternalRouteRef.isType(optionRef)) {
|
|
33
|
+
routeRef = createRouteRef();
|
|
34
|
+
externalBindings.set(optionRef, routeRef);
|
|
35
|
+
} else {
|
|
36
|
+
routeRef = optionRef;
|
|
37
|
+
}
|
|
27
38
|
extensions.push(
|
|
28
39
|
createExtension({
|
|
29
40
|
kind: "test-route",
|
|
@@ -90,7 +101,12 @@ function renderTestApp(options) {
|
|
|
90
101
|
const [apiRef, implementation] = entry;
|
|
91
102
|
return createApiFactory(apiRef, implementation);
|
|
92
103
|
})
|
|
93
|
-
}
|
|
104
|
+
},
|
|
105
|
+
bindRoutes: externalBindings.size > 0 ? ({ bind }) => {
|
|
106
|
+
for (const [externalRef, targetRef] of externalBindings) {
|
|
107
|
+
bind({ ref: externalRef }, { ref: targetRef });
|
|
108
|
+
}
|
|
109
|
+
} : void 0
|
|
94
110
|
}).finalize();
|
|
95
111
|
return render(
|
|
96
112
|
app.tree.root.instance.getData(coreExtensionData.reactElement)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderTestApp.esm.js","sources":["../../src/app/renderTestApp.tsx"],"sourcesContent":["/*\n * Copyright 2025 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 { prepareSpecializedApp } from '@backstage/frontend-app-api';\nimport {\n coreExtensionData,\n createApiFactory,\n createExtension,\n createFrontendModule,\n createFrontendPlugin,\n ExtensionDefinition,\n FrontendFeature,\n RouteRef,\n type ApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { render, type RenderResult } from '@testing-library/react';\nimport appPlugin from '@backstage/plugin-app';\nimport { JsonObject } from '@backstage/types';\nimport { ConfigReader } from '@backstage/config';\nimport { MemoryRouter } from 'react-router-dom';\nimport { RouterBlueprint } from '@backstage/plugin-app-react';\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';\n\nconst DEFAULT_MOCK_CONFIG = {\n app: { baseUrl: 'http://localhost:3000' },\n backend: { baseUrl: 'http://localhost:7007' },\n};\n\n/**\n * Options for `renderTestApp`.\n *\n * @public\n */\nexport type RenderTestAppOptions<TApiPairs extends any[] = any[]> = {\n /**\n * Additional configuration passed to the app when rendering elements inside it.\n */\n config?: JsonObject;\n /**\n * Additional extensions to add to the test app.\n */\n extensions?: ExtensionDefinition<any>[];\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 * An object of paths to mount route refs on, with the key being the path and\n * the value being the
|
|
1
|
+
{"version":3,"file":"renderTestApp.esm.js","sources":["../../src/app/renderTestApp.tsx"],"sourcesContent":["/*\n * Copyright 2025 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 { prepareSpecializedApp } from '@backstage/frontend-app-api';\nimport {\n coreExtensionData,\n createApiFactory,\n createExtension,\n createFrontendModule,\n createFrontendPlugin,\n ExtensionDefinition,\n FrontendFeature,\n RouteRef,\n ExternalRouteRef,\n createRouteRef,\n type ApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { render, type RenderResult } from '@testing-library/react';\nimport appPlugin from '@backstage/plugin-app';\nimport { JsonObject } from '@backstage/types';\nimport { ConfigReader } from '@backstage/config';\nimport { MemoryRouter } from 'react-router-dom';\nimport { RouterBlueprint } from '@backstage/plugin-app-react';\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 for `renderTestApp`.\n *\n * @public\n */\nexport type RenderTestAppOptions<TApiPairs extends any[] = any[]> = {\n /**\n * Additional configuration passed to the app when rendering elements inside it.\n */\n config?: JsonObject;\n /**\n * Additional extensions to add to the test app.\n */\n extensions?: ExtensionDefinition<any>[];\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 * An object of paths to mount route refs on, with the key being the path and\n * the value being the route ref that the path will be bound to. This allows\n * the route refs to be used by `useRouteRef` in the rendered elements.\n *\n * @example\n * ```ts\n * renderTestApp({\n * mountedRoutes: {\n * '/my-path': myRouteRef,\n * },\n * extensions: [...],\n * })\n * ```\n */\n mountedRoutes?: { [path: string]: RouteRef | ExternalRouteRef };\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 * renderTestApp({\n * apis: [mockApis.identity({ userEntityRef: 'user:default/guest' })],\n * extensions: [...],\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 ],\n});\n\n/**\n * Renders the provided extensions inside a Backstage app, returning the same\n * utilities as `@testing-library/react` `render` function.\n *\n * @public\n */\nexport function renderTestApp<const TApiPairs extends any[] = any[]>(\n options?: RenderTestAppOptions<TApiPairs>,\n): RenderResult {\n const extensions = [...(options?.extensions ?? [])];\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/routes', input: 'routes' },\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;AA6DA,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;AAAA;AAEL,CAAC,CAAA;AAQM,SAAS,cACd,OAAA,EACc;AACd,EAAA,MAAM,aAAa,CAAC,GAAI,OAAA,EAAS,UAAA,IAAc,EAAG,CAAA;AAElD,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,YAAA,EAAc,OAAO,QAAA,EAAS;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;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createErrorCollector.esm.js","sources":["../../../../../frontend-app-api/src/wiring/createErrorCollector.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { AppNode, FrontendPlugin } from '@backstage/frontend-plugin-api';\n\n/**\n * @public\n */\nexport type AppErrorTypes = {\n // resolveAppNodeSpecs\n EXTENSION_IGNORED: {\n context: { plugin: FrontendPlugin; extensionId: string };\n };\n INVALID_EXTENSION_CONFIG_KEY: {\n context: { extensionId: string };\n };\n // resolveAppTree\n EXTENSION_INPUT_REDIRECT_CONFLICT: {\n context: { node: AppNode; inputName: string };\n };\n // instantiateAppNodeTree\n EXTENSION_INPUT_DATA_IGNORED: {\n context: { node: AppNode; inputName: string };\n };\n EXTENSION_INPUT_DATA_MISSING: {\n context: { node: AppNode; inputName: string };\n };\n EXTENSION_INPUT_INTERNAL_IGNORED: {\n context: {\n node: AppNode;\n inputName: string;\n extensionId: string;\n plugin: FrontendPlugin;\n };\n };\n EXTENSION_ATTACHMENT_CONFLICT: {\n context: { node: AppNode; inputName: string };\n };\n EXTENSION_ATTACHMENT_MISSING: {\n context: { node: AppNode; inputName: string };\n };\n EXTENSION_CONFIGURATION_INVALID: {\n context: { node: AppNode };\n };\n EXTENSION_INVALID: {\n context: { node: AppNode };\n };\n EXTENSION_OUTPUT_CONFLICT: {\n context: { node: AppNode; dataRefId: string };\n };\n EXTENSION_OUTPUT_MISSING: {\n context: { node: AppNode; dataRefId: string };\n };\n EXTENSION_OUTPUT_IGNORED: {\n context: { node: AppNode; dataRefId: string };\n };\n EXTENSION_FACTORY_ERROR: {\n context: { node: AppNode };\n };\n // createSpecializedApp\n API_EXTENSION_INVALID: {\n context: { node: AppNode };\n };\n API_FACTORY_CONFLICT: {\n context: {\n node: AppNode;\n apiRefId: string;\n pluginId: string;\n existingPluginId: string;\n };\n };\n EXTENSION_BOOTSTRAP_PREDICATE_IGNORED: {\n context: { node: AppNode };\n };\n EXTENSION_BOOTSTRAP_API_UNAVAILABLE: {\n context: { node: AppNode; apiRefId: string };\n };\n EXTENSION_BOOTSTRAP_API_OVERRIDE_IGNORED: {\n context: {\n node: AppNode;\n apiRefId: string;\n bootstrapNode: AppNode;\n pluginId: string;\n bootstrapPluginId: string;\n };\n };\n // routing\n ROUTE_DUPLICATE: {\n context: { routeId: string };\n };\n ROUTE_BINDING_INVALID_VALUE: {\n context: { routeId: string };\n };\n ROUTE_NOT_FOUND: {\n context: { routeId: string };\n };\n};\n\n/**\n * @public\n */\nexport type AppError =\n keyof AppErrorTypes extends infer ICode extends keyof AppErrorTypes\n ? ICode extends any\n ? {\n code: ICode;\n message: string;\n context: AppErrorTypes[ICode]['context'];\n }\n : never\n : never;\n\n/** @internal */\nexport interface ErrorCollector<TContext extends {} = {}> {\n report<TCode extends keyof AppErrorTypes>(\n report: Omit<\n AppErrorTypes[TCode]['context'],\n keyof TContext\n > extends infer IContext extends {}\n ? {} extends IContext\n ? {\n code: TCode;\n message: string;\n }\n : {\n code: TCode;\n message: string;\n context: IContext;\n }\n : never,\n ): void;\n child<TAdditionalContext extends {}>(\n context: TAdditionalContext,\n ): ErrorCollector<TContext & TAdditionalContext>;\n collectErrors(): AppError[] | undefined;\n}\n\n/** @internal */\nexport function createErrorCollector(\n context?: Partial<AppError['context']>,\n): ErrorCollector {\n const errors: AppError[] = [];\n const children: ErrorCollector[] = [];\n return {\n report(report: { code: string; message: string; context?: {} }) {\n errors.push({\n ...report,\n context: { ...context, ...report.context },\n } as AppError);\n },\n collectErrors() {\n const allErrors = [\n ...errors,\n ...children.flatMap(child => child.collectErrors() ?? []),\n ];\n errors.length = 0;\n if (allErrors.length === 0) {\n return undefined;\n }\n return allErrors;\n },\n child(childContext) {\n const child = createErrorCollector({ ...context, ...childContext });\n children.push(child);\n return child as ErrorCollector<any>;\n },\n };\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"createErrorCollector.esm.js","sources":["../../../../../frontend-app-api/src/wiring/createErrorCollector.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { AppNode, FrontendPlugin } from '@backstage/frontend-plugin-api';\n\n/**\n * @public\n */\nexport type AppErrorTypes = {\n // resolveAppNodeSpecs\n EXTENSION_IGNORED: {\n context: { plugin: FrontendPlugin; extensionId: string };\n };\n INVALID_EXTENSION_CONFIG_KEY: {\n context: { extensionId: string };\n };\n // resolveAppTree\n EXTENSION_INPUT_REDIRECT_CONFLICT: {\n context: { node: AppNode; inputName: string };\n };\n // instantiateAppNodeTree\n EXTENSION_INPUT_DATA_IGNORED: {\n context: { node: AppNode; inputName: string };\n };\n EXTENSION_INPUT_DATA_MISSING: {\n context: { node: AppNode; inputName: string };\n };\n EXTENSION_INPUT_INTERNAL_IGNORED: {\n context: {\n node: AppNode;\n inputName: string;\n extensionId: string;\n plugin: FrontendPlugin;\n };\n };\n EXTENSION_ATTACHMENT_CONFLICT: {\n context: { node: AppNode; inputName: string };\n };\n EXTENSION_ATTACHMENT_MISSING: {\n context: { node: AppNode; inputName: string };\n };\n EXTENSION_CONFIGURATION_INVALID: {\n context: { node: AppNode };\n };\n EXTENSION_INVALID: {\n context: { node: AppNode };\n };\n EXTENSION_OUTPUT_CONFLICT: {\n context: { node: AppNode; dataRefId: string };\n };\n EXTENSION_OUTPUT_MISSING: {\n context: { node: AppNode; dataRefId: string };\n };\n EXTENSION_OUTPUT_IGNORED: {\n context: { node: AppNode; dataRefId: string };\n };\n EXTENSION_FACTORY_ERROR: {\n context: { node: AppNode };\n };\n // createSpecializedApp\n API_EXTENSION_INVALID: {\n context: { node: AppNode };\n };\n API_FACTORY_CONFLICT: {\n context: {\n node: AppNode;\n apiRefId: string;\n pluginId: string;\n existingPluginId: string;\n };\n };\n EXTENSION_BOOTSTRAP_PREDICATE_IGNORED: {\n context: { node: AppNode };\n };\n EXTENSION_BOOTSTRAP_API_UNAVAILABLE: {\n context: { node: AppNode; apiRefId: string };\n };\n EXTENSION_BOOTSTRAP_API_OVERRIDE_IGNORED: {\n context: {\n node: AppNode;\n apiRefId: string;\n bootstrapNode: AppNode;\n pluginId: string;\n bootstrapPluginId: string;\n };\n };\n FEATURE_FLAG_INVALID: {\n context: { pluginId: string; flagName: string; error: Error };\n };\n // routing\n ROUTE_DUPLICATE: {\n context: { routeId: string };\n };\n ROUTE_BINDING_INVALID_VALUE: {\n context: { routeId: string };\n };\n ROUTE_NOT_FOUND: {\n context: { routeId: string };\n };\n};\n\n/**\n * @public\n */\nexport type AppError =\n keyof AppErrorTypes extends infer ICode extends keyof AppErrorTypes\n ? ICode extends any\n ? {\n code: ICode;\n message: string;\n context: AppErrorTypes[ICode]['context'];\n }\n : never\n : never;\n\n/** @internal */\nexport interface ErrorCollector<TContext extends {} = {}> {\n report<TCode extends keyof AppErrorTypes>(\n report: Omit<\n AppErrorTypes[TCode]['context'],\n keyof TContext\n > extends infer IContext extends {}\n ? {} extends IContext\n ? {\n code: TCode;\n message: string;\n }\n : {\n code: TCode;\n message: string;\n context: IContext;\n }\n : never,\n ): void;\n child<TAdditionalContext extends {}>(\n context: TAdditionalContext,\n ): ErrorCollector<TContext & TAdditionalContext>;\n collectErrors(): AppError[] | undefined;\n}\n\n/** @internal */\nexport function createErrorCollector(\n context?: Partial<AppError['context']>,\n): ErrorCollector {\n const errors: AppError[] = [];\n const children: ErrorCollector[] = [];\n return {\n report(report: { code: string; message: string; context?: {} }) {\n errors.push({\n ...report,\n context: { ...context, ...report.context },\n } as AppError);\n },\n collectErrors() {\n const allErrors = [\n ...errors,\n ...children.flatMap(child => child.collectErrors() ?? []),\n ];\n errors.length = 0;\n if (allErrors.length === 0) {\n return undefined;\n }\n return allErrors;\n },\n child(childContext) {\n const child = createErrorCollector({ ...context, ...childContext });\n children.push(child);\n return child as ErrorCollector<any>;\n },\n };\n}\n"],"names":[],"mappings":"AA0JO,SAAS,qBACd,OAAA,EACgB;AAChB,EAAA,MAAM,SAAqB,EAAC;AAC5B,EAAA,MAAM,WAA6B,EAAC;AACpC,EAAA,OAAO;AAAA,IACL,OAAO,MAAA,EAAyD;AAC9D,MAAA,MAAA,CAAO,IAAA,CAAK;AAAA,QACV,GAAG,MAAA;AAAA,QACH,SAAS,EAAE,GAAG,OAAA,EAAS,GAAG,OAAO,OAAA;AAAQ,OAC9B,CAAA;AAAA,IACf,CAAA;AAAA,IACA,aAAA,GAAgB;AACd,MAAA,MAAM,SAAA,GAAY;AAAA,QAChB,GAAG,MAAA;AAAA,QACH,GAAG,SAAS,OAAA,CAAQ,CAAA,KAAA,KAAS,MAAM,aAAA,EAAc,IAAK,EAAE;AAAA,OAC1D;AACA,MAAA,MAAA,CAAO,MAAA,GAAS,CAAA;AAChB,MAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAC1B,QAAA,OAAO,MAAA;AAAA,MACT;AACA,MAAA,OAAO,SAAA;AAAA,IACT,CAAA;AAAA,IACA,MAAM,YAAA,EAAc;AAClB,MAAA,MAAM,QAAQ,oBAAA,CAAqB,EAAE,GAAG,OAAA,EAAS,GAAG,cAAc,CAAA;AAClE,MAAA,QAAA,CAAS,KAAK,KAAK,CAAA;AACnB,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,GACF;AACF;;;;"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { OpaqueType } from '../../../opaque-internal/src/OpaqueType.esm.js';
|
|
2
|
+
|
|
3
|
+
const OpaqueExternalRouteRef = OpaqueType.create({
|
|
4
|
+
type: "@backstage/ExternalRouteRef",
|
|
5
|
+
versions: ["v1"]
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export { OpaqueExternalRouteRef };
|
|
9
|
+
//# sourceMappingURL=OpaqueExternalRouteRef.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpaqueExternalRouteRef.esm.js","sources":["../../../../../frontend-internal/src/routing/OpaqueExternalRouteRef.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { ExternalRouteRef } from '@backstage/frontend-plugin-api';\nimport { OpaqueType } from '@internal/opaque';\n\nexport const OpaqueExternalRouteRef = OpaqueType.create<{\n public: ExternalRouteRef;\n versions: {\n readonly version: 'v1';\n\n getParams(): string[];\n getDescription(): string;\n getDefaultTarget(): string | undefined;\n\n setId(id: string): void;\n };\n}>({\n type: '@backstage/ExternalRouteRef',\n versions: ['v1'],\n});\n"],"names":[],"mappings":";;AAmBO,MAAM,sBAAA,GAAyB,WAAW,MAAA,CAW9C;AAAA,EACD,IAAA,EAAM,6BAAA;AAAA,EACN,QAAA,EAAU,CAAC,IAAI;AACjB,CAAC;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpaqueRouteRef.esm.js","sources":["../../../../../frontend-internal/src/routing/OpaqueRouteRef.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { RouteRef } from '@backstage/frontend-plugin-api';\nimport { OpaqueType } from '@internal/opaque';\n\nexport const OpaqueRouteRef = OpaqueType.create<{\n public: RouteRef;\n versions: {\n readonly version: 'v1';\n\n getParams(): string[];\n getDescription(): string;\n\n alias: string | undefined;\n\n setId(id: string): void;\n };\n}>({\n type: '@backstage/RouteRef',\n versions: ['v1'],\n});\n"],"names":[],"mappings":";;AAmB8B,WAAW,MAAA,CAYtC;AAAA,EACD,IAAA,EAAM,qBAAA;AAAA,EACN,QAAA,EAAU,CAAC,IAAI;AACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpaqueSubRouteRef.esm.js","sources":["../../../../../frontend-internal/src/routing/OpaqueSubRouteRef.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { RouteRef, SubRouteRef } from '@backstage/frontend-plugin-api';\nimport { OpaqueType } from '@internal/opaque';\n\nexport const OpaqueSubRouteRef = OpaqueType.create<{\n public: SubRouteRef;\n versions: {\n readonly version: 'v1';\n\n getParams(): string[];\n getParent(): RouteRef;\n getDescription(): string;\n };\n}>({\n type: '@backstage/SubRouteRef',\n versions: ['v1'],\n});\n"],"names":[],"mappings":";;AAmBiC,WAAW,MAAA,CASzC;AAAA,EACD,IAAA,EAAM,wBAAA;AAAA,EACN,QAAA,EAAU,CAAC,IAAI;AACjB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _backstage_config from '@backstage/config';
|
|
2
2
|
import { Config } from '@backstage/config';
|
|
3
3
|
import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
|
|
4
|
-
import { ApiFactory, ApiRef, AlertApi, AlertMessage, FeatureFlagsApi, FeatureFlagState, FeatureFlag, FeatureFlagsSaveOptions, AnalyticsApi, AnalyticsEvent, TranslationApi, TranslationRef, TranslationSnapshot, ConfigApi as ConfigApi$1, DiscoveryApi as DiscoveryApi$1, IdentityApi as IdentityApi$1, StorageApi as StorageApi$1, ErrorApi as ErrorApi$1, FetchApi as FetchApi$1, ExtensionDataRef, AppNode, ExtensionDefinitionParameters, ExtensionDefinition, RouteRef, FrontendFeature } from '@backstage/frontend-plugin-api';
|
|
4
|
+
import { ApiFactory, ApiRef, AlertApi, AlertMessage, FeatureFlagsApi, FeatureFlagState, FeatureFlag, FeatureFlagsSaveOptions, AnalyticsApi, AnalyticsEvent, TranslationApi, TranslationRef, TranslationSnapshot, ConfigApi as ConfigApi$1, DiscoveryApi as DiscoveryApi$1, IdentityApi as IdentityApi$1, StorageApi as StorageApi$1, ErrorApi as ErrorApi$1, FetchApi as FetchApi$1, ExtensionDataRef, AppNode, ExtensionDefinitionParameters, ExtensionDefinition, RouteRef, ExternalRouteRef, FrontendFeature } from '@backstage/frontend-plugin-api';
|
|
5
5
|
import { PermissionApi } from '@backstage/plugin-permission-react';
|
|
6
6
|
import { Observable, JsonObject, JsonValue } from '@backstage/types';
|
|
7
7
|
import { EvaluatePermissionRequest, AuthorizeResult, EvaluatePermissionResponse } from '@backstage/plugin-permission-common';
|
|
@@ -794,7 +794,7 @@ declare function createExtensionTester<T extends ExtensionDefinitionParameters,
|
|
|
794
794
|
type TestAppOptions<TApiPairs extends any[] = any[]> = {
|
|
795
795
|
/**
|
|
796
796
|
* An object of paths to mount route ref on, with the key being the path and the value
|
|
797
|
-
* being the
|
|
797
|
+
* being the route ref that the path will be bound to. This allows the route refs to be
|
|
798
798
|
* used by `useRouteRef` in the rendered elements.
|
|
799
799
|
*
|
|
800
800
|
* @example
|
|
@@ -809,7 +809,7 @@ type TestAppOptions<TApiPairs extends any[] = any[]> = {
|
|
|
809
809
|
* ```
|
|
810
810
|
*/
|
|
811
811
|
mountedRoutes?: {
|
|
812
|
-
[path: string]: RouteRef;
|
|
812
|
+
[path: string]: RouteRef | ExternalRouteRef;
|
|
813
813
|
};
|
|
814
814
|
/**
|
|
815
815
|
* Additional configuration passed to the app when rendering elements inside it.
|
|
@@ -868,7 +868,7 @@ type RenderTestAppOptions<TApiPairs extends any[] = any[]> = {
|
|
|
868
868
|
initialRouteEntries?: string[];
|
|
869
869
|
/**
|
|
870
870
|
* An object of paths to mount route refs on, with the key being the path and
|
|
871
|
-
* the value being the
|
|
871
|
+
* the value being the route ref that the path will be bound to. This allows
|
|
872
872
|
* the route refs to be used by `useRouteRef` in the rendered elements.
|
|
873
873
|
*
|
|
874
874
|
* @example
|
|
@@ -882,7 +882,7 @@ type RenderTestAppOptions<TApiPairs extends any[] = any[]> = {
|
|
|
882
882
|
* ```
|
|
883
883
|
*/
|
|
884
884
|
mountedRoutes?: {
|
|
885
|
-
[path: string]: RouteRef;
|
|
885
|
+
[path: string]: RouteRef | ExternalRouteRef;
|
|
886
886
|
};
|
|
887
887
|
/**
|
|
888
888
|
* API overrides to provide to the test app. Use `mockApis` helpers
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/frontend-test-utils",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3-next.1",
|
|
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": "
|
|
35
|
-
"@backstage/core-app-api": "
|
|
36
|
-
"@backstage/core-plugin-api": "
|
|
37
|
-
"@backstage/filter-predicates": "
|
|
38
|
-
"@backstage/frontend-app-api": "
|
|
39
|
-
"@backstage/frontend-plugin-api": "
|
|
40
|
-
"@backstage/plugin-app": "
|
|
41
|
-
"@backstage/plugin-app-react": "
|
|
42
|
-
"@backstage/plugin-permission-common": "
|
|
43
|
-
"@backstage/plugin-permission-react": "
|
|
44
|
-
"@backstage/test-utils": "
|
|
45
|
-
"@backstage/types": "
|
|
46
|
-
"@backstage/version-bridge": "
|
|
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",
|
|
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": "
|
|
52
|
+
"@backstage/cli": "0.36.2-next.1",
|
|
53
53
|
"@testing-library/jest-dom": "^6.0.0",
|
|
54
54
|
"@types/jest": "*",
|
|
55
55
|
"@types/react": "^18.0.0",
|