@backstage/frontend-test-utils 0.6.4-next.2 → 0.6.5-next.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/apis/AlertApi/MockAlertApi.esm.js.map +1 -1
  3. package/dist/apis/AnalyticsApi/MockAnalyticsApi.esm.js.map +1 -1
  4. package/dist/apis/ConfigApi/MockConfigApi.esm.js.map +1 -1
  5. package/dist/apis/ErrorApi/MockErrorApi.esm.js +1 -0
  6. package/dist/apis/ErrorApi/MockErrorApi.esm.js.map +1 -1
  7. package/dist/apis/FeatureFlagsApi/MockFeatureFlagsApi.esm.js.map +1 -1
  8. package/dist/apis/FetchApi/MockFetchApi.esm.js.map +1 -1
  9. package/dist/apis/MockWithApiFactory.esm.js.map +1 -1
  10. package/dist/apis/PermissionApi/MockPermissionApi.esm.js +1 -0
  11. package/dist/apis/PermissionApi/MockPermissionApi.esm.js.map +1 -1
  12. package/dist/apis/StorageApi/MockStorageApi.esm.js.map +1 -1
  13. package/dist/apis/TestApiProvider.esm.js.map +1 -1
  14. package/dist/apis/TranslationApi/MockTranslationApi.esm.js.map +1 -1
  15. package/dist/apis/createApiMock.esm.js.map +1 -1
  16. package/dist/apis/mockApis.esm.js.map +1 -1
  17. package/dist/app/createExtensionTester.esm.js.map +1 -1
  18. package/dist/app/renderInTestApp.esm.js.map +1 -1
  19. package/dist/app/renderTestApp.esm.js.map +1 -1
  20. package/dist/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.esm.js.map +1 -1
  21. package/dist/core-app-api/src/apis/system/ApiAggregator.esm.js.map +1 -1
  22. package/dist/core-app-api/src/apis/system/ApiProvider.esm.js.map +1 -1
  23. package/dist/frontend-app-api/src/routing/getBasePath.esm.js.map +1 -1
  24. package/dist/frontend-app-api/src/tree/instantiateAppNodeTree.esm.js.map +1 -1
  25. package/dist/frontend-app-api/src/tree/readAppExtensionsConfig.esm.js.map +1 -1
  26. package/dist/frontend-app-api/src/tree/resolveAppNodeSpecs.esm.js.map +1 -1
  27. package/dist/frontend-app-api/src/tree/resolveAppTree.esm.js.map +1 -1
  28. package/dist/frontend-app-api/src/wiring/createErrorCollector.esm.js.map +1 -1
  29. package/dist/frontend-internal/src/routing/OpaqueExternalRouteRef.esm.js.map +1 -1
  30. package/dist/frontend-internal/src/wiring/InternalExtensionDefinition.esm.js.map +1 -1
  31. package/dist/frontend-internal/src/wiring/InternalExtensionInput.esm.js.map +1 -1
  32. package/dist/frontend-internal/src/wiring/InternalFrontendPlugin.esm.js.map +1 -1
  33. package/dist/frontend-internal/src/wiring/createExtensionDataContainer.esm.js.map +1 -1
  34. package/dist/frontend-plugin-api/src/translation/TranslationRef.esm.js.map +1 -1
  35. package/dist/frontend-plugin-api/src/wiring/createFrontendModule.esm.js.map +1 -1
  36. package/dist/frontend-plugin-api/src/wiring/resolveExtensionDefinition.esm.js.map +1 -1
  37. package/dist/opaque-internal/src/OpaqueType.esm.js.map +1 -1
  38. package/package.json +13 -13
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # @backstage/frontend-test-utils
2
2
 
3
+ ## 0.6.5-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @backstage/config@1.3.10-next.0
9
+ - @backstage/core-app-api@1.20.6-next.0
10
+ - @backstage/core-plugin-api@1.12.11-next.0
11
+ - @backstage/filter-predicates@0.1.6-next.0
12
+ - @backstage/frontend-app-api@0.16.9-next.0
13
+ - @backstage/frontend-plugin-api@0.18.2-next.0
14
+ - @backstage/test-utils@1.7.23-next.0
15
+ - @backstage/plugin-permission-common@0.9.12-next.0
16
+ - @backstage/plugin-permission-react@0.5.6-next.0
17
+ - @backstage/plugin-app@0.5.4-next.0
18
+ - @backstage/plugin-app-react@0.2.8-next.0
19
+
20
+ ## 0.6.4
21
+
22
+ ### Patch Changes
23
+
24
+ - 279fdf6: Declared the DOM Testing Library dependency required by React Testing Library.
25
+ - Updated dependencies
26
+ - @backstage/core-app-api@1.20.5
27
+ - @backstage/filter-predicates@0.1.5
28
+ - @backstage/config@1.3.9
29
+ - @backstage/frontend-plugin-api@0.18.1
30
+ - @backstage/plugin-app-react@0.2.7
31
+ - @backstage/test-utils@1.7.22
32
+ - @backstage/plugin-app@0.5.3
33
+ - @backstage/frontend-app-api@0.16.8
34
+ - @backstage/core-plugin-api@1.12.10
35
+ - @backstage/plugin-permission-common@0.9.11
36
+ - @backstage/plugin-permission-react@0.5.5
37
+
3
38
  ## 0.6.4-next.2
4
39
 
5
40
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"MockAlertApi.esm.js","sources":["../../../src/apis/AlertApi/MockAlertApi.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 { AlertMessage } from '@backstage/frontend-plugin-api';\nimport { AlertApi } from '@backstage/frontend-plugin-api';\nimport { Observable } from '@backstage/types';\nimport ObservableImpl from 'zen-observable';\n\n/**\n * Mock implementation of {@link @backstage/frontend-plugin-api#AlertApi} for testing alert behavior.\n *\n * @public\n * @deprecated Use `mockApis.alert()` instead.\n * @example\n * ```ts\n * const alertApi = mockApis.alert();\n * alertApi.post({ message: 'Test alert' });\n * expect(alertApi.getAlerts()).toHaveLength(1);\n * ```\n */\nexport class MockAlertApi implements AlertApi {\n private readonly alerts: AlertMessage[] = [];\n private readonly observers = new Set<(alert: AlertMessage) => void>();\n\n post(alert: AlertMessage) {\n this.alerts.push(alert);\n this.observers.forEach(observer => observer(alert));\n }\n\n alert$(): Observable<AlertMessage> {\n return new ObservableImpl(subscriber => {\n const observer = (alert: AlertMessage) => {\n subscriber.next(alert);\n };\n this.observers.add(observer);\n return () => {\n this.observers.delete(observer);\n };\n });\n }\n\n /**\n * Get all alerts that have been posted.\n */\n getAlerts(): AlertMessage[] {\n return this.alerts;\n }\n\n /**\n * Clear all collected alerts.\n */\n clearAlerts(): void {\n this.alerts.length = 0;\n }\n\n /**\n * Wait for an alert matching the given predicate.\n *\n * @param predicate - Function to test each alert\n * @param timeoutMs - Maximum time to wait in milliseconds\n * @returns Promise that resolves with the matching alert\n */\n async waitForAlert(\n predicate: (alert: AlertMessage) => boolean,\n timeoutMs: number = 2000,\n ): Promise<AlertMessage> {\n const existing = this.alerts.find(predicate);\n if (existing) {\n return existing;\n }\n const observers = this.observers;\n\n return new Promise<AlertMessage>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n observers.delete(observer);\n reject(new Error('Timed out waiting for alert'));\n }, timeoutMs);\n\n function observer(alert: AlertMessage) {\n if (predicate(alert)) {\n clearTimeout(timeoutId);\n observers.delete(observer);\n resolve(alert);\n }\n }\n\n observers.add(observer);\n });\n }\n}\n"],"names":[],"mappings":";;AAiCO,MAAM,YAAA,CAAiC;AAAA,EAC3B,SAAyB,EAAC;AAAA,EAC1B,SAAA,uBAAgB,GAAA,EAAmC;AAAA,EAEpE,KAAK,KAAA,EAAqB;AACxB,IAAA,IAAA,CAAK,MAAA,CAAO,KAAK,KAAK,CAAA;AACtB,IAAA,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,CAAA,QAAA,KAAY,QAAA,CAAS,KAAK,CAAC,CAAA;AAAA,EACpD;AAAA,EAEA,MAAA,GAAmC;AACjC,IAAA,OAAO,IAAI,eAAe,CAAA,UAAA,KAAc;AACtC,MAAA,MAAM,QAAA,GAAW,CAAC,KAAA,KAAwB;AACxC,QAAA,UAAA,CAAW,KAAK,KAAK,CAAA;AAAA,MACvB,CAAA;AACA,MAAA,IAAA,CAAK,SAAA,CAAU,IAAI,QAAQ,CAAA;AAC3B,MAAA,OAAO,MAAM;AACX,QAAA,IAAA,CAAK,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,MAChC,CAAA;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,SAAA,GAA4B;AAC1B,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,WAAA,GAAoB;AAClB,IAAA,IAAA,CAAK,OAAO,MAAA,GAAS,CAAA;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,YAAA,CACJ,SAAA,EACA,SAAA,GAAoB,GAAA,EACG;AACvB,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA;AAC3C,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,OAAO,QAAA;AAAA,IACT;AACA,IAAA,MAAM,YAAY,IAAA,CAAK,SAAA;AAEvB,IAAA,OAAO,IAAI,OAAA,CAAsB,CAAC,OAAA,EAAS,MAAA,KAAW;AACpD,MAAA,MAAM,SAAA,GAAY,WAAW,MAAM;AACjC,QAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AACzB,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,6BAA6B,CAAC,CAAA;AAAA,MACjD,GAAG,SAAS,CAAA;AAEZ,MAAA,SAAS,SAAS,KAAA,EAAqB;AACrC,QAAA,IAAI,SAAA,CAAU,KAAK,CAAA,EAAG;AACpB,UAAA,YAAA,CAAa,SAAS,CAAA;AACtB,UAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AACzB,UAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,QACf;AAAA,MACF;AAEA,MAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AAAA,IACxB,CAAC,CAAA;AAAA,EACH;AACF;;;;"}
1
+ {"version":3,"file":"MockAlertApi.esm.js","sources":["../../../src/apis/AlertApi/MockAlertApi.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 { AlertMessage } from '@backstage/frontend-plugin-api';\nimport { AlertApi } from '@backstage/frontend-plugin-api';\nimport { Observable } from '@backstage/types';\nimport ObservableImpl from 'zen-observable';\n\n/**\n * Mock implementation of {@link @backstage/frontend-plugin-api#AlertApi} for testing alert behavior.\n *\n * @public\n * @deprecated Use `mockApis.alert()` instead.\n * @example\n * ```ts\n * const alertApi = mockApis.alert();\n * alertApi.post({ message: 'Test alert' });\n * expect(alertApi.getAlerts()).toHaveLength(1);\n * ```\n */\nexport class MockAlertApi implements AlertApi {\n private readonly alerts: AlertMessage[] = [];\n private readonly observers = new Set<(alert: AlertMessage) => void>();\n\n post(alert: AlertMessage) {\n this.alerts.push(alert);\n this.observers.forEach(observer => observer(alert));\n }\n\n alert$(): Observable<AlertMessage> {\n return new ObservableImpl(subscriber => {\n const observer = (alert: AlertMessage) => {\n subscriber.next(alert);\n };\n this.observers.add(observer);\n return () => {\n this.observers.delete(observer);\n };\n });\n }\n\n /**\n * Get all alerts that have been posted.\n */\n getAlerts(): AlertMessage[] {\n return this.alerts;\n }\n\n /**\n * Clear all collected alerts.\n */\n clearAlerts(): void {\n this.alerts.length = 0;\n }\n\n /**\n * Wait for an alert matching the given predicate.\n *\n * @param predicate - Function to test each alert\n * @param timeoutMs - Maximum time to wait in milliseconds\n * @returns Promise that resolves with the matching alert\n */\n async waitForAlert(\n predicate: (alert: AlertMessage) => boolean,\n timeoutMs: number = 2000,\n ): Promise<AlertMessage> {\n const existing = this.alerts.find(predicate);\n if (existing) {\n return existing;\n }\n const observers = this.observers;\n\n return new Promise<AlertMessage>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n observers.delete(observer);\n reject(new Error('Timed out waiting for alert'));\n }, timeoutMs);\n\n function observer(alert: AlertMessage) {\n if (predicate(alert)) {\n clearTimeout(timeoutId);\n observers.delete(observer);\n resolve(alert);\n }\n }\n\n observers.add(observer);\n });\n }\n}\n"],"names":[],"mappings":";;AAiCO,MAAM,YAAA,CAAiC;AAAA,EAC3B,SAAyB,EAAC;AAAA,EAC1B,SAAA,uBAAgB,GAAA,EAAmC;AAAA,EAEpE,KAAK,KAAA,EAAqB;AACxB,IAAA,IAAA,CAAK,MAAA,CAAO,KAAK,KAAK,CAAA;AACtB,IAAA,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,CAAA,QAAA,KAAY,QAAA,CAAS,KAAK,CAAC,CAAA;AAAA,EACpD;AAAA,EAEA,MAAA,GAAmC;AACjC,IAAA,OAAO,IAAI,eAAe,CAAA,UAAA,KAAc;AACtC,MAAA,MAAM,QAAA,GAAW,CAAC,KAAA,KAAwB;AACxC,QAAA,UAAA,CAAW,KAAK,KAAK,CAAA;AAAA,MACvB,CAAA;AACA,MAAA,IAAA,CAAK,SAAA,CAAU,IAAI,QAAQ,CAAA;AAC3B,MAAA,OAAO,MAAM;AACX,QAAA,IAAA,CAAK,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,MAChC,CAAA;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,SAAA,GAA4B;AAC1B,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,WAAA,GAAoB;AAClB,IAAA,IAAA,CAAK,OAAO,MAAA,GAAS,CAAA;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,YAAA,CACJ,SAAA,EACA,SAAA,GAAoB,GAAA,EACG;AACvB,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA;AAC3C,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,OAAO,QAAA;AAAA,IACT;AACA,IAAA,MAAM,YAAY,IAAA,CAAK,SAAA;AAEvB,IAAA,OAAO,IAAI,OAAA,CAAsB,CAAC,OAAA,EAAS,MAAA,KAAW;AACpD,MAAA,MAAM,SAAA,GAAY,WAAW,MAAM;AACjC,QAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AACzB,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,6BAA6B,CAAC,CAAA;AAAA,MACjD,GAAG,SAAS,CAAA;AAEZ,MAAA,SAAS,SAAS,KAAA,EAAqB;AACrC,QAAA,IAAI,SAAA,CAAU,KAAK,CAAA,EAAG;AACpB,UAAA,YAAA,CAAa,SAAS,CAAA;AACtB,UAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AACzB,UAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,QACf;AAAA,MACF;AAEA,MAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AAAA,IACxB,CAAC,CAAA;AAAA,EACH;AACF;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"MockAnalyticsApi.esm.js","sources":["../../../src/apis/AnalyticsApi/MockAnalyticsApi.ts"],"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 { AnalyticsApi, AnalyticsEvent } from '@backstage/frontend-plugin-api';\n\n/**\n * Mock implementation of {@link frontend-plugin-api#AnalyticsApi} with helpers to ensure that events are sent correctly.\n * Use getEvents in tests to verify captured events.\n *\n * @public\n * @deprecated Use `mockApis.analytics()` instead.\n */\nexport class MockAnalyticsApi implements AnalyticsApi {\n private events: AnalyticsEvent[] = [];\n\n captureEvent(event: AnalyticsEvent) {\n const { action, subject, value, attributes, context } = event;\n\n this.events.push({\n action,\n subject,\n context,\n ...(value !== undefined ? { value } : {}),\n ...(attributes !== undefined ? { attributes } : {}),\n });\n }\n\n getEvents(): AnalyticsEvent[] {\n return this.events;\n }\n}\n"],"names":[],"mappings":"AAyBO,MAAM,gBAAA,CAAyC;AAAA,EAC5C,SAA2B,EAAC;AAAA,EAEpC,aAAa,KAAA,EAAuB;AAClC,IAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,UAAA,EAAY,SAAQ,GAAI,KAAA;AAExD,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK;AAAA,MACf,MAAA;AAAA,MACA,OAAA;AAAA,MACA,OAAA;AAAA,MACA,GAAI,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,KAAU,EAAC;AAAA,MACvC,GAAI,UAAA,KAAe,MAAA,GAAY,EAAE,UAAA,KAAe;AAAC,KAClD,CAAA;AAAA,EACH;AAAA,EAEA,SAAA,GAA8B;AAC5B,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AACF;;;;"}
1
+ {"version":3,"file":"MockAnalyticsApi.esm.js","sources":["../../../src/apis/AnalyticsApi/MockAnalyticsApi.ts"],"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 { AnalyticsApi, AnalyticsEvent } from '@backstage/frontend-plugin-api';\n\n/**\n * Mock implementation of {@link frontend-plugin-api#AnalyticsApi} with helpers to ensure that events are sent correctly.\n * Use getEvents in tests to verify captured events.\n *\n * @public\n * @deprecated Use `mockApis.analytics()` instead.\n */\nexport class MockAnalyticsApi implements AnalyticsApi {\n private events: AnalyticsEvent[] = [];\n\n captureEvent(event: AnalyticsEvent) {\n const { action, subject, value, attributes, context } = event;\n\n this.events.push({\n action,\n subject,\n context,\n ...(value !== undefined ? { value } : {}),\n ...(attributes !== undefined ? { attributes } : {}),\n });\n }\n\n getEvents(): AnalyticsEvent[] {\n return this.events;\n }\n}\n"],"names":[],"mappings":"AAyBO,MAAM,gBAAA,CAAyC;AAAA,EAC5C,SAA2B,EAAC;AAAA,EAEpC,aAAa,KAAA,EAAuB;AAClC,IAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,UAAA,EAAY,SAAQ,GAAI,KAAA;AAExD,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK;AAAA,MACf,MAAA;AAAA,MACA,OAAA;AAAA,MACA,OAAA;AAAA,MACA,GAAI,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,KAAU,EAAC;AAAA,MACvC,GAAI,UAAA,KAAe,MAAA,GAAY,EAAE,UAAA,KAAe;AAAC,KAClD,CAAA;AAAA,EACH;AAAA,EAEA,SAAA,GAA8B;AAC5B,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AACF;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"MockConfigApi.esm.js","sources":["../../../src/apis/ConfigApi/MockConfigApi.ts"],"sourcesContent":["/*\n * Copyright 2022 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 { Config, ConfigReader } from '@backstage/config';\nimport { JsonObject, JsonValue } from '@backstage/types';\nimport { ConfigApi } from '@backstage/core-plugin-api';\n\n/**\n * MockConfigApi is a thin wrapper around {@link @backstage/config#ConfigReader}\n * that can be used to mock configuration using a plain object.\n *\n * @public\n * @deprecated Use `mockApis.config()` instead.\n * @example\n * ```tsx\n * const mockConfig = mockApis.config({\n * data: { app: { baseUrl: 'https://example.com' } },\n * });\n *\n * const rendered = await renderInTestApp(\n * <TestApiProvider apis={[[configApiRef, mockConfig]]}>\n * <MyTestedComponent />\n * </TestApiProvider>,\n * );\n * ```\n */\nexport class MockConfigApi implements ConfigApi {\n private readonly config: ConfigReader;\n\n // NOTE: not extending in order to avoid inheriting the static `.fromConfigs`\n constructor({ data }: { data: JsonObject }) {\n this.config = new ConfigReader(data);\n }\n\n /** {@inheritdoc @backstage/config#Config.has} */\n has(key: string): boolean {\n return this.config.has(key);\n }\n /** {@inheritdoc @backstage/config#Config.keys} */\n keys(): string[] {\n return this.config.keys();\n }\n /** {@inheritdoc @backstage/config#Config.get} */\n get<T = JsonValue>(key?: string): T {\n return this.config.get(key);\n }\n /** {@inheritdoc @backstage/config#Config.getOptional} */\n getOptional<T = JsonValue>(key?: string): T | undefined {\n return this.config.getOptional(key);\n }\n /** {@inheritdoc @backstage/config#Config.getConfig} */\n getConfig(key: string): Config {\n return this.config.getConfig(key);\n }\n /** {@inheritdoc @backstage/config#Config.getOptionalConfig} */\n getOptionalConfig(key: string): Config | undefined {\n return this.config.getOptionalConfig(key);\n }\n /** {@inheritdoc @backstage/config#Config.getConfigArray} */\n getConfigArray(key: string): Config[] {\n return this.config.getConfigArray(key);\n }\n /** {@inheritdoc @backstage/config#Config.getOptionalConfigArray} */\n getOptionalConfigArray(key: string): Config[] | undefined {\n return this.config.getOptionalConfigArray(key);\n }\n /** {@inheritdoc @backstage/config#Config.getNumber} */\n getNumber(key: string): number {\n return this.config.getNumber(key);\n }\n /** {@inheritdoc @backstage/config#Config.getOptionalNumber} */\n getOptionalNumber(key: string): number | undefined {\n return this.config.getOptionalNumber(key);\n }\n /** {@inheritdoc @backstage/config#Config.getBoolean} */\n getBoolean(key: string): boolean {\n return this.config.getBoolean(key);\n }\n /** {@inheritdoc @backstage/config#Config.getOptionalBoolean} */\n getOptionalBoolean(key: string): boolean | undefined {\n return this.config.getOptionalBoolean(key);\n }\n /** {@inheritdoc @backstage/config#Config.getString} */\n getString(key: string): string {\n return this.config.getString(key);\n }\n /** {@inheritdoc @backstage/config#Config.getOptionalString} */\n getOptionalString(key: string): string | undefined {\n return this.config.getOptionalString(key);\n }\n /** {@inheritdoc @backstage/config#Config.getStringArray} */\n getStringArray(key: string): string[] {\n return this.config.getStringArray(key);\n }\n /** {@inheritdoc @backstage/config#Config.getOptionalStringArray} */\n getOptionalStringArray(key: string): string[] | undefined {\n return this.config.getOptionalStringArray(key);\n }\n}\n"],"names":[],"mappings":";;AAuCO,MAAM,aAAA,CAAmC;AAAA,EAC7B,MAAA;AAAA;AAAA,EAGjB,WAAA,CAAY,EAAE,IAAA,EAAK,EAAyB;AAC1C,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,YAAA,CAAa,IAAI,CAAA;AAAA,EACrC;AAAA;AAAA,EAGA,IAAI,GAAA,EAAsB;AACxB,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA;AAAA,EAC5B;AAAA;AAAA,EAEA,IAAA,GAAiB;AACf,IAAA,OAAO,IAAA,CAAK,OAAO,IAAA,EAAK;AAAA,EAC1B;AAAA;AAAA,EAEA,IAAmB,GAAA,EAAiB;AAClC,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA;AAAA,EAC5B;AAAA;AAAA,EAEA,YAA2B,GAAA,EAA6B;AACtD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,WAAA,CAAY,GAAG,CAAA;AAAA,EACpC;AAAA;AAAA,EAEA,UAAU,GAAA,EAAqB;AAC7B,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,SAAA,CAAU,GAAG,CAAA;AAAA,EAClC;AAAA;AAAA,EAEA,kBAAkB,GAAA,EAAiC;AACjD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,iBAAA,CAAkB,GAAG,CAAA;AAAA,EAC1C;AAAA;AAAA,EAEA,eAAe,GAAA,EAAuB;AACpC,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,cAAA,CAAe,GAAG,CAAA;AAAA,EACvC;AAAA;AAAA,EAEA,uBAAuB,GAAA,EAAmC;AACxD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,sBAAA,CAAuB,GAAG,CAAA;AAAA,EAC/C;AAAA;AAAA,EAEA,UAAU,GAAA,EAAqB;AAC7B,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,SAAA,CAAU,GAAG,CAAA;AAAA,EAClC;AAAA;AAAA,EAEA,kBAAkB,GAAA,EAAiC;AACjD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,iBAAA,CAAkB,GAAG,CAAA;AAAA,EAC1C;AAAA;AAAA,EAEA,WAAW,GAAA,EAAsB;AAC/B,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,UAAA,CAAW,GAAG,CAAA;AAAA,EACnC;AAAA;AAAA,EAEA,mBAAmB,GAAA,EAAkC;AACnD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,kBAAA,CAAmB,GAAG,CAAA;AAAA,EAC3C;AAAA;AAAA,EAEA,UAAU,GAAA,EAAqB;AAC7B,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,SAAA,CAAU,GAAG,CAAA;AAAA,EAClC;AAAA;AAAA,EAEA,kBAAkB,GAAA,EAAiC;AACjD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,iBAAA,CAAkB,GAAG,CAAA;AAAA,EAC1C;AAAA;AAAA,EAEA,eAAe,GAAA,EAAuB;AACpC,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,cAAA,CAAe,GAAG,CAAA;AAAA,EACvC;AAAA;AAAA,EAEA,uBAAuB,GAAA,EAAmC;AACxD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,sBAAA,CAAuB,GAAG,CAAA;AAAA,EAC/C;AACF;;;;"}
1
+ {"version":3,"file":"MockConfigApi.esm.js","sources":["../../../src/apis/ConfigApi/MockConfigApi.ts"],"sourcesContent":["/*\n * Copyright 2022 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 { Config, ConfigReader } from '@backstage/config';\nimport { JsonObject, JsonValue } from '@backstage/types';\nimport { ConfigApi } from '@backstage/core-plugin-api';\n\n/**\n * MockConfigApi is a thin wrapper around {@link @backstage/config#ConfigReader}\n * that can be used to mock configuration using a plain object.\n *\n * @public\n * @deprecated Use `mockApis.config()` instead.\n * @example\n * ```tsx\n * const mockConfig = mockApis.config({\n * data: { app: { baseUrl: 'https://example.com' } },\n * });\n *\n * const rendered = await renderInTestApp(\n * <TestApiProvider apis={[[configApiRef, mockConfig]]}>\n * <MyTestedComponent />\n * </TestApiProvider>,\n * );\n * ```\n */\nexport class MockConfigApi implements ConfigApi {\n private readonly config: ConfigReader;\n\n // NOTE: not extending in order to avoid inheriting the static `.fromConfigs`\n constructor({ data }: { data: JsonObject }) {\n this.config = new ConfigReader(data);\n }\n\n /** {@inheritdoc @backstage/config#Config.has} */\n has(key: string): boolean {\n return this.config.has(key);\n }\n /** {@inheritdoc @backstage/config#Config.keys} */\n keys(): string[] {\n return this.config.keys();\n }\n /** {@inheritdoc @backstage/config#Config.get} */\n get<T = JsonValue>(key?: string): T {\n return this.config.get(key);\n }\n /** {@inheritdoc @backstage/config#Config.getOptional} */\n getOptional<T = JsonValue>(key?: string): T | undefined {\n return this.config.getOptional(key);\n }\n /** {@inheritdoc @backstage/config#Config.getConfig} */\n getConfig(key: string): Config {\n return this.config.getConfig(key);\n }\n /** {@inheritdoc @backstage/config#Config.getOptionalConfig} */\n getOptionalConfig(key: string): Config | undefined {\n return this.config.getOptionalConfig(key);\n }\n /** {@inheritdoc @backstage/config#Config.getConfigArray} */\n getConfigArray(key: string): Config[] {\n return this.config.getConfigArray(key);\n }\n /** {@inheritdoc @backstage/config#Config.getOptionalConfigArray} */\n getOptionalConfigArray(key: string): Config[] | undefined {\n return this.config.getOptionalConfigArray(key);\n }\n /** {@inheritdoc @backstage/config#Config.getNumber} */\n getNumber(key: string): number {\n return this.config.getNumber(key);\n }\n /** {@inheritdoc @backstage/config#Config.getOptionalNumber} */\n getOptionalNumber(key: string): number | undefined {\n return this.config.getOptionalNumber(key);\n }\n /** {@inheritdoc @backstage/config#Config.getBoolean} */\n getBoolean(key: string): boolean {\n return this.config.getBoolean(key);\n }\n /** {@inheritdoc @backstage/config#Config.getOptionalBoolean} */\n getOptionalBoolean(key: string): boolean | undefined {\n return this.config.getOptionalBoolean(key);\n }\n /** {@inheritdoc @backstage/config#Config.getString} */\n getString(key: string): string {\n return this.config.getString(key);\n }\n /** {@inheritdoc @backstage/config#Config.getOptionalString} */\n getOptionalString(key: string): string | undefined {\n return this.config.getOptionalString(key);\n }\n /** {@inheritdoc @backstage/config#Config.getStringArray} */\n getStringArray(key: string): string[] {\n return this.config.getStringArray(key);\n }\n /** {@inheritdoc @backstage/config#Config.getOptionalStringArray} */\n getOptionalStringArray(key: string): string[] | undefined {\n return this.config.getOptionalStringArray(key);\n }\n}\n"],"names":[],"mappings":";;AAuCO,MAAM,aAAA,CAAmC;AAAA,EAC7B,MAAA;AAAA;AAAA,EAGjB,WAAA,CAAY,EAAE,IAAA,EAAK,EAAyB;AAC1C,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,YAAA,CAAa,IAAI,CAAA;AAAA,EACrC;AAAA;AAAA,EAGA,IAAI,GAAA,EAAsB;AACxB,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA;AAAA,EAC5B;AAAA;AAAA,EAEA,IAAA,GAAiB;AACf,IAAA,OAAO,IAAA,CAAK,OAAO,IAAA,EAAK;AAAA,EAC1B;AAAA;AAAA,EAEA,IAAmB,GAAA,EAAiB;AAClC,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA;AAAA,EAC5B;AAAA;AAAA,EAEA,YAA2B,GAAA,EAA6B;AACtD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,WAAA,CAAY,GAAG,CAAA;AAAA,EACpC;AAAA;AAAA,EAEA,UAAU,GAAA,EAAqB;AAC7B,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,SAAA,CAAU,GAAG,CAAA;AAAA,EAClC;AAAA;AAAA,EAEA,kBAAkB,GAAA,EAAiC;AACjD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,iBAAA,CAAkB,GAAG,CAAA;AAAA,EAC1C;AAAA;AAAA,EAEA,eAAe,GAAA,EAAuB;AACpC,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,cAAA,CAAe,GAAG,CAAA;AAAA,EACvC;AAAA;AAAA,EAEA,uBAAuB,GAAA,EAAmC;AACxD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,sBAAA,CAAuB,GAAG,CAAA;AAAA,EAC/C;AAAA;AAAA,EAEA,UAAU,GAAA,EAAqB;AAC7B,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,SAAA,CAAU,GAAG,CAAA;AAAA,EAClC;AAAA;AAAA,EAEA,kBAAkB,GAAA,EAAiC;AACjD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,iBAAA,CAAkB,GAAG,CAAA;AAAA,EAC1C;AAAA;AAAA,EAEA,WAAW,GAAA,EAAsB;AAC/B,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,UAAA,CAAW,GAAG,CAAA;AAAA,EACnC;AAAA;AAAA,EAEA,mBAAmB,GAAA,EAAkC;AACnD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,kBAAA,CAAmB,GAAG,CAAA;AAAA,EAC3C;AAAA;AAAA,EAEA,UAAU,GAAA,EAAqB;AAC7B,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,SAAA,CAAU,GAAG,CAAA;AAAA,EAClC;AAAA;AAAA,EAEA,kBAAkB,GAAA,EAAiC;AACjD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,iBAAA,CAAkB,GAAG,CAAA;AAAA,EAC1C;AAAA;AAAA,EAEA,eAAe,GAAA,EAAuB;AACpC,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,cAAA,CAAe,GAAG,CAAA;AAAA,EACvC;AAAA;AAAA,EAEA,uBAAuB,GAAA,EAAmC;AACxD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,sBAAA,CAAuB,GAAG,CAAA;AAAA,EAC/C;AACF;;"}
@@ -9,6 +9,7 @@ class MockErrorApi {
9
9
  constructor(options = {}) {
10
10
  this.options = options;
11
11
  }
12
+ options;
12
13
  errors = new Array();
13
14
  waiters = /* @__PURE__ */ new Set();
14
15
  post(error, context) {
@@ -1 +1 @@
1
- {"version":3,"file":"MockErrorApi.esm.js","sources":["../../../src/apis/ErrorApi/MockErrorApi.ts"],"sourcesContent":["/*\n * Copyright 2020 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 ErrorApi,\n ErrorApiError,\n ErrorApiErrorContext,\n} from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\n\n/**\n * Constructor arguments for {@link MockErrorApi}\n * @public\n * @deprecated Use `mockApis.error()` instead.\n */\nexport type MockErrorApiOptions = {\n collect?: boolean;\n};\n\n/**\n * ErrorWithContext contains error and ErrorApiErrorContext\n * @public\n * @deprecated Use the return type of `MockErrorApi.getErrors` instead.\n */\nexport type ErrorWithContext = {\n error: ErrorApiError;\n context?: ErrorApiErrorContext;\n};\n\ntype Waiter = {\n pattern: RegExp;\n resolve: (err: {\n error: ErrorApiError;\n context?: ErrorApiErrorContext;\n }) => void;\n};\n\nconst nullObservable = {\n subscribe: () => ({ unsubscribe: () => {}, closed: true }),\n\n [Symbol.observable]() {\n return this;\n },\n};\n\n/**\n * Mock implementation of the {@link core-plugin-api#ErrorApi} to be used in tests.\n * Includes withForError and getErrors methods for error testing.\n * @public\n * @deprecated Use `mockApis.error()` instead.\n */\nexport class MockErrorApi implements ErrorApi {\n private readonly errors = new Array<{\n error: ErrorApiError;\n context?: ErrorApiErrorContext;\n }>();\n private readonly waiters = new Set<Waiter>();\n\n constructor(private readonly options: { collect?: boolean } = {}) {}\n\n post(error: ErrorApiError, context?: ErrorApiErrorContext) {\n if (this.options.collect) {\n this.errors.push({ error, context });\n\n for (const waiter of this.waiters) {\n if (waiter.pattern.test(error.message)) {\n this.waiters.delete(waiter);\n waiter.resolve({ error, context });\n }\n }\n\n return;\n }\n\n throw new Error(`MockErrorApi received unexpected error, ${error}`);\n }\n\n error$(): Observable<{\n error: ErrorApiError;\n context?: ErrorApiErrorContext;\n }> {\n return nullObservable;\n }\n\n getErrors(): { error: ErrorApiError; context?: ErrorApiErrorContext }[] {\n return this.errors;\n }\n\n waitForError(\n pattern: RegExp,\n timeoutMs: number = 2000,\n ): Promise<{ error: ErrorApiError; context?: ErrorApiErrorContext }> {\n return new Promise<{\n error: ErrorApiError;\n context?: ErrorApiErrorContext;\n }>((resolve, reject) => {\n setTimeout(() => {\n reject(new Error('Timed out waiting for error'));\n }, timeoutMs);\n\n this.waiters.add({ resolve, pattern });\n });\n }\n}\n"],"names":[],"mappings":"AAkDA,MAAM,cAAA,GAAiB;AAAA,EACrB,SAAA,EAAW,OAAO,EAAE,WAAA,EAAa,MAAM;AAAA,EAAC,CAAA,EAAG,QAAQ,IAAA,EAAK,CAAA;AAAA,EAExD,CAAC,MAAA,CAAO,UAAU,CAAA,GAAI;AACpB,IAAA,OAAO,IAAA;AAAA,EACT;AACF,CAAA;AAQO,MAAM,YAAA,CAAiC;AAAA,EAO5C,WAAA,CAA6B,OAAA,GAAiC,EAAC,EAAG;AAArC,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAAsC;AAAA,EANlD,MAAA,GAAS,IAAI,KAAA,EAG3B;AAAA,EACc,OAAA,uBAAc,GAAA,EAAY;AAAA,EAI3C,IAAA,CAAK,OAAsB,OAAA,EAAgC;AACzD,IAAA,IAAI,IAAA,CAAK,QAAQ,OAAA,EAAS;AACxB,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,EAAE,KAAA,EAAO,SAAS,CAAA;AAEnC,MAAA,KAAA,MAAW,MAAA,IAAU,KAAK,OAAA,EAAS;AACjC,QAAA,IAAI,MAAA,CAAO,OAAA,CAAQ,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA,EAAG;AACtC,UAAA,IAAA,CAAK,OAAA,CAAQ,OAAO,MAAM,CAAA;AAC1B,UAAA,MAAA,CAAO,OAAA,CAAQ,EAAE,KAAA,EAAO,OAAA,EAAS,CAAA;AAAA,QACnC;AAAA,MACF;AAEA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wCAAA,EAA2C,KAAK,CAAA,CAAE,CAAA;AAAA,EACpE;AAAA,EAEA,MAAA,GAGG;AACD,IAAA,OAAO,cAAA;AAAA,EACT;AAAA,EAEA,SAAA,GAAwE;AACtE,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA,EAEA,YAAA,CACE,OAAA,EACA,SAAA,GAAoB,GAAA,EAC+C;AACnE,IAAA,OAAO,IAAI,OAAA,CAGR,CAAC,OAAA,EAAS,MAAA,KAAW;AACtB,MAAA,UAAA,CAAW,MAAM;AACf,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,6BAA6B,CAAC,CAAA;AAAA,MACjD,GAAG,SAAS,CAAA;AAEZ,MAAA,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,EAAE,OAAA,EAAS,SAAS,CAAA;AAAA,IACvC,CAAC,CAAA;AAAA,EACH;AACF;;;;"}
1
+ {"version":3,"file":"MockErrorApi.esm.js","sources":["../../../src/apis/ErrorApi/MockErrorApi.ts"],"sourcesContent":["/*\n * Copyright 2020 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 ErrorApi,\n ErrorApiError,\n ErrorApiErrorContext,\n} from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\n\n/**\n * Constructor arguments for {@link MockErrorApi}\n * @public\n * @deprecated Use `mockApis.error()` instead.\n */\nexport type MockErrorApiOptions = {\n collect?: boolean;\n};\n\n/**\n * ErrorWithContext contains error and ErrorApiErrorContext\n * @public\n * @deprecated Use the return type of `MockErrorApi.getErrors` instead.\n */\nexport type ErrorWithContext = {\n error: ErrorApiError;\n context?: ErrorApiErrorContext;\n};\n\ntype Waiter = {\n pattern: RegExp;\n resolve: (err: {\n error: ErrorApiError;\n context?: ErrorApiErrorContext;\n }) => void;\n};\n\nconst nullObservable = {\n subscribe: () => ({ unsubscribe: () => {}, closed: true }),\n\n [Symbol.observable]() {\n return this;\n },\n};\n\n/**\n * Mock implementation of the {@link core-plugin-api#ErrorApi} to be used in tests.\n * Includes withForError and getErrors methods for error testing.\n * @public\n * @deprecated Use `mockApis.error()` instead.\n */\nexport class MockErrorApi implements ErrorApi {\n private readonly errors = new Array<{\n error: ErrorApiError;\n context?: ErrorApiErrorContext;\n }>();\n private readonly waiters = new Set<Waiter>();\n\n constructor(private readonly options: { collect?: boolean } = {}) {}\n\n post(error: ErrorApiError, context?: ErrorApiErrorContext) {\n if (this.options.collect) {\n this.errors.push({ error, context });\n\n for (const waiter of this.waiters) {\n if (waiter.pattern.test(error.message)) {\n this.waiters.delete(waiter);\n waiter.resolve({ error, context });\n }\n }\n\n return;\n }\n\n throw new Error(`MockErrorApi received unexpected error, ${error}`);\n }\n\n error$(): Observable<{\n error: ErrorApiError;\n context?: ErrorApiErrorContext;\n }> {\n return nullObservable;\n }\n\n getErrors(): { error: ErrorApiError; context?: ErrorApiErrorContext }[] {\n return this.errors;\n }\n\n waitForError(\n pattern: RegExp,\n timeoutMs: number = 2000,\n ): Promise<{ error: ErrorApiError; context?: ErrorApiErrorContext }> {\n return new Promise<{\n error: ErrorApiError;\n context?: ErrorApiErrorContext;\n }>((resolve, reject) => {\n setTimeout(() => {\n reject(new Error('Timed out waiting for error'));\n }, timeoutMs);\n\n this.waiters.add({ resolve, pattern });\n });\n }\n}\n"],"names":[],"mappings":"AAkDA,MAAM,cAAA,GAAiB;AAAA,EACrB,SAAA,EAAW,OAAO,EAAE,WAAA,EAAa,MAAM;AAAA,EAAC,CAAA,EAAG,QAAQ,IAAA,EAAK,CAAA;AAAA,EAExD,CAAC,MAAA,CAAO,UAAU,CAAA,GAAI;AACpB,IAAA,OAAO,IAAA;AAAA,EACT;AACF,CAAA;AAQO,MAAM,YAAA,CAAiC;AAAA,EAO5C,WAAA,CAA6B,OAAA,GAAiC,EAAC,EAAG;AAArC,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAAsC;AAAA,EAAtC,OAAA;AAAA,EANZ,MAAA,GAAS,IAAI,KAAA,EAG3B;AAAA,EACc,OAAA,uBAAc,GAAA,EAAY;AAAA,EAI3C,IAAA,CAAK,OAAsB,OAAA,EAAgC;AACzD,IAAA,IAAI,IAAA,CAAK,QAAQ,OAAA,EAAS;AACxB,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,EAAE,KAAA,EAAO,SAAS,CAAA;AAEnC,MAAA,KAAA,MAAW,MAAA,IAAU,KAAK,OAAA,EAAS;AACjC,QAAA,IAAI,MAAA,CAAO,OAAA,CAAQ,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA,EAAG;AACtC,UAAA,IAAA,CAAK,OAAA,CAAQ,OAAO,MAAM,CAAA;AAC1B,UAAA,MAAA,CAAO,OAAA,CAAQ,EAAE,KAAA,EAAO,OAAA,EAAS,CAAA;AAAA,QACnC;AAAA,MACF;AAEA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wCAAA,EAA2C,KAAK,CAAA,CAAE,CAAA;AAAA,EACpE;AAAA,EAEA,MAAA,GAGG;AACD,IAAA,OAAO,cAAA;AAAA,EACT;AAAA,EAEA,SAAA,GAAwE;AACtE,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA,EAEA,YAAA,CACE,OAAA,EACA,SAAA,GAAoB,GAAA,EAC+C;AACnE,IAAA,OAAO,IAAI,OAAA,CAGR,CAAC,OAAA,EAAS,MAAA,KAAW;AACtB,MAAA,UAAA,CAAW,MAAM;AACf,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,6BAA6B,CAAC,CAAA;AAAA,MACjD,GAAG,SAAS,CAAA;AAEZ,MAAA,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,EAAE,OAAA,EAAS,SAAS,CAAA;AAAA,IACvC,CAAC,CAAA;AAAA,EACH;AACF;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"MockFeatureFlagsApi.esm.js","sources":["../../../src/apis/FeatureFlagsApi/MockFeatureFlagsApi.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 FeatureFlag,\n FeatureFlagsApi,\n FeatureFlagsSaveOptions,\n FeatureFlagState,\n} from '@backstage/frontend-plugin-api';\n\n/**\n * Options for configuring {@link MockFeatureFlagsApi}.\n *\n * @public\n * @deprecated Use `mockApis.featureFlags()` instead.\n */\nexport interface MockFeatureFlagsApiOptions {\n /**\n * Initial feature flag states.\n */\n initialStates?: Record<string, FeatureFlagState>;\n}\n\n/**\n * Mock implementation of {@link @backstage/frontend-plugin-api#FeatureFlagsApi} for testing feature flag behavior.\n *\n * @public\n * @deprecated Use `mockApis.featureFlags()` instead.\n * @example\n * ```ts\n * const api = mockApis.featureFlags({\n * initialStates: { 'my-feature': FeatureFlagState.Active },\n * });\n * expect(api.isActive('my-feature')).toBe(true);\n * ```\n */\nexport class MockFeatureFlagsApi implements FeatureFlagsApi {\n private readonly registeredFlags: FeatureFlag[] = [];\n private readonly states: Map<string, FeatureFlagState>;\n\n constructor(options?: MockFeatureFlagsApiOptions) {\n this.states = new Map(Object.entries(options?.initialStates ?? {}));\n }\n\n registerFlag(flag: FeatureFlag): void {\n if (!this.registeredFlags.some(f => f.name === flag.name)) {\n this.registeredFlags.push(flag);\n }\n }\n\n getRegisteredFlags(): FeatureFlag[] {\n return this.registeredFlags;\n }\n\n isActive(name: string): boolean {\n return this.states.get(name) === FeatureFlagState.Active;\n }\n\n save(options: FeatureFlagsSaveOptions): void {\n if (options.merge) {\n for (const [name, state] of Object.entries(options.states)) {\n this.states.set(name, state);\n }\n } else {\n this.states.clear();\n for (const [name, state] of Object.entries(options.states)) {\n this.states.set(name, state);\n }\n }\n }\n\n /**\n * Get the current state of all feature flags as a record.\n */\n getState(): Record<string, FeatureFlagState> {\n return Object.fromEntries(this.states);\n }\n\n /**\n * Set the state of multiple feature flags.\n */\n setState(states: Record<string, FeatureFlagState>): void {\n for (const [name, state] of Object.entries(states)) {\n this.states.set(name, state);\n }\n }\n\n /**\n * Clear all feature flag states.\n */\n clearState(): void {\n this.states.clear();\n }\n}\n"],"names":[],"mappings":";;AAiDO,MAAM,mBAAA,CAA+C;AAAA,EACzC,kBAAiC,EAAC;AAAA,EAClC,MAAA;AAAA,EAEjB,YAAY,OAAA,EAAsC;AAChD,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,GAAA,CAAI,MAAA,CAAO,QAAQ,OAAA,EAAS,aAAA,IAAiB,EAAE,CAAC,CAAA;AAAA,EACpE;AAAA,EAEA,aAAa,IAAA,EAAyB;AACpC,IAAA,IAAI,CAAC,KAAK,eAAA,CAAgB,IAAA,CAAK,OAAK,CAAA,CAAE,IAAA,KAAS,IAAA,CAAK,IAAI,CAAA,EAAG;AACzD,MAAA,IAAA,CAAK,eAAA,CAAgB,KAAK,IAAI,CAAA;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,kBAAA,GAAoC;AAClC,IAAA,OAAO,IAAA,CAAK,eAAA;AAAA,EACd;AAAA,EAEA,SAAS,IAAA,EAAuB;AAC9B,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAI,MAAM,gBAAA,CAAiB,MAAA;AAAA,EACpD;AAAA,EAEA,KAAK,OAAA,EAAwC;AAC3C,IAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,MAAA,KAAA,MAAW,CAAC,MAAM,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC1D,QAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAA,EAAM,KAAK,CAAA;AAAA,MAC7B;AAAA,IACF,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,OAAO,KAAA,EAAM;AAClB,MAAA,KAAA,MAAW,CAAC,MAAM,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC1D,QAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAA,EAAM,KAAK,CAAA;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAA,GAA6C;AAC3C,IAAA,OAAO,MAAA,CAAO,WAAA,CAAY,IAAA,CAAK,MAAM,CAAA;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,MAAA,EAAgD;AACvD,IAAA,KAAA,MAAW,CAAC,IAAA,EAAM,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AAClD,MAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAA,EAAM,KAAK,CAAA;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAA,GAAmB;AACjB,IAAA,IAAA,CAAK,OAAO,KAAA,EAAM;AAAA,EACpB;AACF;;;;"}
1
+ {"version":3,"file":"MockFeatureFlagsApi.esm.js","sources":["../../../src/apis/FeatureFlagsApi/MockFeatureFlagsApi.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 FeatureFlag,\n FeatureFlagsApi,\n FeatureFlagsSaveOptions,\n FeatureFlagState,\n} from '@backstage/frontend-plugin-api';\n\n/**\n * Options for configuring {@link MockFeatureFlagsApi}.\n *\n * @public\n * @deprecated Use `mockApis.featureFlags()` instead.\n */\nexport interface MockFeatureFlagsApiOptions {\n /**\n * Initial feature flag states.\n */\n initialStates?: Record<string, FeatureFlagState>;\n}\n\n/**\n * Mock implementation of {@link @backstage/frontend-plugin-api#FeatureFlagsApi} for testing feature flag behavior.\n *\n * @public\n * @deprecated Use `mockApis.featureFlags()` instead.\n * @example\n * ```ts\n * const api = mockApis.featureFlags({\n * initialStates: { 'my-feature': FeatureFlagState.Active },\n * });\n * expect(api.isActive('my-feature')).toBe(true);\n * ```\n */\nexport class MockFeatureFlagsApi implements FeatureFlagsApi {\n private readonly registeredFlags: FeatureFlag[] = [];\n private readonly states: Map<string, FeatureFlagState>;\n\n constructor(options?: MockFeatureFlagsApiOptions) {\n this.states = new Map(Object.entries(options?.initialStates ?? {}));\n }\n\n registerFlag(flag: FeatureFlag): void {\n if (!this.registeredFlags.some(f => f.name === flag.name)) {\n this.registeredFlags.push(flag);\n }\n }\n\n getRegisteredFlags(): FeatureFlag[] {\n return this.registeredFlags;\n }\n\n isActive(name: string): boolean {\n return this.states.get(name) === FeatureFlagState.Active;\n }\n\n save(options: FeatureFlagsSaveOptions): void {\n if (options.merge) {\n for (const [name, state] of Object.entries(options.states)) {\n this.states.set(name, state);\n }\n } else {\n this.states.clear();\n for (const [name, state] of Object.entries(options.states)) {\n this.states.set(name, state);\n }\n }\n }\n\n /**\n * Get the current state of all feature flags as a record.\n */\n getState(): Record<string, FeatureFlagState> {\n return Object.fromEntries(this.states);\n }\n\n /**\n * Set the state of multiple feature flags.\n */\n setState(states: Record<string, FeatureFlagState>): void {\n for (const [name, state] of Object.entries(states)) {\n this.states.set(name, state);\n }\n }\n\n /**\n * Clear all feature flag states.\n */\n clearState(): void {\n this.states.clear();\n }\n}\n"],"names":[],"mappings":";;AAiDO,MAAM,mBAAA,CAA+C;AAAA,EACzC,kBAAiC,EAAC;AAAA,EAClC,MAAA;AAAA,EAEjB,YAAY,OAAA,EAAsC;AAChD,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,GAAA,CAAI,MAAA,CAAO,QAAQ,OAAA,EAAS,aAAA,IAAiB,EAAE,CAAC,CAAA;AAAA,EACpE;AAAA,EAEA,aAAa,IAAA,EAAyB;AACpC,IAAA,IAAI,CAAC,KAAK,eAAA,CAAgB,IAAA,CAAK,OAAK,CAAA,CAAE,IAAA,KAAS,IAAA,CAAK,IAAI,CAAA,EAAG;AACzD,MAAA,IAAA,CAAK,eAAA,CAAgB,KAAK,IAAI,CAAA;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,kBAAA,GAAoC;AAClC,IAAA,OAAO,IAAA,CAAK,eAAA;AAAA,EACd;AAAA,EAEA,SAAS,IAAA,EAAuB;AAC9B,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAI,MAAM,gBAAA,CAAiB,MAAA;AAAA,EACpD;AAAA,EAEA,KAAK,OAAA,EAAwC;AAC3C,IAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,MAAA,KAAA,MAAW,CAAC,MAAM,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC1D,QAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAA,EAAM,KAAK,CAAA;AAAA,MAC7B;AAAA,IACF,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,OAAO,KAAA,EAAM;AAClB,MAAA,KAAA,MAAW,CAAC,MAAM,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC1D,QAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAA,EAAM,KAAK,CAAA;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAA,GAA6C;AAC3C,IAAA,OAAO,MAAA,CAAO,WAAA,CAAY,IAAA,CAAK,MAAM,CAAA;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,MAAA,EAAgD;AACvD,IAAA,KAAA,MAAW,CAAC,IAAA,EAAM,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AAClD,MAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAA,EAAM,KAAK,CAAA;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAA,GAAmB;AACjB,IAAA,IAAA,CAAK,OAAO,KAAA,EAAM;AAAA,EACpB;AACF;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"MockFetchApi.esm.js","sources":["../../../src/apis/FetchApi/MockFetchApi.ts"],"sourcesContent":["/*\n * Copyright 2022 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 createFetchApi,\n FetchMiddleware,\n FetchMiddlewares,\n} from '@backstage/core-app-api';\nimport {\n DiscoveryApi,\n FetchApi,\n IdentityApi,\n} from '@backstage/core-plugin-api';\n/**\n * The options given when constructing a {@link MockFetchApi}.\n *\n * @public\n */\nexport interface MockFetchApiOptions {\n /**\n * Define the underlying base `fetch` implementation.\n *\n * @defaultValue undefined\n * @remarks\n *\n * Leaving out this parameter or passing `undefined`, makes the API use the\n * global `fetch` implementation to make real network requests.\n *\n * `'none'` swallows all calls and makes no requests at all.\n *\n * You can also pass in any `fetch` compatible callback, such as a\n * `jest.fn()`, if you want to use a custom implementation or to just track\n * and assert on calls.\n */\n baseImplementation?: undefined | 'none' | typeof fetch;\n\n /**\n * Add translation from `plugin://` URLs to concrete http(s) URLs, basically\n * simulating what\n * {@link @backstage/core-app-api#FetchMiddlewares.resolvePluginProtocol}\n * does.\n *\n * @defaultValue undefined\n * @remarks\n *\n * Leaving out this parameter or passing `undefined`, disables plugin protocol\n * translation.\n *\n * To enable the feature, pass in a discovery API which is then used to\n * resolve the URLs.\n */\n resolvePluginProtocol?:\n | undefined\n | { discoveryApi: Pick<DiscoveryApi, 'getBaseUrl'> };\n\n /**\n * Add token based Authorization headers to requests, basically simulating\n * what {@link @backstage/core-app-api#FetchMiddlewares.injectIdentityAuth}\n * does.\n *\n * @defaultValue undefined\n * @remarks\n *\n * Leaving out this parameter or passing `undefined`, disables auth injection.\n *\n * To enable the feature, pass in either a static token or an identity API\n * which is queried on each request for a token.\n */\n injectIdentityAuth?:\n | undefined\n | { token: string }\n | { identityApi: Pick<IdentityApi, 'getCredentials'> };\n}\n\n/**\n * A test helper implementation of {@link @backstage/core-plugin-api#FetchApi}.\n *\n * @public\n * @deprecated Use `mockApis.fetch()` instead.\n */\nexport class MockFetchApi implements FetchApi {\n private readonly implementation: FetchApi;\n\n /**\n * Creates a mock {@link @backstage/core-plugin-api#FetchApi}.\n */\n constructor(options?: MockFetchApiOptions) {\n this.implementation = build(options);\n }\n\n /** {@inheritdoc @backstage/core-plugin-api#FetchApi.fetch} */\n get fetch(): typeof fetch {\n return this.implementation.fetch;\n }\n}\n\n//\n// Helpers\n//\n\nfunction build(options?: MockFetchApiOptions): FetchApi {\n return createFetchApi({\n baseImplementation: baseImplementation(options),\n middleware: [\n resolvePluginProtocol(options),\n injectIdentityAuth(options),\n ].filter((x): x is FetchMiddleware => Boolean(x)),\n });\n}\n\nfunction baseImplementation(\n options: MockFetchApiOptions | undefined,\n): typeof fetch {\n const implementation = options?.baseImplementation;\n if (!implementation) {\n // Return a wrapper that evaluates global.fetch at call time, not construction time.\n // This allows MSW to patch global.fetch after MockFetchApi is constructed.\n return (input, init) => global.fetch(input, init);\n } else if (implementation === 'none') {\n return () => Promise.resolve(new Response());\n }\n return implementation;\n}\n\nfunction resolvePluginProtocol(\n allOptions: MockFetchApiOptions | undefined,\n): FetchMiddleware | undefined {\n const options = allOptions?.resolvePluginProtocol;\n if (!options) {\n return undefined;\n }\n\n return FetchMiddlewares.resolvePluginProtocol({\n discoveryApi: options.discoveryApi,\n });\n}\n\nfunction injectIdentityAuth(\n allOptions: MockFetchApiOptions | undefined,\n): FetchMiddleware | undefined {\n const options = allOptions?.injectIdentityAuth;\n if (!options) {\n return undefined;\n }\n\n const identityApi: Pick<IdentityApi, 'getCredentials'> =\n 'token' in options\n ? { getCredentials: async () => ({ token: options.token }) }\n : options.identityApi;\n\n return FetchMiddlewares.injectIdentityAuth({\n identityApi: identityApi as IdentityApi,\n allowUrl: () => true,\n });\n}\n"],"names":[],"mappings":";;AA6FO,MAAM,YAAA,CAAiC;AAAA,EAC3B,cAAA;AAAA;AAAA;AAAA;AAAA,EAKjB,YAAY,OAAA,EAA+B;AACzC,IAAA,IAAA,CAAK,cAAA,GAAiB,MAAM,OAAO,CAAA;AAAA,EACrC;AAAA;AAAA,EAGA,IAAI,KAAA,GAAsB;AACxB,IAAA,OAAO,KAAK,cAAA,CAAe,KAAA;AAAA,EAC7B;AACF;AAMA,SAAS,MAAM,OAAA,EAAyC;AACtD,EAAA,OAAO,cAAA,CAAe;AAAA,IACpB,kBAAA,EAAoB,mBAAmB,OAAO,CAAA;AAAA,IAC9C,UAAA,EAAY;AAAA,MACV,sBAAsB,OAAO,CAAA;AAAA,MAC7B,mBAAmB,OAAO;AAAA,MAC1B,MAAA,CAAO,CAAC,CAAA,KAA4B,OAAA,CAAQ,CAAC,CAAC;AAAA,GACjD,CAAA;AACH;AAEA,SAAS,mBACP,OAAA,EACc;AACd,EAAA,MAAM,iBAAiB,OAAA,EAAS,kBAAA;AAChC,EAAA,IAAI,CAAC,cAAA,EAAgB;AAGnB,IAAA,OAAO,CAAC,KAAA,EAAO,IAAA,KAAS,MAAA,CAAO,KAAA,CAAM,OAAO,IAAI,CAAA;AAAA,EAClD,CAAA,MAAA,IAAW,mBAAmB,MAAA,EAAQ;AACpC,IAAA,OAAO,MAAM,OAAA,CAAQ,OAAA,CAAQ,IAAI,UAAU,CAAA;AAAA,EAC7C;AACA,EAAA,OAAO,cAAA;AACT;AAEA,SAAS,sBACP,UAAA,EAC6B;AAC7B,EAAA,MAAM,UAAU,UAAA,EAAY,qBAAA;AAC5B,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,iBAAiB,qBAAA,CAAsB;AAAA,IAC5C,cAAc,OAAA,CAAQ;AAAA,GACvB,CAAA;AACH;AAEA,SAAS,mBACP,UAAA,EAC6B;AAC7B,EAAA,MAAM,UAAU,UAAA,EAAY,kBAAA;AAC5B,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,WAAA,GACJ,OAAA,IAAW,OAAA,GACP,EAAE,cAAA,EAAgB,aAAa,EAAE,KAAA,EAAO,OAAA,CAAQ,KAAA,EAAM,CAAA,EAAG,GACzD,OAAA,CAAQ,WAAA;AAEd,EAAA,OAAO,iBAAiB,kBAAA,CAAmB;AAAA,IACzC,WAAA;AAAA,IACA,UAAU,MAAM;AAAA,GACjB,CAAA;AACH;;;;"}
1
+ {"version":3,"file":"MockFetchApi.esm.js","sources":["../../../src/apis/FetchApi/MockFetchApi.ts"],"sourcesContent":["/*\n * Copyright 2022 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 createFetchApi,\n FetchMiddleware,\n FetchMiddlewares,\n} from '@backstage/core-app-api';\nimport {\n DiscoveryApi,\n FetchApi,\n IdentityApi,\n} from '@backstage/core-plugin-api';\n/**\n * The options given when constructing a {@link MockFetchApi}.\n *\n * @public\n */\nexport interface MockFetchApiOptions {\n /**\n * Define the underlying base `fetch` implementation.\n *\n * @defaultValue undefined\n * @remarks\n *\n * Leaving out this parameter or passing `undefined`, makes the API use the\n * global `fetch` implementation to make real network requests.\n *\n * `'none'` swallows all calls and makes no requests at all.\n *\n * You can also pass in any `fetch` compatible callback, such as a\n * `jest.fn()`, if you want to use a custom implementation or to just track\n * and assert on calls.\n */\n baseImplementation?: undefined | 'none' | typeof fetch;\n\n /**\n * Add translation from `plugin://` URLs to concrete http(s) URLs, basically\n * simulating what\n * {@link @backstage/core-app-api#FetchMiddlewares.resolvePluginProtocol}\n * does.\n *\n * @defaultValue undefined\n * @remarks\n *\n * Leaving out this parameter or passing `undefined`, disables plugin protocol\n * translation.\n *\n * To enable the feature, pass in a discovery API which is then used to\n * resolve the URLs.\n */\n resolvePluginProtocol?:\n | undefined\n | { discoveryApi: Pick<DiscoveryApi, 'getBaseUrl'> };\n\n /**\n * Add token based Authorization headers to requests, basically simulating\n * what {@link @backstage/core-app-api#FetchMiddlewares.injectIdentityAuth}\n * does.\n *\n * @defaultValue undefined\n * @remarks\n *\n * Leaving out this parameter or passing `undefined`, disables auth injection.\n *\n * To enable the feature, pass in either a static token or an identity API\n * which is queried on each request for a token.\n */\n injectIdentityAuth?:\n | undefined\n | { token: string }\n | { identityApi: Pick<IdentityApi, 'getCredentials'> };\n}\n\n/**\n * A test helper implementation of {@link @backstage/core-plugin-api#FetchApi}.\n *\n * @public\n * @deprecated Use `mockApis.fetch()` instead.\n */\nexport class MockFetchApi implements FetchApi {\n private readonly implementation: FetchApi;\n\n /**\n * Creates a mock {@link @backstage/core-plugin-api#FetchApi}.\n */\n constructor(options?: MockFetchApiOptions) {\n this.implementation = build(options);\n }\n\n /** {@inheritdoc @backstage/core-plugin-api#FetchApi.fetch} */\n get fetch(): typeof fetch {\n return this.implementation.fetch;\n }\n}\n\n//\n// Helpers\n//\n\nfunction build(options?: MockFetchApiOptions): FetchApi {\n return createFetchApi({\n baseImplementation: baseImplementation(options),\n middleware: [\n resolvePluginProtocol(options),\n injectIdentityAuth(options),\n ].filter((x): x is FetchMiddleware => Boolean(x)),\n });\n}\n\nfunction baseImplementation(\n options: MockFetchApiOptions | undefined,\n): typeof fetch {\n const implementation = options?.baseImplementation;\n if (!implementation) {\n // Return a wrapper that evaluates global.fetch at call time, not construction time.\n // This allows MSW to patch global.fetch after MockFetchApi is constructed.\n return (input, init) => global.fetch(input, init);\n } else if (implementation === 'none') {\n return () => Promise.resolve(new Response());\n }\n return implementation;\n}\n\nfunction resolvePluginProtocol(\n allOptions: MockFetchApiOptions | undefined,\n): FetchMiddleware | undefined {\n const options = allOptions?.resolvePluginProtocol;\n if (!options) {\n return undefined;\n }\n\n return FetchMiddlewares.resolvePluginProtocol({\n discoveryApi: options.discoveryApi,\n });\n}\n\nfunction injectIdentityAuth(\n allOptions: MockFetchApiOptions | undefined,\n): FetchMiddleware | undefined {\n const options = allOptions?.injectIdentityAuth;\n if (!options) {\n return undefined;\n }\n\n const identityApi: Pick<IdentityApi, 'getCredentials'> =\n 'token' in options\n ? { getCredentials: async () => ({ token: options.token }) }\n : options.identityApi;\n\n return FetchMiddlewares.injectIdentityAuth({\n identityApi: identityApi as IdentityApi,\n allowUrl: () => true,\n });\n}\n"],"names":[],"mappings":";;AA6FO,MAAM,YAAA,CAAiC;AAAA,EAC3B,cAAA;AAAA;AAAA;AAAA;AAAA,EAKjB,YAAY,OAAA,EAA+B;AACzC,IAAA,IAAA,CAAK,cAAA,GAAiB,MAAM,OAAO,CAAA;AAAA,EACrC;AAAA;AAAA,EAGA,IAAI,KAAA,GAAsB;AACxB,IAAA,OAAO,KAAK,cAAA,CAAe,KAAA;AAAA,EAC7B;AACF;AAMA,SAAS,MAAM,OAAA,EAAyC;AACtD,EAAA,OAAO,cAAA,CAAe;AAAA,IACpB,kBAAA,EAAoB,mBAAmB,OAAO,CAAA;AAAA,IAC9C,UAAA,EAAY;AAAA,MACV,sBAAsB,OAAO,CAAA;AAAA,MAC7B,mBAAmB,OAAO;AAAA,MAC1B,MAAA,CAAO,CAAC,CAAA,KAA4B,OAAA,CAAQ,CAAC,CAAC;AAAA,GACjD,CAAA;AACH;AAEA,SAAS,mBACP,OAAA,EACc;AACd,EAAA,MAAM,iBAAiB,OAAA,EAAS,kBAAA;AAChC,EAAA,IAAI,CAAC,cAAA,EAAgB;AAGnB,IAAA,OAAO,CAAC,KAAA,EAAO,IAAA,KAAS,MAAA,CAAO,KAAA,CAAM,OAAO,IAAI,CAAA;AAAA,EAClD,CAAA,MAAA,IAAW,mBAAmB,MAAA,EAAQ;AACpC,IAAA,OAAO,MAAM,OAAA,CAAQ,OAAA,CAAQ,IAAI,UAAU,CAAA;AAAA,EAC7C;AACA,EAAA,OAAO,cAAA;AACT;AAEA,SAAS,sBACP,UAAA,EAC6B;AAC7B,EAAA,MAAM,UAAU,UAAA,EAAY,qBAAA;AAC5B,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,iBAAiB,qBAAA,CAAsB;AAAA,IAC5C,cAAc,OAAA,CAAQ;AAAA,GACvB,CAAA;AACH;AAEA,SAAS,mBACP,UAAA,EAC6B;AAC7B,EAAA,MAAM,UAAU,UAAA,EAAY,kBAAA;AAC5B,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,WAAA,GACJ,OAAA,IAAW,OAAA,GACP,EAAE,cAAA,EAAgB,aAAa,EAAE,KAAA,EAAO,OAAA,CAAQ,KAAA,EAAM,CAAA,EAAG,GACzD,OAAA,CAAQ,WAAA;AAEd,EAAA,OAAO,iBAAiB,kBAAA,CAAmB;AAAA,IACzC,WAAA;AAAA,IACA,UAAU,MAAM;AAAA,GACjB,CAAA;AACH;;"}
@@ -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 * 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;;;;"}
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;;"}
@@ -4,6 +4,7 @@ class MockPermissionApi {
4
4
  constructor(requestHandler = () => AuthorizeResult.ALLOW) {
5
5
  this.requestHandler = requestHandler;
6
6
  }
7
+ requestHandler;
7
8
  async authorize(request) {
8
9
  return { result: this.requestHandler(request) };
9
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MockPermissionApi.esm.js","sources":["../../../src/apis/PermissionApi/MockPermissionApi.ts"],"sourcesContent":["/*\n * Copyright 2022 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 { PermissionApi } from '@backstage/plugin-permission-react';\nimport {\n EvaluatePermissionResponse,\n EvaluatePermissionRequest,\n AuthorizeResult,\n} from '@backstage/plugin-permission-common';\n\n/**\n * Mock implementation of\n * {@link @backstage/plugin-permission-react#PermissionApi}. Supply a\n * requestHandler function to override the mock result returned for a given\n * request.\n *\n * @public\n * @deprecated Use `mockApis.permission()` instead.\n */\nexport class MockPermissionApi implements PermissionApi {\n constructor(\n private readonly requestHandler: (\n request: EvaluatePermissionRequest,\n ) => AuthorizeResult.ALLOW | AuthorizeResult.DENY = () =>\n AuthorizeResult.ALLOW,\n ) {}\n\n async authorize(\n request: EvaluatePermissionRequest,\n ): Promise<EvaluatePermissionResponse> {\n return { result: this.requestHandler(request) };\n }\n}\n"],"names":[],"mappings":";;AAgCO,MAAM,iBAAA,CAA2C;AAAA,EACtD,WAAA,CACmB,cAAA,GAEmC,MAClD,eAAA,CAAgB,KAAA,EAClB;AAJiB,IAAA,IAAA,CAAA,cAAA,GAAA,cAAA;AAAA,EAIhB;AAAA,EAEH,MAAM,UACJ,OAAA,EACqC;AACrC,IAAA,OAAO,EAAE,MAAA,EAAQ,IAAA,CAAK,cAAA,CAAe,OAAO,CAAA,EAAE;AAAA,EAChD;AACF;;;;"}
1
+ {"version":3,"file":"MockPermissionApi.esm.js","sources":["../../../src/apis/PermissionApi/MockPermissionApi.ts"],"sourcesContent":["/*\n * Copyright 2022 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 { PermissionApi } from '@backstage/plugin-permission-react';\nimport {\n EvaluatePermissionResponse,\n EvaluatePermissionRequest,\n AuthorizeResult,\n} from '@backstage/plugin-permission-common';\n\n/**\n * Mock implementation of\n * {@link @backstage/plugin-permission-react#PermissionApi}. Supply a\n * requestHandler function to override the mock result returned for a given\n * request.\n *\n * @public\n * @deprecated Use `mockApis.permission()` instead.\n */\nexport class MockPermissionApi implements PermissionApi {\n constructor(\n private readonly requestHandler: (\n request: EvaluatePermissionRequest,\n ) => AuthorizeResult.ALLOW | AuthorizeResult.DENY = () =>\n AuthorizeResult.ALLOW,\n ) {}\n\n async authorize(\n request: EvaluatePermissionRequest,\n ): Promise<EvaluatePermissionResponse> {\n return { result: this.requestHandler(request) };\n }\n}\n"],"names":[],"mappings":";;AAgCO,MAAM,iBAAA,CAA2C;AAAA,EACtD,WAAA,CACmB,cAAA,GAEmC,MAClD,eAAA,CAAgB,KAAA,EAClB;AAJiB,IAAA,IAAA,CAAA,cAAA,GAAA,cAAA;AAAA,EAIhB;AAAA,EAJgB,cAAA;AAAA,EAMnB,MAAM,UACJ,OAAA,EACqC;AACrC,IAAA,OAAO,EAAE,MAAA,EAAQ,IAAA,CAAK,cAAA,CAAe,OAAO,CAAA,EAAE;AAAA,EAChD;AACF;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"MockStorageApi.esm.js","sources":["../../../src/apis/StorageApi/MockStorageApi.ts"],"sourcesContent":["/*\n * Copyright 2020 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 { StorageApi, StorageValueSnapshot } from '@backstage/core-plugin-api';\nimport { JsonObject, JsonValue, Observable } from '@backstage/types';\nimport ObservableImpl from 'zen-observable';\n\n/**\n * Mock implementation of the {@link core-plugin-api#StorageApi} to be used in tests.\n *\n * @public\n * @deprecated Use `mockApis.storage()` instead.\n */\nexport class MockStorageApi implements StorageApi {\n private readonly namespace: string;\n private readonly data: JsonObject;\n private readonly bucketStorageApis: Map<string, MockStorageApi>;\n\n private constructor(\n namespace: string,\n bucketStorageApis: Map<string, MockStorageApi>,\n data?: JsonObject,\n ) {\n this.namespace = namespace;\n this.bucketStorageApis = bucketStorageApis;\n this.data = { ...data };\n }\n\n static create(data?: JsonObject) {\n // Translate a nested data object structure into a flat object with keys\n // like `/a/b` with their corresponding leaf values\n const keyValues: { [key: string]: any } = {};\n function put(value: { [key: string]: any }, namespace: string) {\n for (const [key, val] of Object.entries(value)) {\n if (typeof val === 'object' && val !== null) {\n put(val, `${namespace}/${key}`);\n } else {\n const namespacedKey = `${namespace}/${key.replace(/^\\//, '')}`;\n keyValues[namespacedKey] = val;\n }\n }\n }\n put(data ?? {}, '');\n return new MockStorageApi('', new Map(), keyValues);\n }\n\n forBucket(name: string): StorageApi {\n if (!this.bucketStorageApis.has(name)) {\n this.bucketStorageApis.set(\n name,\n new MockStorageApi(\n `${this.namespace}/${name}`,\n this.bucketStorageApis,\n this.data,\n ),\n );\n }\n return this.bucketStorageApis.get(name)!;\n }\n\n snapshot<T extends JsonValue>(key: string): StorageValueSnapshot<T> {\n if (this.data.hasOwnProperty(this.getKeyName(key))) {\n const data = this.data[this.getKeyName(key)];\n return {\n key,\n presence: 'present',\n value: data as T,\n };\n }\n return {\n key,\n presence: 'absent',\n value: undefined,\n };\n }\n\n async set<T>(key: string, data: T): Promise<void> {\n const serialized = JSON.parse(JSON.stringify(data), (_key, value) => {\n if (typeof value === 'object' && value !== null) {\n Object.freeze(value);\n }\n return value;\n });\n this.data[this.getKeyName(key)] = serialized;\n this.notifyChanges({\n key,\n presence: 'present',\n value: serialized,\n });\n }\n\n async remove(key: string): Promise<void> {\n delete this.data[this.getKeyName(key)];\n this.notifyChanges({\n key,\n presence: 'absent',\n value: undefined,\n });\n }\n\n observe$<T extends JsonValue>(\n key: string,\n ): Observable<StorageValueSnapshot<T>> {\n return this.observable.filter(({ key: messageKey }) => messageKey === key);\n }\n\n private getKeyName(key: string) {\n return `${this.namespace}/${encodeURIComponent(key)}`;\n }\n\n private notifyChanges<T extends JsonValue>(message: StorageValueSnapshot<T>) {\n for (const subscription of this.subscribers) {\n subscription.next(message);\n }\n }\n\n private subscribers = new Set<\n ZenObservable.SubscriptionObserver<StorageValueSnapshot<JsonValue>>\n >();\n\n private readonly observable = new ObservableImpl<\n StorageValueSnapshot<JsonValue>\n >(subscriber => {\n this.subscribers.add(subscriber);\n return () => {\n this.subscribers.delete(subscriber);\n };\n });\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MockStorageApi.esm.js","sources":["../../../src/apis/StorageApi/MockStorageApi.ts"],"sourcesContent":["/*\n * Copyright 2020 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 { StorageApi, StorageValueSnapshot } from '@backstage/core-plugin-api';\nimport { JsonObject, JsonValue, Observable } from '@backstage/types';\nimport ObservableImpl from 'zen-observable';\n\n/**\n * Mock implementation of the {@link core-plugin-api#StorageApi} to be used in tests.\n *\n * @public\n * @deprecated Use `mockApis.storage()` instead.\n */\nexport class MockStorageApi implements StorageApi {\n private readonly namespace: string;\n private readonly data: JsonObject;\n private readonly bucketStorageApis: Map<string, MockStorageApi>;\n\n private constructor(\n namespace: string,\n bucketStorageApis: Map<string, MockStorageApi>,\n data?: JsonObject,\n ) {\n this.namespace = namespace;\n this.bucketStorageApis = bucketStorageApis;\n this.data = { ...data };\n }\n\n static create(data?: JsonObject) {\n // Translate a nested data object structure into a flat object with keys\n // like `/a/b` with their corresponding leaf values\n const keyValues: { [key: string]: any } = {};\n function put(value: { [key: string]: any }, namespace: string) {\n for (const [key, val] of Object.entries(value)) {\n if (typeof val === 'object' && val !== null) {\n put(val, `${namespace}/${key}`);\n } else {\n const namespacedKey = `${namespace}/${key.replace(/^\\//, '')}`;\n keyValues[namespacedKey] = val;\n }\n }\n }\n put(data ?? {}, '');\n return new MockStorageApi('', new Map(), keyValues);\n }\n\n forBucket(name: string): StorageApi {\n if (!this.bucketStorageApis.has(name)) {\n this.bucketStorageApis.set(\n name,\n new MockStorageApi(\n `${this.namespace}/${name}`,\n this.bucketStorageApis,\n this.data,\n ),\n );\n }\n return this.bucketStorageApis.get(name)!;\n }\n\n snapshot<T extends JsonValue>(key: string): StorageValueSnapshot<T> {\n if (this.data.hasOwnProperty(this.getKeyName(key))) {\n const data = this.data[this.getKeyName(key)];\n return {\n key,\n presence: 'present',\n value: data as T,\n };\n }\n return {\n key,\n presence: 'absent',\n value: undefined,\n };\n }\n\n async set<T>(key: string, data: T): Promise<void> {\n const serialized = JSON.parse(JSON.stringify(data), (_key, value) => {\n if (typeof value === 'object' && value !== null) {\n Object.freeze(value);\n }\n return value;\n });\n this.data[this.getKeyName(key)] = serialized;\n this.notifyChanges({\n key,\n presence: 'present',\n value: serialized,\n });\n }\n\n async remove(key: string): Promise<void> {\n delete this.data[this.getKeyName(key)];\n this.notifyChanges({\n key,\n presence: 'absent',\n value: undefined,\n });\n }\n\n observe$<T extends JsonValue>(\n key: string,\n ): Observable<StorageValueSnapshot<T>> {\n return this.observable.filter(({ key: messageKey }) => messageKey === key);\n }\n\n private getKeyName(key: string) {\n return `${this.namespace}/${encodeURIComponent(key)}`;\n }\n\n private notifyChanges<T extends JsonValue>(message: StorageValueSnapshot<T>) {\n for (const subscription of this.subscribers) {\n subscription.next(message);\n }\n }\n\n private subscribers = new Set<\n ZenObservable.SubscriptionObserver<StorageValueSnapshot<JsonValue>>\n >();\n\n private readonly observable = new ObservableImpl<\n StorageValueSnapshot<JsonValue>\n >(subscriber => {\n this.subscribers.add(subscriber);\n return () => {\n this.subscribers.delete(subscriber);\n };\n });\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TestApiProvider.esm.js","sources":["../../src/apis/TestApiProvider.tsx"],"sourcesContent":["/*\n * Copyright 2020 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 { ReactNode } from 'react';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { ApiProvider } from '../../../core-app-api/src/apis/system';\nimport { ApiHolder, ApiRef } from '@backstage/frontend-plugin-api';\nimport {\n getMockApiFactory,\n type MockWithApiFactory,\n} from './MockWithApiFactory';\n\n/**\n * Represents a single API implementation, either as a tuple of the reference and the implementation, or a mock with an embedded factory.\n * @public\n */\nexport type TestApiPair<TApi> =\n | readonly [ApiRef<TApi>, TApi extends infer TImpl ? Partial<TImpl> : never]\n | MockWithApiFactory<NoInfer<TApi>>;\n\n/**\n * Represents an array of mock API implementation.\n * @public\n */\nexport type TestApiPairs<TApiPairs> = {\n [TIndex in keyof TApiPairs]: TestApiPair<TApiPairs[TIndex]>;\n};\n\n/** @internal */\nexport function resolveTestApiEntries<const TApiPairs extends any[]>(\n apis: readonly [...TestApiPairs<TApiPairs>],\n): ApiHolder {\n const apiMap = new Map<string, unknown>();\n\n for (const entry of apis) {\n const mockFactory = getMockApiFactory(entry);\n if (mockFactory) {\n apiMap.set(mockFactory.api.id, mockFactory.factory({}));\n } else {\n const [apiRef, impl] = entry as readonly [ApiRef<any>, any];\n apiMap.set(apiRef.id, impl);\n }\n }\n\n return {\n get: <T,>(ref: ApiRef<T>) => apiMap.get(ref.id) as T | undefined,\n };\n}\n\n/**\n * Properties for the {@link TestApiProvider} component.\n *\n * @public\n */\nexport type TestApiProviderProps<TApiPairs extends any[]> = {\n apis: readonly [...TestApiPairs<TApiPairs>];\n children: ReactNode;\n};\n\n/**\n * The `TestApiProvider` is a Utility API context provider for standalone rendering\n * scenarios where you're not using `renderInTestApp` or other test utilities.\n *\n * It lets you provide any number of API implementations, without necessarily\n * having to fully implement each of the APIs.\n *\n * @remarks\n *\n * For most test scenarios, prefer using the `apis` option in `renderInTestApp` or\n * `createExtensionTester` instead of wrapping components with `TestApiProvider`.\n *\n * @example\n * ```tsx\n * import { render } from '\\@testing-library/react';\n * import { myCustomApiRef } from '../apis';\n * import { TestApiProvider, mockApis } from '\\@backstage/frontend-test-utils';\n *\n * // Mock custom APIs with tuple syntax\n * const myCustomApiMock = { myMethod: jest.fn() };\n * render(\n * <TestApiProvider\n * apis={[\n * [myCustomApiRef, myCustomApiMock]\n * ]}\n * >\n * <MyComponent />\n * </TestApiProvider>\n * );\n *\n * // Use with built-in mock APIs (no tuples needed)\n * render(\n * <TestApiProvider\n * apis={[\n * mockApis.identity({ userEntityRef: 'user:default/guest' }),\n * mockApis.alert(),\n * ]}\n * >\n * <MyComponent />\n * </TestApiProvider>\n * );\n * ```\n *\n * @public\n */\nexport function TestApiProvider<const TApiPairs extends any[]>(\n props: TestApiProviderProps<TApiPairs>,\n): JSX.Element {\n return (\n <ApiProvider\n apis={resolveTestApiEntries(props.apis)}\n children={props.children}\n />\n );\n}\n"],"names":[],"mappings":";;;;AA0CO,SAAS,sBACd,IAAA,EACW;AACX,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAqB;AAExC,EAAA,KAAA,MAAW,SAAS,IAAA,EAAM;AACxB,IAAA,MAAM,WAAA,GAAc,kBAAkB,KAAK,CAAA;AAC3C,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,MAAA,CAAO,GAAA,CAAI,YAAY,GAAA,CAAI,EAAA,EAAI,YAAY,OAAA,CAAQ,EAAE,CAAC,CAAA;AAAA,IACxD,CAAA,MAAO;AACL,MAAA,MAAM,CAAC,MAAA,EAAQ,IAAI,CAAA,GAAI,KAAA;AACvB,MAAA,MAAA,CAAO,GAAA,CAAI,MAAA,CAAO,EAAA,EAAI,IAAI,CAAA;AAAA,IAC5B;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,KAAK,CAAK,GAAA,KAAmB,MAAA,CAAO,GAAA,CAAI,IAAI,EAAE;AAAA,GAChD;AACF;AAyDO,SAAS,gBACd,KAAA,EACa;AACb,EAAA,uBACE,GAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAM,qBAAA,CAAsB,KAAA,CAAM,IAAI,CAAA;AAAA,MACtC,UAAU,KAAA,CAAM;AAAA;AAAA,GAClB;AAEJ;;;;"}
1
+ {"version":3,"file":"TestApiProvider.esm.js","sources":["../../src/apis/TestApiProvider.tsx"],"sourcesContent":["/*\n * Copyright 2020 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 { ReactNode } from 'react';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { ApiProvider } from '../../../core-app-api/src/apis/system';\nimport { ApiHolder, ApiRef } from '@backstage/frontend-plugin-api';\nimport {\n getMockApiFactory,\n type MockWithApiFactory,\n} from './MockWithApiFactory';\n\n/**\n * Represents a single API implementation, either as a tuple of the reference and the implementation, or a mock with an embedded factory.\n * @public\n */\nexport type TestApiPair<TApi> =\n | readonly [ApiRef<TApi>, TApi extends infer TImpl ? Partial<TImpl> : never]\n | MockWithApiFactory<NoInfer<TApi>>;\n\n/**\n * Represents an array of mock API implementation.\n * @public\n */\nexport type TestApiPairs<TApiPairs> = {\n [TIndex in keyof TApiPairs]: TestApiPair<TApiPairs[TIndex]>;\n};\n\n/** @internal */\nexport function resolveTestApiEntries<const TApiPairs extends any[]>(\n apis: readonly [...TestApiPairs<TApiPairs>],\n): ApiHolder {\n const apiMap = new Map<string, unknown>();\n\n for (const entry of apis) {\n const mockFactory = getMockApiFactory(entry);\n if (mockFactory) {\n apiMap.set(mockFactory.api.id, mockFactory.factory({}));\n } else {\n const [apiRef, impl] = entry as readonly [ApiRef<any>, any];\n apiMap.set(apiRef.id, impl);\n }\n }\n\n return {\n get: <T,>(ref: ApiRef<T>) => apiMap.get(ref.id) as T | undefined,\n };\n}\n\n/**\n * Properties for the {@link TestApiProvider} component.\n *\n * @public\n */\nexport type TestApiProviderProps<TApiPairs extends any[]> = {\n apis: readonly [...TestApiPairs<TApiPairs>];\n children: ReactNode;\n};\n\n/**\n * The `TestApiProvider` is a Utility API context provider for standalone rendering\n * scenarios where you're not using `renderInTestApp` or other test utilities.\n *\n * It lets you provide any number of API implementations, without necessarily\n * having to fully implement each of the APIs.\n *\n * @remarks\n *\n * For most test scenarios, prefer using the `apis` option in `renderInTestApp` or\n * `createExtensionTester` instead of wrapping components with `TestApiProvider`.\n *\n * @example\n * ```tsx\n * import { render } from '\\@testing-library/react';\n * import { myCustomApiRef } from '../apis';\n * import { TestApiProvider, mockApis } from '\\@backstage/frontend-test-utils';\n *\n * // Mock custom APIs with tuple syntax\n * const myCustomApiMock = { myMethod: jest.fn() };\n * render(\n * <TestApiProvider\n * apis={[\n * [myCustomApiRef, myCustomApiMock]\n * ]}\n * >\n * <MyComponent />\n * </TestApiProvider>\n * );\n *\n * // Use with built-in mock APIs (no tuples needed)\n * render(\n * <TestApiProvider\n * apis={[\n * mockApis.identity({ userEntityRef: 'user:default/guest' }),\n * mockApis.alert(),\n * ]}\n * >\n * <MyComponent />\n * </TestApiProvider>\n * );\n * ```\n *\n * @public\n */\nexport function TestApiProvider<const TApiPairs extends any[]>(\n props: TestApiProviderProps<TApiPairs>,\n): JSX.Element {\n return (\n <ApiProvider\n apis={resolveTestApiEntries(props.apis)}\n children={props.children}\n />\n );\n}\n"],"names":[],"mappings":";;;;AA0CO,SAAS,sBACd,IAAA,EACW;AACX,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAqB;AAExC,EAAA,KAAA,MAAW,SAAS,IAAA,EAAM;AACxB,IAAA,MAAM,WAAA,GAAc,kBAAkB,KAAK,CAAA;AAC3C,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,MAAA,CAAO,GAAA,CAAI,YAAY,GAAA,CAAI,EAAA,EAAI,YAAY,OAAA,CAAQ,EAAE,CAAC,CAAA;AAAA,IACxD,CAAA,MAAO;AACL,MAAA,MAAM,CAAC,MAAA,EAAQ,IAAI,CAAA,GAAI,KAAA;AACvB,MAAA,MAAA,CAAO,GAAA,CAAI,MAAA,CAAO,EAAA,EAAI,IAAI,CAAA;AAAA,IAC5B;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,KAAK,CAAK,GAAA,KAAmB,MAAA,CAAO,GAAA,CAAI,IAAI,EAAE;AAAA,GAChD;AACF;AAyDO,SAAS,gBACd,KAAA,EACa;AACb,EAAA,uBACE,GAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAM,qBAAA,CAAsB,KAAA,CAAM,IAAI,CAAA;AAAA,MACtC,UAAU,KAAA,CAAM;AAAA;AAAA,GAClB;AAEJ;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"MockTranslationApi.esm.js","sources":["../../../src/apis/TranslationApi/MockTranslationApi.ts"],"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 {\n TranslationApi,\n TranslationRef,\n TranslationSnapshot,\n} from '@backstage/frontend-plugin-api';\nimport { createInstance as createI18n, type i18n as I18n } from 'i18next';\nimport ObservableImpl from 'zen-observable';\n\nimport { Observable } from '@backstage/types';\n// Internal import to avoid code duplication, this will lead to duplication in build output\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalTranslationRef } from '../../../../frontend-plugin-api/src/translation/TranslationRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { JsxInterpolator } from '../../../../core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi';\n\nconst DEFAULT_LANGUAGE = 'en';\n\n/**\n * Mock implementation of {@link @backstage/frontend-plugin-api#TranslationApi}.\n *\n * @public\n * @deprecated Use `mockApis.translation()` instead.\n */\nexport class MockTranslationApi implements TranslationApi {\n static create() {\n const i18n = createI18n({\n fallbackLng: DEFAULT_LANGUAGE,\n supportedLngs: [DEFAULT_LANGUAGE],\n interpolation: {\n escapeValue: false,\n // Used for the JsxInterpolator format hook\n alwaysFormat: true,\n },\n ns: [],\n defaultNS: false,\n fallbackNS: false,\n\n // Disable resource loading on init, meaning i18n will be ready to use immediately\n initImmediate: false,\n });\n\n i18n.init();\n if (!i18n.isInitialized) {\n throw new Error('i18next was unexpectedly not initialized');\n }\n\n const interpolator = JsxInterpolator.fromI18n(i18n);\n\n return new MockTranslationApi(i18n, interpolator);\n }\n\n readonly #i18n: I18n;\n readonly #interpolator: JsxInterpolator;\n readonly #registeredRefs = new Set<string>();\n\n private constructor(i18n: I18n, interpolator: JsxInterpolator) {\n this.#i18n = i18n;\n this.#interpolator = interpolator;\n }\n\n getTranslation<TMessages extends { [key in string]: string }>(\n translationRef: TranslationRef<string, TMessages>,\n ): TranslationSnapshot<TMessages> {\n const internalRef = toInternalTranslationRef(translationRef);\n\n if (!this.#registeredRefs.has(internalRef.id)) {\n this.#registeredRefs.add(internalRef.id);\n this.#i18n.addResourceBundle(\n DEFAULT_LANGUAGE,\n internalRef.id,\n internalRef.getDefaultMessages(),\n false, // do not merge\n true, // overwrite existing\n );\n }\n\n const t = this.#interpolator.wrapT<TMessages>(\n this.#i18n.getFixedT(null, internalRef.id),\n );\n\n return {\n ready: true,\n t,\n };\n }\n\n translation$<TMessages extends { [key in string]: string }>(): Observable<\n TranslationSnapshot<TMessages>\n > {\n // No need to implement, getTranslation will always return a ready snapshot\n return new ObservableImpl<TranslationSnapshot<TMessages>>(_subscriber => {\n return () => {};\n });\n }\n}\n"],"names":["createI18n"],"mappings":";;;;;AA+BA,MAAM,gBAAA,GAAmB,IAAA;AAQlB,MAAM,kBAAA,CAA6C;AAAA,EACxD,OAAO,MAAA,GAAS;AACd,IAAA,MAAM,OAAOA,cAAA,CAAW;AAAA,MACtB,WAAA,EAAa,gBAAA;AAAA,MACb,aAAA,EAAe,CAAC,gBAAgB,CAAA;AAAA,MAChC,aAAA,EAAe;AAAA,QACb,WAAA,EAAa,KAAA;AAAA;AAAA,QAEb,YAAA,EAAc;AAAA,OAChB;AAAA,MACA,IAAI,EAAC;AAAA,MACL,SAAA,EAAW,KAAA;AAAA,MACX,UAAA,EAAY,KAAA;AAAA;AAAA,MAGZ,aAAA,EAAe;AAAA,KAChB,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,EAAK;AACV,IAAA,IAAI,CAAC,KAAK,aAAA,EAAe;AACvB,MAAA,MAAM,IAAI,MAAM,0CAA0C,CAAA;AAAA,IAC5D;AAEA,IAAA,MAAM,YAAA,GAAe,eAAA,CAAgB,QAAA,CAAS,IAAI,CAAA;AAElD,IAAA,OAAO,IAAI,kBAAA,CAAmB,IAAA,EAAM,YAAY,CAAA;AAAA,EAClD;AAAA,EAES,KAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA,uBAAsB,GAAA,EAAY;AAAA,EAEnC,WAAA,CAAY,MAAY,YAAA,EAA+B;AAC7D,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAA;AACb,IAAA,IAAA,CAAK,aAAA,GAAgB,YAAA;AAAA,EACvB;AAAA,EAEA,eACE,cAAA,EACgC;AAChC,IAAA,MAAM,WAAA,GAAc,yBAAyB,cAAc,CAAA;AAE3D,IAAA,IAAI,CAAC,IAAA,CAAK,eAAA,CAAgB,GAAA,CAAI,WAAA,CAAY,EAAE,CAAA,EAAG;AAC7C,MAAA,IAAA,CAAK,eAAA,CAAgB,GAAA,CAAI,WAAA,CAAY,EAAE,CAAA;AACvC,MAAA,IAAA,CAAK,KAAA,CAAM,iBAAA;AAAA,QACT,gBAAA;AAAA,QACA,WAAA,CAAY,EAAA;AAAA,QACZ,YAAY,kBAAA,EAAmB;AAAA,QAC/B,KAAA;AAAA;AAAA,QACA;AAAA;AAAA,OACF;AAAA,IACF;AAEA,IAAA,MAAM,CAAA,GAAI,KAAK,aAAA,CAAc,KAAA;AAAA,MAC3B,IAAA,CAAK,KAAA,CAAM,SAAA,CAAU,IAAA,EAAM,YAAY,EAAE;AAAA,KAC3C;AAEA,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,IAAA;AAAA,MACP;AAAA,KACF;AAAA,EACF;AAAA,EAEA,YAAA,GAEE;AAEA,IAAA,OAAO,IAAI,eAA+C,CAAA,WAAA,KAAe;AACvE,MAAA,OAAO,MAAM;AAAA,MAAC,CAAA;AAAA,IAChB,CAAC,CAAA;AAAA,EACH;AACF;;;;"}
1
+ {"version":3,"file":"MockTranslationApi.esm.js","sources":["../../../src/apis/TranslationApi/MockTranslationApi.ts"],"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 {\n TranslationApi,\n TranslationRef,\n TranslationSnapshot,\n} from '@backstage/frontend-plugin-api';\nimport { createInstance as createI18n, type i18n as I18n } from 'i18next';\nimport ObservableImpl from 'zen-observable';\n\nimport { Observable } from '@backstage/types';\n// Internal import to avoid code duplication, this will lead to duplication in build output\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalTranslationRef } from '../../../../frontend-plugin-api/src/translation/TranslationRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { JsxInterpolator } from '../../../../core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi';\n\nconst DEFAULT_LANGUAGE = 'en';\n\n/**\n * Mock implementation of {@link @backstage/frontend-plugin-api#TranslationApi}.\n *\n * @public\n * @deprecated Use `mockApis.translation()` instead.\n */\nexport class MockTranslationApi implements TranslationApi {\n static create() {\n const i18n = createI18n({\n fallbackLng: DEFAULT_LANGUAGE,\n supportedLngs: [DEFAULT_LANGUAGE],\n interpolation: {\n escapeValue: false,\n // Used for the JsxInterpolator format hook\n alwaysFormat: true,\n },\n ns: [],\n defaultNS: false,\n fallbackNS: false,\n\n // Disable resource loading on init, meaning i18n will be ready to use immediately\n initImmediate: false,\n });\n\n i18n.init();\n if (!i18n.isInitialized) {\n throw new Error('i18next was unexpectedly not initialized');\n }\n\n const interpolator = JsxInterpolator.fromI18n(i18n);\n\n return new MockTranslationApi(i18n, interpolator);\n }\n\n readonly #i18n: I18n;\n readonly #interpolator: JsxInterpolator;\n readonly #registeredRefs = new Set<string>();\n\n private constructor(i18n: I18n, interpolator: JsxInterpolator) {\n this.#i18n = i18n;\n this.#interpolator = interpolator;\n }\n\n getTranslation<TMessages extends { [key in string]: string }>(\n translationRef: TranslationRef<string, TMessages>,\n ): TranslationSnapshot<TMessages> {\n const internalRef = toInternalTranslationRef(translationRef);\n\n if (!this.#registeredRefs.has(internalRef.id)) {\n this.#registeredRefs.add(internalRef.id);\n this.#i18n.addResourceBundle(\n DEFAULT_LANGUAGE,\n internalRef.id,\n internalRef.getDefaultMessages(),\n false, // do not merge\n true, // overwrite existing\n );\n }\n\n const t = this.#interpolator.wrapT<TMessages>(\n this.#i18n.getFixedT(null, internalRef.id),\n );\n\n return {\n ready: true,\n t,\n };\n }\n\n translation$<TMessages extends { [key in string]: string }>(): Observable<\n TranslationSnapshot<TMessages>\n > {\n // No need to implement, getTranslation will always return a ready snapshot\n return new ObservableImpl<TranslationSnapshot<TMessages>>(_subscriber => {\n return () => {};\n });\n }\n}\n"],"names":["createI18n"],"mappings":";;;;;AA+BA,MAAM,gBAAA,GAAmB,IAAA;AAQlB,MAAM,kBAAA,CAA6C;AAAA,EACxD,OAAO,MAAA,GAAS;AACd,IAAA,MAAM,OAAOA,cAAA,CAAW;AAAA,MACtB,WAAA,EAAa,gBAAA;AAAA,MACb,aAAA,EAAe,CAAC,gBAAgB,CAAA;AAAA,MAChC,aAAA,EAAe;AAAA,QACb,WAAA,EAAa,KAAA;AAAA;AAAA,QAEb,YAAA,EAAc;AAAA,OAChB;AAAA,MACA,IAAI,EAAC;AAAA,MACL,SAAA,EAAW,KAAA;AAAA,MACX,UAAA,EAAY,KAAA;AAAA;AAAA,MAGZ,aAAA,EAAe;AAAA,KAChB,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,EAAK;AACV,IAAA,IAAI,CAAC,KAAK,aAAA,EAAe;AACvB,MAAA,MAAM,IAAI,MAAM,0CAA0C,CAAA;AAAA,IAC5D;AAEA,IAAA,MAAM,YAAA,GAAe,eAAA,CAAgB,QAAA,CAAS,IAAI,CAAA;AAElD,IAAA,OAAO,IAAI,kBAAA,CAAmB,IAAA,EAAM,YAAY,CAAA;AAAA,EAClD;AAAA,EAES,KAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA,uBAAsB,GAAA,EAAY;AAAA,EAEnC,WAAA,CAAY,MAAY,YAAA,EAA+B;AAC7D,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAA;AACb,IAAA,IAAA,CAAK,aAAA,GAAgB,YAAA;AAAA,EACvB;AAAA,EAEA,eACE,cAAA,EACgC;AAChC,IAAA,MAAM,WAAA,GAAc,yBAAyB,cAAc,CAAA;AAE3D,IAAA,IAAI,CAAC,IAAA,CAAK,eAAA,CAAgB,GAAA,CAAI,WAAA,CAAY,EAAE,CAAA,EAAG;AAC7C,MAAA,IAAA,CAAK,eAAA,CAAgB,GAAA,CAAI,WAAA,CAAY,EAAE,CAAA;AACvC,MAAA,IAAA,CAAK,KAAA,CAAM,iBAAA;AAAA,QACT,gBAAA;AAAA,QACA,WAAA,CAAY,EAAA;AAAA,QACZ,YAAY,kBAAA,EAAmB;AAAA,QAC/B,KAAA;AAAA;AAAA,QACA;AAAA;AAAA,OACF;AAAA,IACF;AAEA,IAAA,MAAM,CAAA,GAAI,KAAK,aAAA,CAAc,KAAA;AAAA,MAC3B,IAAA,CAAK,KAAA,CAAM,SAAA,CAAU,IAAA,EAAM,YAAY,EAAE;AAAA,KAC3C;AAEA,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,IAAA;AAAA,MACP;AAAA,KACF;AAAA,EACF;AAAA,EAEA,YAAA,GAEE;AAEA,IAAA,OAAO,IAAI,eAA+C,CAAA,WAAA,KAAe;AACvE,MAAA,OAAO,MAAM;AAAA,MAAC,CAAA;AAAA,IAChB,CAAC,CAAA;AAAA,EACH;AACF;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"createApiMock.esm.js","sources":["../../src/apis/createApiMock.ts"],"sourcesContent":["/*\n * Copyright 2026 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 { ApiFactory, type ApiRef } from '@backstage/frontend-plugin-api';\nimport { mockApiFactorySymbol } from './MockWithApiFactory';\n\n/**\n * Represents a mocked version of an API, where you automatically have access to\n * the mocked versions of all of its methods along with a factory that returns\n * that same mock.\n *\n * @public\n */\nexport type ApiMock<TApi> = {\n [mockApiFactorySymbol]: ApiFactory<TApi, TApi, {}>;\n} & {\n [Key in keyof TApi]: TApi[Key] extends (...args: infer Args) => infer Return\n ? TApi[Key] & jest.MockInstance<Return, Args>\n : TApi[Key];\n};\n\n/**\n * Creates a standardized Backstage Utility API mockfactory function for\n * producing mock API instances.\n *\n * @remarks\n *\n * Each method in the mock factory is a `jest.fn()`, and you can optionally pass\n * partial implementations when calling the returned function. No type\n * parameters should be provided to this function, they will be inferred from\n * the provided API reference.\n *\n * @public\n * @example\n * ```ts\n * import { createApiMock } from '@backstage/frontend-test-utils';\n * import { myApiRef } from '../apis';\n *\n * // Set up the mock factory\n * const mock = createApiMock(myApiRef, () => ({\n * greet: jest.fn(),\n * }));\n *\n * // Create a mock with default behavior\n * const api = mock();\n *\n * // Or with a partial implementation\n * const api = mock({ greet: async () => 'Hello!' });\n * expect(api.greet).toHaveBeenCalledTimes(1);\n * ```\n */\nexport function createApiMock<TApi>(\n apiRef: ApiRef<TApi>,\n mockFactory: () => jest.Mocked<TApi>,\n): (partialImpl?: Partial<TApi>) => ApiMock<TApi> {\n return partialImpl => {\n const mock = mockFactory();\n if (partialImpl) {\n for (const [key, impl] of Object.entries(partialImpl)) {\n if (typeof impl === 'function') {\n (mock as any)[key].mockImplementation(impl);\n } else {\n (mock as any)[key] = impl;\n }\n }\n }\n (mock as any)[mockApiFactorySymbol] = {\n api: apiRef,\n deps: {},\n factory: () => mock,\n };\n return mock as unknown as ApiMock<TApi>;\n };\n}\n"],"names":[],"mappings":";;AAgEO,SAAS,aAAA,CACd,QACA,WAAA,EACgD;AAChD,EAAA,OAAO,CAAA,WAAA,KAAe;AACpB,IAAA,MAAM,OAAO,WAAA,EAAY;AACzB,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,KAAA,MAAW,CAAC,GAAA,EAAK,IAAI,KAAK,MAAA,CAAO,OAAA,CAAQ,WAAW,CAAA,EAAG;AACrD,QAAA,IAAI,OAAO,SAAS,UAAA,EAAY;AAC9B,UAAC,IAAA,CAAa,GAAG,CAAA,CAAE,kBAAA,CAAmB,IAAI,CAAA;AAAA,QAC5C,CAAA,MAAO;AACL,UAAC,IAAA,CAAa,GAAG,CAAA,GAAI,IAAA;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AACA,IAAC,IAAA,CAAa,oBAAoB,CAAA,GAAI;AAAA,MACpC,GAAA,EAAK,MAAA;AAAA,MACL,MAAM,EAAC;AAAA,MACP,SAAS,MAAM;AAAA,KACjB;AACA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AACF;;;;"}
1
+ {"version":3,"file":"createApiMock.esm.js","sources":["../../src/apis/createApiMock.ts"],"sourcesContent":["/*\n * Copyright 2026 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 { ApiFactory, type ApiRef } from '@backstage/frontend-plugin-api';\nimport { mockApiFactorySymbol } from './MockWithApiFactory';\n\n/**\n * Represents a mocked version of an API, where you automatically have access to\n * the mocked versions of all of its methods along with a factory that returns\n * that same mock.\n *\n * @public\n */\nexport type ApiMock<TApi> = {\n [mockApiFactorySymbol]: ApiFactory<TApi, TApi, {}>;\n} & {\n [Key in keyof TApi]: TApi[Key] extends (...args: infer Args) => infer Return\n ? TApi[Key] & jest.MockInstance<Return, Args>\n : TApi[Key];\n};\n\n/**\n * Creates a standardized Backstage Utility API mockfactory function for\n * producing mock API instances.\n *\n * @remarks\n *\n * Each method in the mock factory is a `jest.fn()`, and you can optionally pass\n * partial implementations when calling the returned function. No type\n * parameters should be provided to this function, they will be inferred from\n * the provided API reference.\n *\n * @public\n * @example\n * ```ts\n * import { createApiMock } from '@backstage/frontend-test-utils';\n * import { myApiRef } from '../apis';\n *\n * // Set up the mock factory\n * const mock = createApiMock(myApiRef, () => ({\n * greet: jest.fn(),\n * }));\n *\n * // Create a mock with default behavior\n * const api = mock();\n *\n * // Or with a partial implementation\n * const api = mock({ greet: async () => 'Hello!' });\n * expect(api.greet).toHaveBeenCalledTimes(1);\n * ```\n */\nexport function createApiMock<TApi>(\n apiRef: ApiRef<TApi>,\n mockFactory: () => jest.Mocked<TApi>,\n): (partialImpl?: Partial<TApi>) => ApiMock<TApi> {\n return partialImpl => {\n const mock = mockFactory();\n if (partialImpl) {\n for (const [key, impl] of Object.entries(partialImpl)) {\n if (typeof impl === 'function') {\n (mock as any)[key].mockImplementation(impl);\n } else {\n (mock as any)[key] = impl;\n }\n }\n }\n (mock as any)[mockApiFactorySymbol] = {\n api: apiRef,\n deps: {},\n factory: () => mock,\n };\n return mock as unknown as ApiMock<TApi>;\n };\n}\n"],"names":[],"mappings":";;AAgEO,SAAS,aAAA,CACd,QACA,WAAA,EACgD;AAChD,EAAA,OAAO,CAAA,WAAA,KAAe;AACpB,IAAA,MAAM,OAAO,WAAA,EAAY;AACzB,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,KAAA,MAAW,CAAC,GAAA,EAAK,IAAI,KAAK,MAAA,CAAO,OAAA,CAAQ,WAAW,CAAA,EAAG;AACrD,QAAA,IAAI,OAAO,SAAS,UAAA,EAAY;AAC9B,UAAC,IAAA,CAAa,GAAG,CAAA,CAAE,kBAAA,CAAmB,IAAI,CAAA;AAAA,QAC5C,CAAA,MAAO;AACL,UAAC,IAAA,CAAa,GAAG,CAAA,GAAI,IAAA;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AACA,IAAC,IAAA,CAAa,oBAAoB,CAAA,GAAI;AAAA,MACpC,GAAA,EAAK,MAAA;AAAA,MACL,MAAM,EAAC;AAAA,MACP,SAAS,MAAM;AAAA,KACjB;AACA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AACF;;"}
@@ -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 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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"}
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":";;;;;;;;;;;;;;;AA2FO,IAAU;AAAA,CAAV,CAAUA,SAAAA,KAAV;AAaE,EAAA,SAAS,KAAA,GAA0C;AACxD,IAAA,MAAM,QAAA,GAAW,IAAI,YAAA,EAAa;AAClC,IAAA,OAAO,oBAAA;AAAA,MACL,WAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AANO,EAAAA,SAAAA,CAAS,KAAA,GAAA,KAAA;AAYT,EAAA,CAAA,CAAUC,MAAAA,KAAV;AASE,IAAMA,MAAAA,CAAA,IAAA,GAAO,aAAA,CAAc,WAAA,EAAa,OAAO;AAAA,MACpD,IAAA,EAAM,KAAK,EAAA,EAAG;AAAA,MACd,MAAA,EAAQ,KAAK,EAAA;AAAG,KAClB,CAAE,CAAA;AAAA,EAAA,CAAA,EAZa,KAAA,GAAAD,SAAAA,CAAA,KAAA,KAAAA,SAAAA,CAAA,KAAA,GAAA,EAAA,CAAA,CAAA;AA4BV,EAAA,SAAS,aAAa,OAAA,EAEe;AAC1C,IAAA,MAAM,QAAA,GAAW,IAAI,mBAAA,CAAoB,OAAO,CAAA;AAChD,IAAA,OAAO,oBAAA;AAAA,MACL,kBAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AARO,EAAAA,SAAAA,CAAS,YAAA,GAAA,YAAA;AAcT,EAAA,CAAA,CAAUE,aAAAA,KAAV;AASE,IAAMA,aAAAA,CAAA,IAAA,GAAO,aAAA,CAAc,kBAAA,EAAoB,OAAO;AAAA,MAC3D,YAAA,EAAc,KAAK,EAAA,EAAG;AAAA,MACtB,kBAAA,EAAoB,KAAK,EAAA,EAAG;AAAA,MAC5B,QAAA,EAAU,KAAK,EAAA,EAAG;AAAA,MAClB,IAAA,EAAM,KAAK,EAAA;AAAG,KAChB,CAAE,CAAA;AAAA,EAAA,CAAA,EAda,YAAA,GAAAF,SAAAA,CAAA,YAAA,KAAAA,SAAAA,CAAA,YAAA,GAAA,EAAA,CAAA,CAAA;AAsBV,EAAA,SAAS,SAAA,GACmB;AACjC,IAAA,MAAM,QAAA,GAAW,IAAI,gBAAA,EAAiB;AACtC,IAAA,OAAO,oBAAA,CAAqB,iBAAiB,QAAQ,CAAA;AAAA,EAEvD;AALO,EAAAA,SAAAA,CAAS,SAAA,GAAA,SAAA;AAYT,EAAA,CAAA,CAAUG,UAAAA,KAAV;AACE,IAAMA,UAAAA,CAAA,IAAA,GAAO,aAAA,CAAc,eAAA,EAAiB,OAAO;AAAA,MACxD,YAAA,EAAc,KAAK,EAAA;AAAG,KACxB,CAAE,CAAA;AAAA,EAAA,CAAA,EAHa,SAAA,GAAAH,SAAAA,CAAA,SAAA,KAAAA,SAAAA,CAAA,SAAA,GAAA,EAAA,CAAA,CAAA;AAYV,EAAA,SAAS,WAAA,GACqB;AACnC,IAAA,MAAM,QAAA,GAAW,mBAAmB,MAAA,EAAO;AAC3C,IAAA,OAAO,oBAAA;AAAA,MACL,iBAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAPO,EAAAA,SAAAA,CAAS,WAAA,GAAA,WAAA;AAcT,EAAA,CAAA,CAAUI,YAAAA,KAAV;AAME,IAAMA,YAAAA,CAAA,IAAA,GAAO,aAAA,CAAc,iBAAA,EAAmB,OAAO;AAAA,MAC1D,cAAA,EAAgB,KAAK,EAAA,EAAG;AAAA,MACxB,YAAA,EAAc,KAAK,EAAA;AAAG,KACxB,CAAE,CAAA;AAAA,EAAA,CAAA,EATa,WAAA,GAAAJ,SAAAA,CAAA,WAAA,KAAAA,SAAAA,CAAA,WAAA,GAAA,EAAA,CAAA,CAAA;AAiBV,EAAA,SAAS,OAAO,OAAA,EAE2B;AAChD,IAAA,MAAM,QAAA,GAAW,IAAI,aAAA,CAAc,EAAE,MAAM,OAAA,EAAS,IAAA,IAAQ,EAAC,EAAG,CAAA;AAChE,IAAA,OAAO,oBAAA,CAAqB,cAAc,QAAQ,CAAA;AAAA,EAEpD;AANO,EAAAA,SAAAA,CAAS,MAAA,GAAA,MAAA;AAaT,EAAA,CAAA,CAAUK,OAAAA,KAAV;AACE,IAAMA,OAAAA,CAAA,IAAA,GAAO,aAAA,CAAc,YAAA,EAAc,OAAO;AAAA,MACrD,GAAA,EAAK,KAAK,EAAA,EAAG;AAAA,MACb,IAAA,EAAM,KAAK,EAAA,EAAG;AAAA,MACd,GAAA,EAAK,KAAK,EAAA,EAAG;AAAA,MACb,WAAA,EAAa,KAAK,EAAA,EAAG;AAAA,MACrB,SAAA,EAAW,KAAK,EAAA,EAAG;AAAA,MACnB,iBAAA,EAAmB,KAAK,EAAA,EAAG;AAAA,MAC3B,cAAA,EAAgB,KAAK,EAAA,EAAG;AAAA,MACxB,sBAAA,EAAwB,KAAK,EAAA,EAAG;AAAA,MAChC,SAAA,EAAW,KAAK,EAAA,EAAG;AAAA,MACnB,iBAAA,EAAmB,KAAK,EAAA,EAAG;AAAA,MAC3B,UAAA,EAAY,KAAK,EAAA,EAAG;AAAA,MACpB,kBAAA,EAAoB,KAAK,EAAA,EAAG;AAAA,MAC5B,SAAA,EAAW,KAAK,EAAA,EAAG;AAAA,MACnB,iBAAA,EAAmB,KAAK,EAAA,EAAG;AAAA,MAC3B,cAAA,EAAgB,KAAK,EAAA,EAAG;AAAA,MACxB,sBAAA,EAAwB,KAAK,EAAA;AAAG,KAClC,CAAE,CAAA;AAAA,EAAA,CAAA,EAlBa,MAAA,GAAAL,SAAAA,CAAA,MAAA,KAAAA,SAAAA,CAAA,MAAA,GAAA,EAAA,CAAA,CAAA;AA0BV,EAAA,SAAS,UAAU,OAAA,EAE0B;AAClD,IAAA,MAAM,OAAA,GAAU,SAAS,OAAA,IAAW,oBAAA;AACpC,IAAA,MAAM,QAAA,GAAyB;AAAA,MAC7B,MAAM,WAAW,QAAA,EAAkB;AACjC,QAAA,OAAO,CAAA,EAAG,OAAO,CAAA,KAAA,EAAQ,QAAQ,CAAA,CAAA;AAAA,MACnC;AAAA,KACF;AACA,IAAA,OAAO,oBAAA,CAAqB,iBAAiB,QAAQ,CAAA;AAAA,EAEvD;AAXO,EAAAA,SAAAA,CAAS,SAAA,GAAA,SAAA;AAkBT,EAAA,CAAA,CAAUM,UAAAA,KAAV;AACE,IAAMA,UAAAA,CAAA,IAAA,GAAO,aAAA,CAAc,eAAA,EAAiB,OAAO;AAAA,MACxD,UAAA,EAAY,KAAK,EAAA;AAAG,KACtB,CAAE,CAAA;AAAA,EAAA,CAAA,EAHa,SAAA,GAAAN,SAAAA,CAAA,SAAA,KAAAA,SAAAA,CAAA,SAAA,GAAA,EAAA,CAAA,CAAA;AAWV,EAAA,SAAS,SAAS,OAAA,EAOW;AAClC,IAAA,MAAM;AAAA,MACJ,aAAA,GAAgB,mBAAA;AAAA,MAChB,mBAAA,GAAsB,CAAC,mBAAmB,CAAA;AAAA,MAC1C,KAAA;AAAA,MACA,KAAA;AAAA,MACA,WAAA;AAAA,MACA;AAAA,KACF,GAAI,WAAW,EAAC;AAChB,IAAA,MAAM,QAAA,GAAwB;AAAA,MAC5B,MAAM,oBAAA,GAAuB;AAC3B,QAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,mBAAA,EAAqB,aAAA,EAAc;AAAA,MAC5D,CAAA;AAAA,MACA,MAAM,cAAA,GAAiB;AACrB,QAAA,OAAO,EAAE,KAAA,EAAM;AAAA,MACjB,CAAA;AAAA,MACA,MAAM,cAAA,GAAiB;AACrB,QAAA,OAAO,EAAE,KAAA,EAAO,WAAA,EAAa,OAAA,EAAQ;AAAA,MACvC,CAAA;AAAA,MACA,MAAM,OAAA,GAAU;AAAA,MAAC;AAAA,KACnB;AACA,IAAA,OAAO,oBAAA,CAAqB,gBAAgB,QAAQ,CAAA;AAAA,EAEtD;AA9BO,EAAAA,SAAAA,CAAS,QAAA,GAAA,QAAA;AAqCT,EAAA,CAAA,CAAUO,SAAAA,KAAV;AACE,IAAMA,SAAAA,CAAA,IAAA,GAAO,aAAA,CAAc,cAAA,EAAgB,OAAO;AAAA,MACvD,oBAAA,EAAsB,KAAK,EAAA,EAAG;AAAA,MAC9B,cAAA,EAAgB,KAAK,EAAA,EAAG;AAAA,MACxB,cAAA,EAAgB,KAAK,EAAA,EAAG;AAAA,MACxB,OAAA,EAAS,KAAK,EAAA;AAAG,KACnB,CAAE,CAAA;AAAA,EAAA,CAAA,EANa,QAAA,GAAAP,SAAAA,CAAA,QAAA,KAAAA,SAAAA,CAAA,QAAA,GAAA,EAAA,CAAA,CAAA;AAcV,EAAA,SAAS,WAAW,OAAA,EAO+B;AACxD,IAAA,MAAM,iBAAiB,OAAA,EAAS,SAAA;AAChC,IAAA,MAAM,UACJ,OAAO,cAAA,KAAmB,aACtB,cAAA,GACA,MAAM,kBAAkB,eAAA,CAAgB,KAAA;AAC9C,IAAA,MAAM,QAAA,GAAW,IAAI,iBAAA,CAAkB,OAAO,CAAA;AAC9C,IAAA,OAAO,oBAAA;AAAA,MACL,gBAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAlBO,EAAAA,SAAAA,CAAS,UAAA,GAAA,UAAA;AAyBT,EAAA,CAAA,CAAUQ,WAAAA,KAAV;AACE,IAAMA,WAAAA,CAAA,IAAA,GAAO,aAAA,CAAc,gBAAA,EAAkB,OAAO;AAAA,MACzD,SAAA,EAAW,KAAK,EAAA;AAAG,KACrB,CAAE,CAAA;AAAA,EAAA,CAAA,EAHa,UAAA,GAAAR,SAAAA,CAAA,UAAA,KAAAA,SAAAA,CAAA,UAAA,GAAA,EAAA,CAAA,CAAA;AAWV,EAAA,SAAS,QAAQ,OAAA,EAE4B;AAClD,IAAA,MAAM,QAAA,GAAW,cAAA,CAAe,MAAA,CAAO,OAAA,EAAS,IAAI,CAAA;AACpD,IAAA,OAAO,oBAAA,CAAqB,eAAe,QAAQ,CAAA;AAAA,EAErD;AANO,EAAAA,SAAAA,CAAS,OAAA,GAAA,OAAA;AAaT,EAAA,CAAA,CAAUS,QAAAA,KAAV;AACE,IAAMA,QAAAA,CAAA,IAAA,GAAO,aAAA,CAAc,aAAA,EAAe,OAAO;AAAA,MACtD,SAAA,EAAW,KAAK,EAAA,EAAG;AAAA,MACnB,QAAA,EAAU,KAAK,EAAA,EAAG;AAAA,MAClB,GAAA,EAAK,KAAK,EAAA,EAAG;AAAA,MACb,MAAA,EAAQ,KAAK,EAAA,EAAG;AAAA,MAChB,QAAA,EAAU,KAAK,EAAA;AAAG,KACpB,CAAE,CAAA;AAAA,EAAA,CAAA,EAPa,OAAA,GAAAT,SAAAA,CAAA,OAAA,KAAAA,SAAAA,CAAA,OAAA,GAAA,EAAA,CAAA,CAAA;AAeV,EAAA,SAAS,MAAM,OAAA,EAE0B;AAC9C,IAAA,MAAM,QAAA,GAAW,IAAI,YAAA,CAAa,OAAO,CAAA;AACzC,IAAA,OAAO,oBAAA,CAAqB,aAAa,QAAQ,CAAA;AAAA,EAEnD;AANO,EAAAA,SAAAA,CAAS,KAAA,GAAA,KAAA;AAaT,EAAA,CAAA,CAAUU,MAAAA,KAAV;AACE,IAAMA,MAAAA,CAAA,IAAA,GAAO,aAAA,CAAc,WAAA,EAAa,OAAO;AAAA,MACpD,IAAA,EAAM,KAAK,EAAA,EAAG;AAAA,MACd,MAAA,EAAQ,KAAK,EAAA;AAAG,KAClB,CAAE,CAAA;AAAA,EAAA,CAAA,EAJa,KAAA,GAAAV,SAAAA,CAAA,KAAA,KAAAA,SAAAA,CAAA,KAAA,GAAA,EAAA,CAAA,CAAA;AAYV,EAAA,SAAS,MACd,OAAA,EAC6C;AAC7C,IAAA,MAAM,QAAA,GAAW,IAAI,YAAA,CAAa,OAAO,CAAA;AACzC,IAAA,OAAO,oBAAA,CAAqB,aAAa,QAAQ,CAAA;AAAA,EAEnD;AANO,EAAAA,SAAAA,CAAS,KAAA,GAAA,KAAA;AAaT,EAAA,CAAA,CAAUW,MAAAA,KAAV;AACE,IAAMA,MAAAA,CAAA,IAAA,GAAO,aAAA,CAAc,WAAA,EAAa,OAAO;AAAA,MACpD,KAAA,EAAO,KAAK,EAAA;AAAG,KACjB,CAAE,CAAA;AAAA,EAAA,CAAA,EAHa,KAAA,GAAAX,SAAAA,CAAA,KAAA,KAAAA,SAAAA,CAAA,KAAA,GAAA,EAAA,CAAA,CAAA;AAAA,CAAA,EA7WF,QAAA,KAAA,QAAA,GAAA,EAAA,CAAA,CAAA;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"createExtensionTester.esm.js","sources":["../../src/app/createExtensionTester.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 {\n AppNode,\n AppTree,\n Extension,\n ExtensionDataRef,\n ExtensionDefinition,\n ExtensionDefinitionParameters,\n coreExtensionData,\n} from '@backstage/frontend-plugin-api';\nimport { Config, ConfigReader } from '@backstage/config';\nimport { JsonArray, JsonObject, JsonValue } from '@backstage/types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveAppTree } from '../../../frontend-app-api/src/tree/resolveAppTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveAppNodeSpecs } from '../../../frontend-app-api/src/tree/resolveAppNodeSpecs';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { instantiateAppNodeTree } from '../../../frontend-app-api/src/tree/instantiateAppNodeTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { readAppExtensionsConfig } from '../../../frontend-app-api/src/tree/readAppExtensionsConfig';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { createErrorCollector } from '../../../frontend-app-api/src/wiring/createErrorCollector';\nimport { OpaqueExtensionDefinition } from '@internal/frontend';\nimport { resolveTestApiEntries, TestApiPairs } from '../apis/TestApiProvider';\n\n/**\n * Represents a snapshot of an extension in the app tree.\n *\n * @public\n */\nexport interface ExtensionSnapshotNode {\n /** The ID of the extension */\n id: string;\n /** The IDs of output data refs produced by this extension */\n outputs?: string[];\n /** Child extensions organized by input name */\n children?: Record<string, ExtensionSnapshotNode[]>;\n /** Whether this extension is disabled */\n disabled?: true;\n}\n\n/** @public */\nexport class ExtensionQuery<UOutput extends ExtensionDataRef> {\n #node: AppNode;\n\n constructor(node: AppNode) {\n this.#node = node;\n }\n\n get node() {\n return this.#node;\n }\n\n get instance() {\n const instance = this.#node.instance;\n if (!instance) {\n throw new Error(\n `Unable to access the instance of extension with ID '${\n this.#node.spec.id\n }'`,\n );\n }\n return instance;\n }\n\n get<TId extends UOutput['id']>(\n ref: ExtensionDataRef<any, TId, any>,\n ): UOutput extends ExtensionDataRef<infer IData, TId, infer IConfig>\n ? IConfig['optional'] extends true\n ? IData | undefined\n : IData\n : never {\n return this.instance.getData(ref);\n }\n}\n\n/** @public */\nexport class ExtensionTester<UOutput extends ExtensionDataRef> {\n /** @internal */\n static forSubject<\n T extends ExtensionDefinitionParameters,\n const TApiPairs extends any[],\n >(\n subject: ExtensionDefinition<T>,\n options?: {\n config?: T['configInput'];\n apis?: readonly [...TestApiPairs<TApiPairs>];\n },\n ): ExtensionTester<NonNullable<T['output']>> {\n const tester = new ExtensionTester(options?.apis);\n tester.add(subject, options as T['configInput'] & {});\n return tester;\n }\n\n #tree?: AppTree;\n #apis?: readonly any[];\n\n readonly #extensions = new Array<{\n id: string;\n extension: Extension<any>;\n definition: ExtensionDefinition;\n config?: JsonValue;\n }>();\n\n private constructor(apis?: readonly any[]) {\n this.#apis = apis;\n }\n\n add<T extends ExtensionDefinitionParameters>(\n extension: ExtensionDefinition<T>,\n options?: { config?: T['configInput'] },\n ): ExtensionTester<UOutput> {\n if (this.#tree) {\n throw new Error(\n 'Cannot add more extensions accessing the extension tree',\n );\n }\n\n const { name, namespace } = OpaqueExtensionDefinition.toInternal(extension);\n\n const definition = {\n ...extension,\n // setting name \"test\" as fallback\n name: !namespace && !name ? 'test' : name,\n };\n\n const resolvedExtension = resolveExtensionDefinition(definition);\n\n this.#extensions.push({\n id: resolvedExtension.id,\n extension: resolvedExtension,\n definition,\n config: options?.config as JsonValue,\n });\n\n return this;\n }\n\n get<TId extends UOutput['id']>(\n ref: ExtensionDataRef<any, TId, any>,\n ): UOutput extends ExtensionDataRef<infer IData, TId, infer IConfig>\n ? IConfig['optional'] extends true\n ? IData | undefined\n : IData\n : never {\n const tree = this.#resolveTree();\n\n return new ExtensionQuery(tree.root).get(ref);\n }\n\n query<T extends ExtensionDefinitionParameters>(\n extension: ExtensionDefinition<T>,\n ): ExtensionQuery<NonNullable<T['output']>> {\n const tree = this.#resolveTree();\n\n // Same fallback logic as in .add\n const { name, namespace } = OpaqueExtensionDefinition.toInternal(extension);\n const definition = {\n ...extension,\n name: !namespace && !name ? 'test' : name,\n };\n const actualId = resolveExtensionDefinition(definition).id;\n\n const node = tree.nodes.get(actualId);\n\n if (!node) {\n throw new Error(\n `Extension with ID '${actualId}' not found, please make sure it's added to the tester.`,\n );\n } else if (!node.instance) {\n throw new Error(\n `Extension with ID '${actualId}' has not been instantiated, because it is not part of the test subject's extension tree.`,\n );\n }\n return new ExtensionQuery(node);\n }\n\n reactElement(): JSX.Element {\n const tree = this.#resolveTree();\n\n const element = new ExtensionQuery(tree.root).get(\n coreExtensionData.reactElement,\n );\n\n if (!element) {\n throw new Error(\n 'No element found. Make sure the extension has a `coreExtensionData.reactElement` output, or use the `.get(...)` to access output data directly instead',\n );\n }\n\n return element;\n }\n\n /**\n * Returns a snapshot of the extension tree structure for testing and debugging.\n * Convenient to use with Jest's inline snapshot testing.\n *\n * @example\n * ```tsx\n * const tester = createExtensionTester(myExtension);\n * expect(tester.snapshot()).toMatchInlineSnapshot();\n * ```\n */\n snapshot(): ExtensionSnapshotNode {\n const tree = this.#resolveTree();\n\n const buildNode = (node: AppNode): ExtensionSnapshotNode => {\n const outputs = node.instance\n ? Array.from(node.instance.getDataRefs())\n .map(ref => ref.id)\n .sort()\n : [];\n\n const children: Record<string, ExtensionSnapshotNode[]> = {};\n for (const [inputName, attachedNodes] of node.edges.attachments) {\n children[inputName] = attachedNodes\n .map(n => buildNode(n))\n .sort((a, b) => a.id.localeCompare(b.id));\n }\n\n const result: ExtensionSnapshotNode = {\n id: node.spec.id,\n };\n\n // Only include non-empty/non-default fields\n if (outputs.length > 0) {\n result.outputs = outputs;\n }\n if (Object.keys(children).length > 0) {\n result.children = children;\n }\n if (node.spec.disabled) {\n result.disabled = true;\n }\n\n return result;\n };\n\n return buildNode(tree.root);\n }\n\n #resolveTree() {\n if (this.#tree) {\n return this.#tree;\n }\n\n const [subject] = this.#extensions;\n if (!subject) {\n throw new Error(\n 'No subject found. At least one extension should be added to the tester.',\n );\n }\n\n const collector = createErrorCollector();\n\n const tree = resolveAppTree(\n subject.id,\n resolveAppNodeSpecs({\n features: [],\n builtinExtensions: this.#extensions.map(_ => _.extension),\n parameters: readAppExtensionsConfig(this.#getConfig()),\n collector,\n }),\n collector,\n );\n\n const apiHolder = resolveTestApiEntries(this.#apis ?? []);\n\n instantiateAppNodeTree(tree.root, apiHolder, collector);\n\n const errors = collector.collectErrors();\n if (errors) {\n throw new Error(\n `Failed to resolve the extension tree: ${errors\n .map(e => e.message)\n .join(', ')}`,\n );\n }\n\n this.#tree = tree;\n\n return tree;\n }\n\n #getConfig(additionalConfig?: JsonObject): Config {\n const [subject, ...rest] = this.#extensions;\n\n const extensionsConfig: JsonArray = [\n ...rest.flatMap(extension =>\n extension.config\n ? [\n {\n [extension.id]: {\n config: extension.config,\n },\n },\n ]\n : [],\n ),\n {\n [subject.id]: {\n config: subject.config,\n disabled: false,\n },\n },\n ];\n\n return ConfigReader.fromConfigs([\n { context: 'render-config', data: additionalConfig ?? {} },\n {\n context: 'test',\n data: {\n app: {\n extensions: extensionsConfig,\n },\n },\n },\n ]);\n }\n}\n\n/** @public */\nexport function createExtensionTester<\n T extends ExtensionDefinitionParameters,\n TApiPairs extends any[] = any[],\n>(\n subject: ExtensionDefinition<T>,\n options?: {\n config?: T['configInput'];\n apis?: readonly [...TestApiPairs<TApiPairs>];\n },\n): ExtensionTester<NonNullable<T['output']>> {\n return ExtensionTester.forSubject(subject, options);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"createExtensionTester.esm.js","sources":["../../src/app/createExtensionTester.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 {\n AppNode,\n AppTree,\n Extension,\n ExtensionDataRef,\n ExtensionDefinition,\n ExtensionDefinitionParameters,\n coreExtensionData,\n} from '@backstage/frontend-plugin-api';\nimport { Config, ConfigReader } from '@backstage/config';\nimport { JsonArray, JsonObject, JsonValue } from '@backstage/types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveAppTree } from '../../../frontend-app-api/src/tree/resolveAppTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveAppNodeSpecs } from '../../../frontend-app-api/src/tree/resolveAppNodeSpecs';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { instantiateAppNodeTree } from '../../../frontend-app-api/src/tree/instantiateAppNodeTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { readAppExtensionsConfig } from '../../../frontend-app-api/src/tree/readAppExtensionsConfig';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { createErrorCollector } from '../../../frontend-app-api/src/wiring/createErrorCollector';\nimport { OpaqueExtensionDefinition } from '@internal/frontend';\nimport { resolveTestApiEntries, TestApiPairs } from '../apis/TestApiProvider';\n\n/**\n * Represents a snapshot of an extension in the app tree.\n *\n * @public\n */\nexport interface ExtensionSnapshotNode {\n /** The ID of the extension */\n id: string;\n /** The IDs of output data refs produced by this extension */\n outputs?: string[];\n /** Child extensions organized by input name */\n children?: Record<string, ExtensionSnapshotNode[]>;\n /** Whether this extension is disabled */\n disabled?: true;\n}\n\n/** @public */\nexport class ExtensionQuery<UOutput extends ExtensionDataRef> {\n #node: AppNode;\n\n constructor(node: AppNode) {\n this.#node = node;\n }\n\n get node() {\n return this.#node;\n }\n\n get instance() {\n const instance = this.#node.instance;\n if (!instance) {\n throw new Error(\n `Unable to access the instance of extension with ID '${\n this.#node.spec.id\n }'`,\n );\n }\n return instance;\n }\n\n get<TId extends UOutput['id']>(\n ref: ExtensionDataRef<any, TId, any>,\n ): UOutput extends ExtensionDataRef<infer IData, TId, infer IConfig>\n ? IConfig['optional'] extends true\n ? IData | undefined\n : IData\n : never {\n return this.instance.getData(ref);\n }\n}\n\n/** @public */\nexport class ExtensionTester<UOutput extends ExtensionDataRef> {\n /** @internal */\n static forSubject<\n T extends ExtensionDefinitionParameters,\n const TApiPairs extends any[],\n >(\n subject: ExtensionDefinition<T>,\n options?: {\n config?: T['configInput'];\n apis?: readonly [...TestApiPairs<TApiPairs>];\n },\n ): ExtensionTester<NonNullable<T['output']>> {\n const tester = new ExtensionTester(options?.apis);\n tester.add(subject, options as T['configInput'] & {});\n return tester;\n }\n\n #tree?: AppTree;\n #apis?: readonly any[];\n\n readonly #extensions = new Array<{\n id: string;\n extension: Extension<any>;\n definition: ExtensionDefinition;\n config?: JsonValue;\n }>();\n\n private constructor(apis?: readonly any[]) {\n this.#apis = apis;\n }\n\n add<T extends ExtensionDefinitionParameters>(\n extension: ExtensionDefinition<T>,\n options?: { config?: T['configInput'] },\n ): ExtensionTester<UOutput> {\n if (this.#tree) {\n throw new Error(\n 'Cannot add more extensions accessing the extension tree',\n );\n }\n\n const { name, namespace } = OpaqueExtensionDefinition.toInternal(extension);\n\n const definition = {\n ...extension,\n // setting name \"test\" as fallback\n name: !namespace && !name ? 'test' : name,\n };\n\n const resolvedExtension = resolveExtensionDefinition(definition);\n\n this.#extensions.push({\n id: resolvedExtension.id,\n extension: resolvedExtension,\n definition,\n config: options?.config as JsonValue,\n });\n\n return this;\n }\n\n get<TId extends UOutput['id']>(\n ref: ExtensionDataRef<any, TId, any>,\n ): UOutput extends ExtensionDataRef<infer IData, TId, infer IConfig>\n ? IConfig['optional'] extends true\n ? IData | undefined\n : IData\n : never {\n const tree = this.#resolveTree();\n\n return new ExtensionQuery(tree.root).get(ref);\n }\n\n query<T extends ExtensionDefinitionParameters>(\n extension: ExtensionDefinition<T>,\n ): ExtensionQuery<NonNullable<T['output']>> {\n const tree = this.#resolveTree();\n\n // Same fallback logic as in .add\n const { name, namespace } = OpaqueExtensionDefinition.toInternal(extension);\n const definition = {\n ...extension,\n name: !namespace && !name ? 'test' : name,\n };\n const actualId = resolveExtensionDefinition(definition).id;\n\n const node = tree.nodes.get(actualId);\n\n if (!node) {\n throw new Error(\n `Extension with ID '${actualId}' not found, please make sure it's added to the tester.`,\n );\n } else if (!node.instance) {\n throw new Error(\n `Extension with ID '${actualId}' has not been instantiated, because it is not part of the test subject's extension tree.`,\n );\n }\n return new ExtensionQuery(node);\n }\n\n reactElement(): JSX.Element {\n const tree = this.#resolveTree();\n\n const element = new ExtensionQuery(tree.root).get(\n coreExtensionData.reactElement,\n );\n\n if (!element) {\n throw new Error(\n 'No element found. Make sure the extension has a `coreExtensionData.reactElement` output, or use the `.get(...)` to access output data directly instead',\n );\n }\n\n return element;\n }\n\n /**\n * Returns a snapshot of the extension tree structure for testing and debugging.\n * Convenient to use with Jest's inline snapshot testing.\n *\n * @example\n * ```tsx\n * const tester = createExtensionTester(myExtension);\n * expect(tester.snapshot()).toMatchInlineSnapshot();\n * ```\n */\n snapshot(): ExtensionSnapshotNode {\n const tree = this.#resolveTree();\n\n const buildNode = (node: AppNode): ExtensionSnapshotNode => {\n const outputs = node.instance\n ? Array.from(node.instance.getDataRefs())\n .map(ref => ref.id)\n .sort()\n : [];\n\n const children: Record<string, ExtensionSnapshotNode[]> = {};\n for (const [inputName, attachedNodes] of node.edges.attachments) {\n children[inputName] = attachedNodes\n .map(n => buildNode(n))\n .sort((a, b) => a.id.localeCompare(b.id));\n }\n\n const result: ExtensionSnapshotNode = {\n id: node.spec.id,\n };\n\n // Only include non-empty/non-default fields\n if (outputs.length > 0) {\n result.outputs = outputs;\n }\n if (Object.keys(children).length > 0) {\n result.children = children;\n }\n if (node.spec.disabled) {\n result.disabled = true;\n }\n\n return result;\n };\n\n return buildNode(tree.root);\n }\n\n #resolveTree() {\n if (this.#tree) {\n return this.#tree;\n }\n\n const [subject] = this.#extensions;\n if (!subject) {\n throw new Error(\n 'No subject found. At least one extension should be added to the tester.',\n );\n }\n\n const collector = createErrorCollector();\n\n const tree = resolveAppTree(\n subject.id,\n resolveAppNodeSpecs({\n features: [],\n builtinExtensions: this.#extensions.map(_ => _.extension),\n parameters: readAppExtensionsConfig(this.#getConfig()),\n collector,\n }),\n collector,\n );\n\n const apiHolder = resolveTestApiEntries(this.#apis ?? []);\n\n instantiateAppNodeTree(tree.root, apiHolder, collector);\n\n const errors = collector.collectErrors();\n if (errors) {\n throw new Error(\n `Failed to resolve the extension tree: ${errors\n .map(e => e.message)\n .join(', ')}`,\n );\n }\n\n this.#tree = tree;\n\n return tree;\n }\n\n #getConfig(additionalConfig?: JsonObject): Config {\n const [subject, ...rest] = this.#extensions;\n\n const extensionsConfig: JsonArray = [\n ...rest.flatMap(extension =>\n extension.config\n ? [\n {\n [extension.id]: {\n config: extension.config,\n },\n },\n ]\n : [],\n ),\n {\n [subject.id]: {\n config: subject.config,\n disabled: false,\n },\n },\n ];\n\n return ConfigReader.fromConfigs([\n { context: 'render-config', data: additionalConfig ?? {} },\n {\n context: 'test',\n data: {\n app: {\n extensions: extensionsConfig,\n },\n },\n },\n ]);\n }\n}\n\n/** @public */\nexport function createExtensionTester<\n T extends ExtensionDefinitionParameters,\n TApiPairs extends any[] = any[],\n>(\n subject: ExtensionDefinition<T>,\n options?: {\n config?: T['configInput'];\n apis?: readonly [...TestApiPairs<TApiPairs>];\n },\n): ExtensionTester<NonNullable<T['output']>> {\n return ExtensionTester.forSubject(subject, options);\n}\n"],"names":[],"mappings":"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"}