Merge branch 'master' into scorecard-tech-insights
Signed-off-by: irma12 <irma@roadie.io>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-permission-node': patch
|
||||
---
|
||||
|
||||
New package containing common permission and authorization utilities for backend plugins. For more information, see the [authorization PRFC](https://github.com/backstage/backstage/pull/7761).
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Providing an empty values array in an EntityFilter will now return no matches.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Fix bug where there was error log lines written when failing to `JSON.parse` things that were not `JSON` values.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/core-plugin-api': patch
|
||||
---
|
||||
|
||||
Deprecated `register` option of `createPlugin` and the `outputs` methods of the plugin instance.
|
||||
|
||||
Introduces the `featureFlags` property to define your feature flags instead.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Added Azure DevOps discovery processor
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Detect a duplicate entities when adding locations through dry run
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Bump gitbeaker to the latest version
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-api-docs': patch
|
||||
---
|
||||
|
||||
Adjusted some styles in the OpenAPI definition, for elements which were barely readable in dark mode.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-cost-insights': patch
|
||||
---
|
||||
|
||||
Supply featureFlags using featureFlag config option.
|
||||
@@ -71,3 +71,4 @@
|
||||
| [Epic Games](https://www.epicgames.com) | [Brian Jung](https://github.com/brian-at-epic), [Jeff Goldian](https://github.com/jeffgoldian-Epic) | Developer Portal: Service Catalog, Documentation, Software Templates and more making our internal teams' lives easier! |
|
||||
| [Globo](https://globo.com) | [Carlos Gusmão](https://github.com/caeugusmao), [Guilherme Vierno](https://github.com/vierno), [Denis Aoki](https://github.com/dnsaoki2), [Maycon Dionisio](https://github.com/MayconDionisio), | Reduce the friction of accessing the information engineers need about Globo's digital services through a coherent and centralized experience. |
|
||||
| [QBE](https://www.qbe.com/) | [Daniel Steel](https://github.com/danielsteelqbe), [Pete Jespers](https://github.com/petejespersqbe) | Developer portal allowing our global teams to explore and create applications, documentation and cloud infrastructure easily and quickly 🚀 |
|
||||
| [LogMeIn](https://www.logmein.com) | [Lorenzo Orsatti](https://github.com/lorsatti) | Improve onboarding experience of new developers. Discover faster and painlessly developer documentation, API definitions and team information. Provide useful dev metrics in a central place. Provide easy-to-use templates for new services. |
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
{{- $frontendUrl := urlParse .Values.appConfig.app.baseUrl}}
|
||||
{{- $backendUrl := urlParse .Values.appConfig.backend.baseUrl}}
|
||||
{{- $lighthouseUrl := urlParse .Values.appConfig.lighthouse.baseUrl}}
|
||||
|
||||
{{/* Determine the api type for the ingress */}}
|
||||
{{- if lt .Capabilities.KubeVersion.Minor "19" }}
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
{{- else if ge .Capabilities.KubeVersion.Minor "19" }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
{{- end }}
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "backstage.fullname" . }}-ingress
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
id: discovery
|
||||
title: Azure DevOps Discovery
|
||||
sidebar_label: Discovery
|
||||
# prettier-ignore
|
||||
description: Automatically discovering catalog entities from repositories in an Azure DevOps organization
|
||||
---
|
||||
|
||||
The Azure DevOps integration has a special discovery processor for discovering
|
||||
catalog entities within an Azure DevOps. The processor will crawl the Azure
|
||||
DevOps organization and register entities matching the configured path. This can
|
||||
be useful as an alternative to static locations or manually adding things to the
|
||||
catalog.
|
||||
|
||||
To use the discovery processor, you'll need a GitHub integration
|
||||
[set up](locations.md) with a `AZURE_TOKEN`. Then you can add a location target
|
||||
to the catalog configuration:
|
||||
|
||||
```yaml
|
||||
catalog:
|
||||
locations:
|
||||
# Scan all repositories for a catalog-info.yaml in the root of the default branch
|
||||
- type: azure-discovery
|
||||
target: https://dev.azure.com/myorg/myproject
|
||||
# Or use a custom pattern for a subset of all repositories with default repository
|
||||
- type: azure-discovery
|
||||
target: https://dev.azure.com/myorg/myproject/_git/service-*
|
||||
# Or use a custom file format and location
|
||||
- type: azure-discovery
|
||||
target: https://dev.azure.com/myorg/myproject/_git/*?path=/src/*/catalog-info.yaml
|
||||
```
|
||||
|
||||
Note the `azure-discovery` type, as this is not a regular `url` processor.
|
||||
|
||||
When using a custom pattern, the target is composed of five parts:
|
||||
|
||||
- The base instance URL, `https://dev.azure.com` in this case
|
||||
- The organization name which is required, `myorg` in this case
|
||||
- The project name which is required, `myproject` in this case
|
||||
- The repository blob to scan, which accepts \* wildcard tokens and must be
|
||||
added after `_git/`. This can simply be `*` to scan all repositories in the
|
||||
project.
|
||||
- The path within each repository to find the catalog YAML file. This will
|
||||
usually be `/catalog-info.yaml`, `/src/*/catalog-info.yaml` or a similar
|
||||
variation for catalog files stored in the root directory of each repository.
|
||||
|
||||
_Note:_ the path parameter follows the same rules as the search on Azure DevOps
|
||||
web interface. For more details visit the
|
||||
[official search documentation](https://docs.microsoft.com/en-us/azure/devops/project/search/get-started-search?view=azure-devops)
|
||||
@@ -261,10 +261,13 @@ analytics events captured.
|
||||
Use it like this:
|
||||
|
||||
```tsx
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { analyticsApiRef } from '@backstage/core-plugin-api';
|
||||
import { MockAnalyticsApi, wrapInTestApp } from '@backstage/test-utils';
|
||||
import { render, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { analyticsApiRef } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
MockAnalyticsApi,
|
||||
TestApiProvider,
|
||||
wrapInTestApp,
|
||||
} from '@backstage/test-utils';
|
||||
|
||||
describe('SomeComponent', () => {
|
||||
it('should capture event on click', () => {
|
||||
@@ -274,9 +277,9 @@ describe('SomeComponent', () => {
|
||||
// Render the component being tested
|
||||
const { getByText } = render(
|
||||
wrapInTestApp(
|
||||
<ApiProvider apis={ApiRegistry.from([[analyticsApiRef, apiSpy]])}>
|
||||
<TestApiProvider apis={[[analyticsApiRef, apiSpy]]}>
|
||||
<SomeComponentUnderTest />
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -125,7 +125,11 @@
|
||||
{
|
||||
"type": "subcategory",
|
||||
"label": "Azure",
|
||||
"ids": ["integrations/azure/locations", "integrations/azure/org"]
|
||||
"ids": [
|
||||
"integrations/azure/locations",
|
||||
"integrations/azure/discovery",
|
||||
"integrations/azure/org"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "subcategory",
|
||||
|
||||
@@ -88,6 +88,7 @@ nav:
|
||||
- Discovery: 'integrations/aws-s3/discovery.md'
|
||||
- Azure:
|
||||
- Locations: 'integrations/azure/locations.md'
|
||||
- Discovery: 'integrations/azure/discovery.md'
|
||||
- Org Data: 'integrations/azure/org.md'
|
||||
- Bitbucket:
|
||||
- Locations: 'integrations/bitbucket/locations.md'
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
"devDependencies": {
|
||||
"@backstage/test-utils": "^0.1.22",
|
||||
"@rjsf/core": "^3.2.1",
|
||||
"@testing-library/cypress": "^7.0.1",
|
||||
"@testing-library/cypress": "^8.0.2",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@testing-library/user-event": "^13.1.8",
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { EntityLayout } from '@backstage/plugin-catalog';
|
||||
import {
|
||||
DefaultStarredEntitiesApi,
|
||||
@@ -22,7 +21,11 @@ import {
|
||||
starredEntitiesApiRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { githubActionsApiRef } from '@backstage/plugin-github-actions';
|
||||
import { MockStorageApi, renderInTestApp } from '@backstage/test-utils';
|
||||
import {
|
||||
MockStorageApi,
|
||||
renderInTestApp,
|
||||
TestApiProvider,
|
||||
} from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
import { cicdContent } from './EntityPage';
|
||||
|
||||
@@ -45,22 +48,22 @@ describe('EntityPage Test', () => {
|
||||
|
||||
const mockedApi = {
|
||||
listWorkflowRuns: jest.fn().mockResolvedValue([]),
|
||||
getWorkflow: jest.fn(),
|
||||
getWorkflowRun: jest.fn(),
|
||||
reRunWorkflow: jest.fn(),
|
||||
listJobsForWorkflowRun: jest.fn(),
|
||||
downloadJobLogsForWorkflowRun: jest.fn(),
|
||||
} as jest.Mocked<typeof githubActionsApiRef.T>;
|
||||
|
||||
const apis = ApiRegistry.with(githubActionsApiRef, mockedApi).with(
|
||||
starredEntitiesApiRef,
|
||||
new DefaultStarredEntitiesApi({ storageApi: MockStorageApi.create() }),
|
||||
);
|
||||
};
|
||||
|
||||
describe('cicdContent', () => {
|
||||
it('Should render GitHub Actions View', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[githubActionsApiRef, mockedApi],
|
||||
[
|
||||
starredEntitiesApiRef,
|
||||
new DefaultStarredEntitiesApi({
|
||||
storageApi: MockStorageApi.create(),
|
||||
}),
|
||||
],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
<EntityLayout>
|
||||
<EntityLayout.Route path="/ci-cd" title="CI-CD">
|
||||
@@ -68,7 +71,7 @@ describe('EntityPage Test', () => {
|
||||
</EntityLayout.Route>
|
||||
</EntityLayout>
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(rendered.getByText('ExampleComponent')).toBeInTheDocument();
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
"@backstage/plugin-tech-insights-node": "^0.1.0",
|
||||
"@backstage/plugin-tech-insights-backend-module-jsonfc": "^0.1.0",
|
||||
"@backstage/plugin-todo-backend": "^0.1.13",
|
||||
"@gitbeaker/node": "^30.2.0",
|
||||
"@gitbeaker/node": "^34.6.0",
|
||||
"@octokit/rest": "^18.5.3",
|
||||
"azure-devops-node-api": "^11.0.1",
|
||||
"dockerode": "^3.3.1",
|
||||
|
||||
@@ -349,6 +349,78 @@ describe('Integration Test', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('getFeatureFlags should return feature flags', async () => {
|
||||
const storageFlags = new LocalStorageFeatureFlags();
|
||||
jest.spyOn(storageFlags, 'registerFlag');
|
||||
|
||||
const apis = [
|
||||
noOpAnalyticsApi,
|
||||
createApiFactory({
|
||||
api: featureFlagsApiRef,
|
||||
deps: { configApi: configApiRef },
|
||||
factory() {
|
||||
return storageFlags;
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
const app = new AppManager({
|
||||
apis,
|
||||
defaultApis: [],
|
||||
themes: [
|
||||
{
|
||||
id: 'light',
|
||||
title: 'Light Theme',
|
||||
variant: 'light',
|
||||
Provider: ({ children }) => <>{children}</>,
|
||||
},
|
||||
],
|
||||
icons,
|
||||
plugins: [
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
featureFlags: [
|
||||
{
|
||||
name: 'foo',
|
||||
},
|
||||
],
|
||||
register: p => p.featureFlags.register('name'),
|
||||
}),
|
||||
],
|
||||
components,
|
||||
configLoader: async () => [],
|
||||
bindRoutes: ({ bind }) => {
|
||||
bind(plugin1.externalRoutes, {
|
||||
extRouteRef1: plugin1RouteRef,
|
||||
extRouteRef2: plugin2RouteRef,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const Provider = app.getProvider();
|
||||
const Router = app.getRouter();
|
||||
|
||||
await renderWithEffects(
|
||||
<Provider>
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/" element={<ExposedComponent />} />
|
||||
<Route path="/foo" element={<HiddenComponent />} />
|
||||
</Routes>
|
||||
</Router>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(storageFlags.registerFlag).toHaveBeenCalledWith({
|
||||
name: 'name',
|
||||
pluginId: 'test',
|
||||
});
|
||||
expect(storageFlags.registerFlag).toHaveBeenCalledWith({
|
||||
name: 'foo',
|
||||
pluginId: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
it('should track route changes via analytics api', async () => {
|
||||
const mockAnalyticsApi = new MockAnalyticsApi();
|
||||
const apis = [createApiFactory(analyticsApiRef, mockAnalyticsApi)];
|
||||
|
||||
@@ -272,17 +272,26 @@ export class AppManager implements BackstageApp {
|
||||
const featureFlagsApi = this.getApiHolder().get(featureFlagsApiRef)!;
|
||||
|
||||
for (const plugin of this.plugins.values()) {
|
||||
for (const output of plugin.output()) {
|
||||
switch (output.type) {
|
||||
case 'feature-flag': {
|
||||
featureFlagsApi.registerFlag({
|
||||
name: output.name,
|
||||
pluginId: plugin.getId(),
|
||||
});
|
||||
break;
|
||||
if ('getFeatureFlags' in plugin) {
|
||||
for (const flag of plugin.getFeatureFlags()) {
|
||||
featureFlagsApi.registerFlag({
|
||||
name: flag.name,
|
||||
pluginId: plugin.getId(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (const output of plugin.output()) {
|
||||
switch (output.type) {
|
||||
case 'feature-flag': {
|
||||
featureFlagsApi.registerFlag({
|
||||
name: output.name,
|
||||
pluginId: plugin.getId(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,6 +258,7 @@ export type BackstagePlugin<
|
||||
getId(): string;
|
||||
output(): PluginOutput[];
|
||||
getApis(): Iterable<AnyApiFactory>;
|
||||
getFeatureFlags(): Iterable<PluginFeatureFlagConfig>;
|
||||
provide<T>(extension: Extension<T>): T;
|
||||
routes: Routes;
|
||||
externalRoutes: ExternalRoutes;
|
||||
@@ -463,7 +464,7 @@ export type FeatureFlag = {
|
||||
pluginId: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export type FeatureFlagOutput = {
|
||||
type: 'feature-flag';
|
||||
name: string;
|
||||
@@ -682,14 +683,20 @@ export type PluginConfig<
|
||||
register?(hooks: PluginHooks): void;
|
||||
routes?: Routes;
|
||||
externalRoutes?: ExternalRoutes;
|
||||
featureFlags?: PluginFeatureFlagConfig[];
|
||||
};
|
||||
|
||||
// @public
|
||||
export type PluginFeatureFlagConfig = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export type PluginHooks = {
|
||||
featureFlags: FeatureFlagsHooks;
|
||||
};
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export type PluginOutput = FeatureFlagOutput;
|
||||
|
||||
// @public
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin } from './Plugin';
|
||||
|
||||
describe('Plugin Feature Flag', () => {
|
||||
it('should be able to register and receive feature flags', () => {
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
featureFlags: [{ name: 'test' }],
|
||||
}).getFeatureFlags(),
|
||||
).toEqual([{ name: 'test' }]);
|
||||
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
register({ featureFlags }) {
|
||||
featureFlags.register('blob');
|
||||
},
|
||||
}).getFeatureFlags(),
|
||||
).toEqual([{ name: 'blob' }]);
|
||||
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
register({ featureFlags }) {
|
||||
featureFlags.register('blob');
|
||||
},
|
||||
featureFlags: [{ name: 'test' }],
|
||||
}).getFeatureFlags(),
|
||||
).toEqual([{ name: 'test' }, { name: 'blob' }]);
|
||||
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
}).getFeatureFlags(),
|
||||
).toEqual([]);
|
||||
|
||||
/* deprecated tests */
|
||||
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
featureFlags: [{ name: 'test' }],
|
||||
}).output(),
|
||||
).toEqual([{ name: 'test', type: 'feature-flag' }]);
|
||||
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
register({ featureFlags }) {
|
||||
featureFlags.register('blob');
|
||||
},
|
||||
}).output(),
|
||||
).toEqual([{ name: 'blob', type: 'feature-flag' }]);
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
register({ featureFlags }) {
|
||||
featureFlags.register('blob');
|
||||
},
|
||||
featureFlags: [{ name: 'test' }],
|
||||
}).output(),
|
||||
).toEqual([
|
||||
{ name: 'test', type: 'feature-flag' },
|
||||
{ name: 'blob', type: 'feature-flag' },
|
||||
]);
|
||||
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
}).output(),
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
Extension,
|
||||
AnyRoutes,
|
||||
AnyExternalRoutes,
|
||||
PluginFeatureFlagConfig,
|
||||
} from './types';
|
||||
import { AnyApiFactory } from '../apis';
|
||||
|
||||
@@ -44,6 +45,14 @@ export class PluginImpl<
|
||||
return this.config.apis ?? [];
|
||||
}
|
||||
|
||||
getFeatureFlags(): Iterable<PluginFeatureFlagConfig> {
|
||||
const registeredFlags = this.output()
|
||||
.filter(({ type }) => type === 'feature-flag')
|
||||
.map(({ name }) => ({ name }));
|
||||
|
||||
return registeredFlags;
|
||||
}
|
||||
|
||||
get routes(): Routes {
|
||||
return this.config.routes ?? ({} as Routes);
|
||||
}
|
||||
@@ -56,11 +65,18 @@ export class PluginImpl<
|
||||
if (this.storedOutput) {
|
||||
return this.storedOutput;
|
||||
}
|
||||
if (!this.config.register) {
|
||||
return [];
|
||||
const outputs = new Array<PluginOutput>();
|
||||
this.storedOutput = outputs;
|
||||
|
||||
if (this.config.featureFlags) {
|
||||
for (const flag of this.config.featureFlags) {
|
||||
outputs.push({ type: 'feature-flag', name: flag.name });
|
||||
}
|
||||
}
|
||||
|
||||
const outputs = new Array<PluginOutput>();
|
||||
if (!this.config.register) {
|
||||
return outputs;
|
||||
}
|
||||
|
||||
this.config.register({
|
||||
featureFlags: {
|
||||
@@ -70,7 +86,6 @@ export class PluginImpl<
|
||||
},
|
||||
});
|
||||
|
||||
this.storedOutput = outputs;
|
||||
return this.storedOutput;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,4 +25,5 @@ export type {
|
||||
PluginConfig,
|
||||
PluginHooks,
|
||||
PluginOutput,
|
||||
PluginFeatureFlagConfig,
|
||||
} from './types';
|
||||
|
||||
@@ -19,7 +19,7 @@ import { AnyApiFactory } from '../apis/system';
|
||||
|
||||
/**
|
||||
* Replace with using {@link RouteRef}s.
|
||||
*
|
||||
* @deprecated will be removed
|
||||
* @public
|
||||
*/
|
||||
export type FeatureFlagOutput = {
|
||||
@@ -31,6 +31,7 @@ export type FeatureFlagOutput = {
|
||||
* {@link FeatureFlagOutput} type.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link BackstagePlugin.getFeatureFlags} instead.
|
||||
*/
|
||||
export type PluginOutput = FeatureFlagOutput;
|
||||
|
||||
@@ -71,13 +72,30 @@ export type BackstagePlugin<
|
||||
ExternalRoutes extends AnyExternalRoutes = {},
|
||||
> = {
|
||||
getId(): string;
|
||||
/**
|
||||
* @deprecated use getFeatureFlags instead.
|
||||
* */
|
||||
output(): PluginOutput[];
|
||||
getApis(): Iterable<AnyApiFactory>;
|
||||
/**
|
||||
* Returns all registered feature flags for this plugin.
|
||||
*/
|
||||
getFeatureFlags(): Iterable<PluginFeatureFlagConfig>;
|
||||
provide<T>(extension: Extension<T>): T;
|
||||
routes: Routes;
|
||||
externalRoutes: ExternalRoutes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Plugin feature flag configuration.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type PluginFeatureFlagConfig = {
|
||||
/** Feature flag name */
|
||||
name: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Plugin descriptor type.
|
||||
*
|
||||
@@ -89,14 +107,17 @@ export type PluginConfig<
|
||||
> = {
|
||||
id: string;
|
||||
apis?: Iterable<AnyApiFactory>;
|
||||
/** @deprecated use featureFlags property instead for defining feature flags */
|
||||
register?(hooks: PluginHooks): void;
|
||||
routes?: Routes;
|
||||
externalRoutes?: ExternalRoutes;
|
||||
featureFlags?: PluginFeatureFlagConfig[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds hooks registered by the plugin.
|
||||
*
|
||||
* @deprecated - feature flags are now registered in plugin config under featureFlags
|
||||
* @public
|
||||
*/
|
||||
export type PluginHooks = {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"@backstage/plugin-search-backend": "^{{version '@backstage/plugin-search-backend'}}",
|
||||
"@backstage/plugin-search-backend-node": "^{{version '@backstage/plugin-search-backend-node'}}",
|
||||
"@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}",
|
||||
"@gitbeaker/node": "^30.2.0",
|
||||
"@gitbeaker/node": "^34.6.0",
|
||||
"@octokit/rest": "^18.5.3",
|
||||
"dockerode": "^3.3.1",
|
||||
"express": "^4.17.1",
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
import { AppThemeApi, appThemeApiRef } from '@backstage/core-plugin-api';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
@@ -24,7 +24,6 @@ import { SidebarThemeSwitcher } from './SidebarThemeSwitcher';
|
||||
|
||||
describe('SidebarThemeSwitcher', () => {
|
||||
let appThemeApi: jest.Mocked<AppThemeApi>;
|
||||
let apiRegistry: ApiRegistry;
|
||||
|
||||
beforeEach(() => {
|
||||
appThemeApi = {
|
||||
@@ -51,15 +50,13 @@ describe('SidebarThemeSwitcher', () => {
|
||||
theme: {} as unknown as BackstageTheme,
|
||||
},
|
||||
]);
|
||||
|
||||
apiRegistry = ApiRegistry.with(appThemeApiRef, appThemeApi);
|
||||
});
|
||||
|
||||
it('should display current theme', async () => {
|
||||
const { getByLabelText, getByRole, getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={apiRegistry}>
|
||||
<TestApiProvider apis={[[appThemeApiRef, appThemeApi]]}>
|
||||
<SidebarThemeSwitcher />
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
const button = getByLabelText('Switch Theme');
|
||||
@@ -76,9 +73,9 @@ describe('SidebarThemeSwitcher', () => {
|
||||
|
||||
it('should select different theme', async () => {
|
||||
const { getByLabelText, getByRole, getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={apiRegistry}>
|
||||
<TestApiProvider apis={[[appThemeApiRef, appThemeApi]]}>
|
||||
<SidebarThemeSwitcher />
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
const button = getByLabelText('Switch Theme');
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
AlertApiForwarder,
|
||||
ApiRegistry,
|
||||
ErrorAlerter,
|
||||
ErrorApiForwarder,
|
||||
GithubAuth,
|
||||
@@ -27,78 +26,57 @@ import {
|
||||
configApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
|
||||
const builder = ApiRegistry.builder();
|
||||
|
||||
builder.add(configApiRef, new ConfigReader({}));
|
||||
|
||||
const alertApi = builder.add(alertApiRef, new AlertApiForwarder());
|
||||
|
||||
builder.add(errorApiRef, new ErrorAlerter(alertApi, new ErrorApiForwarder()));
|
||||
|
||||
builder.add(identityApiRef, {
|
||||
const configApi = new ConfigReader({});
|
||||
const alertApi = new AlertApiForwarder();
|
||||
const errorApi = new ErrorAlerter(alertApi, new ErrorApiForwarder());
|
||||
const identityApi = {
|
||||
getUserId: () => 'guest',
|
||||
getProfile: () => ({ email: 'guest@example.com' }),
|
||||
getIdToken: () => undefined,
|
||||
signOut: async () => {},
|
||||
};
|
||||
const oauthRequestApi = new OAuthRequestManager();
|
||||
const googleAuthApi = GoogleAuth.create({
|
||||
apiOrigin: 'http://localhost:7007',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
});
|
||||
const githubAuthApi = GithubAuth.create({
|
||||
apiOrigin: 'http://localhost:7007',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
});
|
||||
const gitlabAuthApi = GitlabAuth.create({
|
||||
apiOrigin: 'http://localhost:7007',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
});
|
||||
const oktaAuthApi = OktaAuth.create({
|
||||
apiOrigin: 'http://localhost:7007',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
});
|
||||
const auth0AuthApi = Auth0Auth.create({
|
||||
apiOrigin: 'http://localhost:7007',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
});
|
||||
const oauth2Api = OAuth2.create({
|
||||
apiOrigin: 'http://localhost:7007',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
});
|
||||
|
||||
const oauthRequestApi = builder.add(
|
||||
oauthRequestApiRef,
|
||||
new OAuthRequestManager(),
|
||||
);
|
||||
|
||||
builder.add(
|
||||
googleAuthApiRef,
|
||||
GoogleAuth.create({
|
||||
apiOrigin: 'http://localhost:7007',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
|
||||
builder.add(
|
||||
githubAuthApiRef,
|
||||
GithubAuth.create({
|
||||
apiOrigin: 'http://localhost:7007',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
|
||||
builder.add(
|
||||
gitlabAuthApiRef,
|
||||
GitlabAuth.create({
|
||||
apiOrigin: 'http://localhost:7007',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
|
||||
builder.add(
|
||||
oktaAuthApiRef,
|
||||
OktaAuth.create({
|
||||
apiOrigin: 'http://localhost:7007',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
|
||||
builder.add(
|
||||
auth0AuthApiRef,
|
||||
Auth0Auth.create({
|
||||
apiOrigin: 'http://localhost:7007',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
|
||||
builder.add(
|
||||
oauth2ApiRef,
|
||||
OAuth2.create({
|
||||
apiOrigin: 'http://localhost:7007',
|
||||
basePath: '/auth/',
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
|
||||
export const apis = builder.build();
|
||||
export const apis = [
|
||||
[configApiRef, configApi],
|
||||
[alertApiRef, alertApi],
|
||||
[errorApiRef, errorApi],
|
||||
[identityApiRef, identityApi],
|
||||
[oauthRequestApiRef, oauthRequestApi],
|
||||
[googleAuthApiRef, googleAuthApi],
|
||||
[githubAuthApiRef, githubAuthApi],
|
||||
[gitlabAuthApiRef, gitlabAuthApi],
|
||||
[oktaAuthApiRef, oktaAuthApi],
|
||||
[auth0AuthApiRef, auth0AuthApi],
|
||||
[oauth2ApiRef, oauth2Api],
|
||||
];
|
||||
|
||||
@@ -6,17 +6,17 @@ import { useDarkMode } from 'storybook-dark-mode';
|
||||
import { apis } from './apis';
|
||||
|
||||
import { Content, AlertDisplay } from '@backstage/core-components';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
|
||||
addDecorator(story => (
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider apis={apis}>
|
||||
<ThemeProvider theme={useDarkMode() ? darkTheme : lightTheme}>
|
||||
<CssBaseline>
|
||||
<AlertDisplay />
|
||||
<Content>{story()}</Content>
|
||||
</CssBaseline>
|
||||
</ThemeProvider>
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
));
|
||||
|
||||
addParameters({
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
|
||||
version: v1.22.1
|
||||
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
|
||||
ignore:
|
||||
SNYK-JS-BROWSERSLIST-1090194:
|
||||
- '*':
|
||||
reason: Developer tools are not a valid target for ReDoS attacks
|
||||
expires: 2022-05-20T00:00:00.000Z
|
||||
created: 2021-11-20T00:00:00.000Z
|
||||
|
||||
SNYK-JS-IMMER-1540542:
|
||||
- '*':
|
||||
reason: Prototype pollution is not an effective attack against a CLI as it already executes arbitrary code
|
||||
expires: 2022-05-20T00:00:00.000Z
|
||||
created: 2021-11-20T00:00:00.000Z
|
||||
|
||||
patch: {}
|
||||
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import {
|
||||
createExternalRouteRef,
|
||||
createRouteRef,
|
||||
@@ -29,6 +28,7 @@ import React, { useEffect } from 'react';
|
||||
import { Route, Routes } from 'react-router';
|
||||
import { MockErrorApi } from './apis';
|
||||
import { renderInTestApp, wrapInTestApp } from './appWrappers';
|
||||
import { TestApiProvider } from './TestApiProvider';
|
||||
|
||||
describe('wrapInTestApp', () => {
|
||||
it('should provide routing and warn about missing act()', async () => {
|
||||
@@ -111,9 +111,9 @@ describe('wrapInTestApp', () => {
|
||||
};
|
||||
|
||||
const rendered = await renderInTestApp(
|
||||
<ApiProvider apis={ApiRegistry.with(errorApiRef, mockErrorApi)}>
|
||||
<TestApiProvider apis={[[errorApiRef, mockErrorApi]]}>
|
||||
<A />
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(rendered.getByText('foo')).toBeInTheDocument();
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
|
||||
import { ApiEntity } from '@backstage/catalog-model';
|
||||
import { EntityProvider } from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { ApiDocsConfig, apiDocsConfigRef } from '../../config';
|
||||
import { OpenApiDefinitionWidget } from '../OpenApiDefinitionWidget';
|
||||
import { ApiDefinitionCard } from './ApiDefinitionCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<ApiDefinitionCard />', () => {
|
||||
const apiDocsConfig: jest.Mocked<ApiDocsConfig> = {
|
||||
@@ -31,10 +30,10 @@ describe('<ApiDefinitionCard />', () => {
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(apiDocsConfigRef, apiDocsConfig);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider apis={[[apiDocsConfigRef, apiDocsConfig]]}>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -15,11 +15,10 @@
|
||||
*/
|
||||
|
||||
import { ApiEntity } from '@backstage/catalog-model';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
import { ApiDocsConfig, apiDocsConfigRef } from '../../config';
|
||||
import { ApiTypeTitle } from './ApiTypeTitle';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<ApiTypeTitle />', () => {
|
||||
const apiDocsConfig: jest.Mocked<ApiDocsConfig> = {
|
||||
@@ -28,10 +27,10 @@ describe('<ApiTypeTitle />', () => {
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(apiDocsConfigRef, apiDocsConfig);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider apis={[[apiDocsConfigRef, apiDocsConfig]]}>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -15,11 +15,7 @@
|
||||
*/
|
||||
|
||||
import { Entity, RELATION_MEMBER_OF } from '@backstage/catalog-model';
|
||||
import {
|
||||
ApiProvider,
|
||||
ApiRegistry,
|
||||
ConfigReader,
|
||||
} from '@backstage/core-app-api';
|
||||
import { ConfigReader } from '@backstage/core-app-api';
|
||||
import { TableColumn, TableProps } from '@backstage/core-components';
|
||||
import {
|
||||
ConfigApi,
|
||||
@@ -34,7 +30,11 @@ import {
|
||||
entityRouteRef,
|
||||
starredEntitiesApiRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils';
|
||||
import {
|
||||
MockStorageApi,
|
||||
TestApiProvider,
|
||||
wrapInTestApp,
|
||||
} from '@backstage/test-utils';
|
||||
import DashboardIcon from '@material-ui/icons/Dashboard';
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
@@ -88,8 +88,8 @@ describe('ApiCatalogPage', () => {
|
||||
const renderWrapped = (children: React.ReactNode) =>
|
||||
render(
|
||||
wrapInTestApp(
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.from([
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[catalogApiRef, catalogApi],
|
||||
[configApiRef, configApi],
|
||||
[storageApiRef, storageApi],
|
||||
@@ -98,10 +98,10 @@ describe('ApiCatalogPage', () => {
|
||||
new DefaultStarredEntitiesApi({ storageApi }),
|
||||
],
|
||||
[apiDocsConfigRef, apiDocsConfig],
|
||||
])}
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
|
||||
@@ -21,12 +21,11 @@ import {
|
||||
EntityProvider,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { ApiDocsConfig, apiDocsConfigRef } from '../../config';
|
||||
import { ConsumedApisCard } from './ConsumedApisCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<ConsumedApisCard />', () => {
|
||||
const apiDocsConfig: jest.Mocked<ApiDocsConfig> = {
|
||||
@@ -43,13 +42,15 @@ describe('<ConsumedApisCard />', () => {
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogApi).with(
|
||||
apiDocsConfigRef,
|
||||
apiDocsConfig,
|
||||
);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[catalogApiRef, catalogApi],
|
||||
[apiDocsConfigRef, apiDocsConfig],
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -21,12 +21,11 @@ import {
|
||||
EntityProvider,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { ApiDocsConfig, apiDocsConfigRef } from '../../config';
|
||||
import { HasApisCard } from './HasApisCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<HasApisCard />', () => {
|
||||
const apiDocsConfig: jest.Mocked<ApiDocsConfig> = {
|
||||
@@ -43,13 +42,15 @@ describe('<HasApisCard />', () => {
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogApi).with(
|
||||
apiDocsConfigRef,
|
||||
apiDocsConfig,
|
||||
);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[catalogApiRef, catalogApi],
|
||||
[apiDocsConfigRef, apiDocsConfig],
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -21,12 +21,11 @@ import {
|
||||
EntityProvider,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { ApiDocsConfig, apiDocsConfigRef } from '../../config';
|
||||
import { ProvidedApisCard } from './ProvidedApisCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<ProvidedApisCard />', () => {
|
||||
const apiDocsConfig: jest.Mocked<ApiDocsConfig> = {
|
||||
@@ -43,13 +42,15 @@ describe('<ProvidedApisCard />', () => {
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogApi).with(
|
||||
apiDocsConfigRef,
|
||||
apiDocsConfig,
|
||||
);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[catalogApiRef, catalogApi],
|
||||
[apiDocsConfigRef, apiDocsConfig],
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -21,11 +21,10 @@ import {
|
||||
EntityProvider,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { ConsumingComponentsCard } from './ConsumingComponentsCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<ConsumingComponentsCard />', () => {
|
||||
const catalogApi: jest.Mocked<CatalogApi> = {
|
||||
@@ -39,10 +38,10 @@ describe('<ConsumingComponentsCard />', () => {
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogApi);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -21,11 +21,10 @@ import {
|
||||
EntityProvider,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { ProvidingComponentsCard } from './ProvidingComponentsCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<ProvidingComponentsCard />', () => {
|
||||
const catalogApi: jest.Mocked<CatalogApi> = {
|
||||
@@ -39,10 +38,10 @@ describe('<ProvidingComponentsCard />', () => {
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogApi);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -61,12 +61,13 @@ const useStyles = makeStyles(theme => ({
|
||||
[`& .opblock .opblock-summary-operation-id,
|
||||
.opblock .opblock-summary-path,
|
||||
.opblock .opblock-summary-path__deprecated,
|
||||
.opblock .opblock-section-header h4,
|
||||
.opblock h4,
|
||||
.opblock h5,
|
||||
.opblock a,
|
||||
.opblock li,
|
||||
.parameter__name,
|
||||
.response-col_status,
|
||||
.response-col_links,
|
||||
.responses-inner h4,
|
||||
.responses-inner h5,
|
||||
.opblock-section-header .btn,
|
||||
.tab li,
|
||||
.info li,
|
||||
@@ -89,6 +90,7 @@ const useStyles = makeStyles(theme => ({
|
||||
color: theme.palette.text.disabled,
|
||||
},
|
||||
[`& .opblock-description-wrapper p,
|
||||
.opblock-description-wrapper li,
|
||||
.opblock-external-docs-wrapper p,
|
||||
.opblock-title_normal p,
|
||||
.response-control-media-type__accept-message,
|
||||
|
||||
@@ -16,12 +16,11 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { renderWithEffects } from '@backstage/test-utils';
|
||||
import { renderWithEffects, TestApiProvider } from '@backstage/test-utils';
|
||||
import { BadgesApi, badgesApiRef } from '../api';
|
||||
import { EntityBadgesDialog } from './EntityBadgesDialog';
|
||||
import { EntityProvider } from '@backstage/plugin-catalog-react';
|
||||
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { ErrorApi, errorApiRef } from '@backstage/core-plugin-api';
|
||||
|
||||
describe('EntityBadgesDialog', () => {
|
||||
@@ -42,16 +41,16 @@ describe('EntityBadgesDialog', () => {
|
||||
const mockEntity = { metadata: { name: 'mock' } } as Entity;
|
||||
|
||||
const rendered = await renderWithEffects(
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.with(badgesApiRef, mockApi).with(
|
||||
errorApiRef,
|
||||
{} as ErrorApi,
|
||||
)}
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[badgesApiRef, mockApi],
|
||||
[errorApiRef, {} as ErrorApi],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={mockEntity}>
|
||||
<EntityBadgesDialog open />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
await expect(
|
||||
|
||||
+7
-7
@@ -21,14 +21,11 @@ import { setupServer } from 'msw/node';
|
||||
import {
|
||||
setupRequestMockHandlers,
|
||||
renderInTestApp,
|
||||
TestApiRegistry,
|
||||
} from '@backstage/test-utils';
|
||||
import { useBitriseBuilds } from '../../hooks/useBitriseBuilds';
|
||||
import { BitriseBuildsTable } from './BitriseBuildsTableComponent';
|
||||
import {
|
||||
ApiProvider,
|
||||
ApiRegistry,
|
||||
UrlPatternDiscovery,
|
||||
} from '@backstage/core-app-api';
|
||||
import { ApiProvider, UrlPatternDiscovery } from '@backstage/core-app-api';
|
||||
|
||||
jest.mock('../../hooks/useBitriseBuilds', () => ({
|
||||
useBitriseBuilds: jest.fn(),
|
||||
@@ -40,10 +37,13 @@ describe('BitriseBuildsFetchComponent', () => {
|
||||
setupRequestMockHandlers(server);
|
||||
const mockBaseUrl = 'http://backstage:9191';
|
||||
const discoveryApi = UrlPatternDiscovery.compile(mockBaseUrl);
|
||||
let apis: ApiRegistry;
|
||||
let apis: TestApiRegistry;
|
||||
|
||||
beforeEach(() => {
|
||||
apis = ApiRegistry.with(bitriseApiRef, new BitriseClientApi(discoveryApi));
|
||||
apis = TestApiRegistry.from([
|
||||
bitriseApiRef,
|
||||
new BitriseClientApi(discoveryApi),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should display `no records` message if there are no builds', async () => {
|
||||
|
||||
@@ -174,6 +174,26 @@ export class AwsS3DiscoveryProcessor implements CatalogProcessor {
|
||||
): Promise<boolean>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "AzureDevOpsDiscoveryProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public
|
||||
export class AzureDevOpsDiscoveryProcessor implements CatalogProcessor {
|
||||
constructor(options: { integrations: ScmIntegrations; logger: Logger_2 });
|
||||
// (undocumented)
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
options: {
|
||||
logger: Logger_2;
|
||||
},
|
||||
): AzureDevOpsDiscoveryProcessor;
|
||||
// (undocumented)
|
||||
readLocation(
|
||||
location: LocationSpec,
|
||||
_optional: boolean,
|
||||
emit: CatalogProcessorEmit,
|
||||
): Promise<boolean>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "BitbucketDiscoveryProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { LocationSpec } from '@backstage/catalog-model';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { codeSearch } from './azure';
|
||||
import {
|
||||
AzureDevOpsDiscoveryProcessor,
|
||||
parseUrl,
|
||||
} from './AzureDevOpsDiscoveryProcessor';
|
||||
|
||||
jest.mock('./azure');
|
||||
const mockCodeSearch = codeSearch as jest.MockedFunction<typeof codeSearch>;
|
||||
|
||||
describe('AzureDevOpsDiscoveryProcessor', () => {
|
||||
describe('parseUrl', () => {
|
||||
it('parses well formed URLs', () => {
|
||||
expect(parseUrl('https://dev.azure.com/my-org/my-proj')).toEqual({
|
||||
baseUrl: 'https://dev.azure.com',
|
||||
org: 'my-org',
|
||||
project: 'my-proj',
|
||||
repo: '',
|
||||
catalogPath: '/catalog-info.yaml',
|
||||
});
|
||||
|
||||
expect(
|
||||
parseUrl(
|
||||
'https://dev.azure.com/spotify/engineering/_git/backstage?path=/catalog.yaml',
|
||||
),
|
||||
).toEqual({
|
||||
baseUrl: 'https://dev.azure.com',
|
||||
org: 'spotify',
|
||||
project: 'engineering',
|
||||
repo: 'backstage',
|
||||
catalogPath: '/catalog.yaml',
|
||||
});
|
||||
|
||||
expect(
|
||||
parseUrl(
|
||||
'https://azuredevops.mycompany.com/spotify/engineering/_git/backstage?path=/src/*/catalog.yaml',
|
||||
),
|
||||
).toEqual({
|
||||
baseUrl: 'https://azuredevops.mycompany.com',
|
||||
org: 'spotify',
|
||||
project: 'engineering',
|
||||
repo: 'backstage',
|
||||
catalogPath: '/src/*/catalog.yaml',
|
||||
});
|
||||
});
|
||||
|
||||
it('throws on incorrectly formed URLs', () => {
|
||||
expect(() => parseUrl('https://dev.azure.com')).toThrow();
|
||||
expect(() => parseUrl('https://dev.azure.com//')).toThrow();
|
||||
expect(() => parseUrl('https://dev.azure.com//foo')).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('reject unrelated entries', () => {
|
||||
it('rejects unknown types', async () => {
|
||||
const processor = AzureDevOpsDiscoveryProcessor.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
azure: [{ host: 'dev.azure.com', token: 'blob' }],
|
||||
},
|
||||
}),
|
||||
{ logger: getVoidLogger() },
|
||||
);
|
||||
const location: LocationSpec = {
|
||||
type: 'not-azure-discovery',
|
||||
target: 'https://dev.azure.com',
|
||||
};
|
||||
await expect(
|
||||
processor.readLocation(location, false, () => {}),
|
||||
).resolves.toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects unknown targets', async () => {
|
||||
const processor = AzureDevOpsDiscoveryProcessor.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{ host: 'dev.azure.com', token: 'blob' },
|
||||
{ host: 'azure.myorg.com', token: 'blob' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
{ logger: getVoidLogger() },
|
||||
);
|
||||
const location: LocationSpec = {
|
||||
type: 'azure-discovery',
|
||||
target: 'https://not.azure.com/org/project',
|
||||
};
|
||||
await expect(
|
||||
processor.readLocation(location, false, () => {}),
|
||||
).rejects.toThrow(
|
||||
/There is no Azure integration that matches https:\/\/not.azure.com\/org\/project. Please add a configuration entry for it under integrations.azure/,
|
||||
);
|
||||
});
|
||||
|
||||
describe('handles repositories', () => {
|
||||
const processor = AzureDevOpsDiscoveryProcessor.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [{ host: 'dev.azure.com', token: 'blob' }],
|
||||
},
|
||||
}),
|
||||
{ logger: getVoidLogger() },
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
mockCodeSearch.mockClear();
|
||||
});
|
||||
|
||||
it('output all locations found on from code search', async () => {
|
||||
const location: LocationSpec = {
|
||||
type: 'azure-discovery',
|
||||
target: 'https://dev.azure.com/shopify/engineering',
|
||||
};
|
||||
mockCodeSearch.mockResolvedValueOnce([
|
||||
{
|
||||
fileName: 'catalog-info.yaml',
|
||||
path: '/catalog-info.yaml',
|
||||
repository: {
|
||||
name: 'backstage',
|
||||
},
|
||||
},
|
||||
{
|
||||
fileName: 'catalog-info.yaml',
|
||||
path: '/src/catalog-info.yaml',
|
||||
repository: {
|
||||
name: 'ios-app',
|
||||
},
|
||||
},
|
||||
]);
|
||||
const emitter = jest.fn();
|
||||
|
||||
await processor.readLocation(location, false, emitter);
|
||||
|
||||
expect(mockCodeSearch).toHaveBeenCalledWith(
|
||||
{ host: 'dev.azure.com' },
|
||||
'shopify',
|
||||
'engineering',
|
||||
'',
|
||||
'/catalog-info.yaml',
|
||||
);
|
||||
expect(emitter).toHaveBeenCalledTimes(2);
|
||||
expect(emitter).toHaveBeenCalledWith({
|
||||
type: 'location',
|
||||
location: {
|
||||
type: 'url',
|
||||
target:
|
||||
'https://dev.azure.com/shopify/engineering/_git/backstage?path=/catalog-info.yaml',
|
||||
},
|
||||
optional: true,
|
||||
});
|
||||
expect(emitter).toHaveBeenCalledWith({
|
||||
type: 'location',
|
||||
location: {
|
||||
type: 'url',
|
||||
target:
|
||||
'https://dev.azure.com/shopify/engineering/_git/ios-app?path=/src/catalog-info.yaml',
|
||||
},
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('output single locations from code search', async () => {
|
||||
const location: LocationSpec = {
|
||||
type: 'azure-discovery',
|
||||
target: 'https://dev.azure.com/shopify/engineering/_git/backstage',
|
||||
};
|
||||
mockCodeSearch.mockResolvedValueOnce([
|
||||
{
|
||||
fileName: 'catalog-info.yaml',
|
||||
path: '/catalog-info.yaml',
|
||||
repository: {
|
||||
name: 'backstage',
|
||||
},
|
||||
},
|
||||
]);
|
||||
const emitter = jest.fn();
|
||||
|
||||
await processor.readLocation(location, false, emitter);
|
||||
|
||||
expect(mockCodeSearch).toHaveBeenCalledWith(
|
||||
{ host: 'dev.azure.com' },
|
||||
'shopify',
|
||||
'engineering',
|
||||
'backstage',
|
||||
'/catalog-info.yaml',
|
||||
);
|
||||
expect(emitter).toHaveBeenCalledTimes(1);
|
||||
expect(emitter).toHaveBeenCalledWith({
|
||||
type: 'location',
|
||||
location: {
|
||||
type: 'url',
|
||||
target:
|
||||
'https://dev.azure.com/shopify/engineering/_git/backstage?path=/catalog-info.yaml',
|
||||
},
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('output single locations with different file name from code search', async () => {
|
||||
const location: LocationSpec = {
|
||||
type: 'azure-discovery',
|
||||
target:
|
||||
'https://dev.azure.com/shopify/engineering?path=/src/*/catalog.yaml',
|
||||
};
|
||||
mockCodeSearch.mockResolvedValueOnce([
|
||||
{
|
||||
fileName: 'catalog.yaml',
|
||||
path: '/src/main/catalog.yaml',
|
||||
repository: {
|
||||
name: 'backstage',
|
||||
},
|
||||
},
|
||||
]);
|
||||
const emitter = jest.fn();
|
||||
|
||||
await processor.readLocation(location, false, emitter);
|
||||
|
||||
expect(mockCodeSearch).toHaveBeenCalledWith(
|
||||
{ host: 'dev.azure.com' },
|
||||
'shopify',
|
||||
'engineering',
|
||||
'',
|
||||
'/src/*/catalog.yaml',
|
||||
);
|
||||
expect(emitter).toHaveBeenCalledTimes(1);
|
||||
expect(emitter).toHaveBeenCalledWith({
|
||||
type: 'location',
|
||||
location: {
|
||||
type: 'url',
|
||||
target:
|
||||
'https://dev.azure.com/shopify/engineering/_git/backstage?path=/src/main/catalog.yaml',
|
||||
},
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('output nothing when code search does not find anything', async () => {
|
||||
const location: LocationSpec = {
|
||||
type: 'azure-discovery',
|
||||
target: 'https://dev.azure.com/shopify/engineering/_git/backstage',
|
||||
};
|
||||
mockCodeSearch.mockResolvedValueOnce([]);
|
||||
const emitter = jest.fn();
|
||||
|
||||
await processor.readLocation(location, false, emitter);
|
||||
|
||||
expect(mockCodeSearch).toHaveBeenCalledWith(
|
||||
{ host: 'dev.azure.com' },
|
||||
'shopify',
|
||||
'engineering',
|
||||
'backstage',
|
||||
'/catalog-info.yaml',
|
||||
);
|
||||
expect(emitter).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { LocationSpec } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { Logger } from 'winston';
|
||||
import * as results from './results';
|
||||
import { CatalogProcessor, CatalogProcessorEmit } from './types';
|
||||
import { codeSearch } from './azure';
|
||||
|
||||
/**
|
||||
* Extracts repositories out of an Azure DevOps org.
|
||||
*
|
||||
* The following will create locations for all projects which have a catalog-info.yaml
|
||||
* on the default branch. The first is shorthand for the second.
|
||||
*
|
||||
* target: "https://dev.azure.com/org/project"
|
||||
* or
|
||||
* target: https://dev.azure.com/org/project?path=/catalog-info.yaml
|
||||
*
|
||||
* You may also explicitly specify a single repo:
|
||||
*
|
||||
* target: https://dev.azure.com/org/project/_git/repo
|
||||
**/
|
||||
export class AzureDevOpsDiscoveryProcessor implements CatalogProcessor {
|
||||
private readonly integrations: ScmIntegrations;
|
||||
private readonly logger: Logger;
|
||||
|
||||
static fromConfig(config: Config, options: { logger: Logger }) {
|
||||
const integrations = ScmIntegrations.fromConfig(config);
|
||||
|
||||
return new AzureDevOpsDiscoveryProcessor({
|
||||
...options,
|
||||
integrations,
|
||||
});
|
||||
}
|
||||
|
||||
constructor(options: { integrations: ScmIntegrations; logger: Logger }) {
|
||||
this.integrations = options.integrations;
|
||||
this.logger = options.logger;
|
||||
}
|
||||
|
||||
async readLocation(
|
||||
location: LocationSpec,
|
||||
_optional: boolean,
|
||||
emit: CatalogProcessorEmit,
|
||||
): Promise<boolean> {
|
||||
if (location.type !== 'azure-discovery') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const azureConfig = this.integrations.azure.byUrl(location.target)?.config;
|
||||
if (!azureConfig) {
|
||||
throw new Error(
|
||||
`There is no Azure integration that matches ${location.target}. Please add a configuration entry for it under integrations.azure`,
|
||||
);
|
||||
}
|
||||
|
||||
const { baseUrl, org, project, repo, catalogPath } = parseUrl(
|
||||
location.target,
|
||||
);
|
||||
this.logger.info(
|
||||
`Reading Azure DevOps repositories from ${location.target}`,
|
||||
);
|
||||
|
||||
const files = await codeSearch(
|
||||
azureConfig,
|
||||
org,
|
||||
project,
|
||||
repo,
|
||||
catalogPath,
|
||||
);
|
||||
|
||||
this.logger.debug(
|
||||
`Found ${files.length} files in Azure DevOps from ${location.target}.`,
|
||||
);
|
||||
|
||||
for (const file of files) {
|
||||
emit(
|
||||
results.location(
|
||||
{
|
||||
type: 'url',
|
||||
target: `${baseUrl}/${org}/${project}/_git/${file.repository.name}?path=${file.path}`,
|
||||
},
|
||||
// Not all locations may actually exist, since the user defined them as a wildcard pattern.
|
||||
// Thus, we emit them as optional and let the downstream processor find them while not outputting
|
||||
// an error if it couldn't.
|
||||
true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* parseUrl extracts segments from the Azure DevOps URL.
|
||||
**/
|
||||
export function parseUrl(urlString: string): {
|
||||
baseUrl: string;
|
||||
org: string;
|
||||
project: string;
|
||||
repo: string;
|
||||
catalogPath: string;
|
||||
} {
|
||||
const url = new URL(urlString);
|
||||
const path = url.pathname.substr(1).split('/');
|
||||
|
||||
const catalogPath = url.searchParams.get('path') || '/catalog-info.yaml';
|
||||
|
||||
if (path.length === 2 && path[0].length && path[1].length) {
|
||||
return {
|
||||
baseUrl: url.origin,
|
||||
org: decodeURIComponent(path[0]),
|
||||
project: decodeURIComponent(path[1]),
|
||||
repo: '',
|
||||
catalogPath,
|
||||
};
|
||||
} else if (
|
||||
path.length === 4 &&
|
||||
path[0].length &&
|
||||
path[1].length &&
|
||||
path[2].length &&
|
||||
path[3].length
|
||||
) {
|
||||
return {
|
||||
baseUrl: url.origin,
|
||||
org: decodeURIComponent(path[0]),
|
||||
project: decodeURIComponent(path[1]),
|
||||
repo: decodeURIComponent(path[3]),
|
||||
catalogPath,
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`Failed to parse ${urlString}`);
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { setupServer } from 'msw/node';
|
||||
import { setupRequestMockHandlers } from '@backstage/test-utils';
|
||||
import { rest } from 'msw';
|
||||
import { codeSearch, CodeSearchResponse } from './azure';
|
||||
|
||||
describe('azure', () => {
|
||||
const server = setupServer();
|
||||
setupRequestMockHandlers(server);
|
||||
|
||||
describe('codeSearch', () => {
|
||||
it('returns empty when nothing is found', async () => {
|
||||
const response: CodeSearchResponse = { count: 0, results: [] };
|
||||
|
||||
server.use(
|
||||
rest.post(
|
||||
`https://almsearch.dev.azure.com/shopify/engineering/_apis/search/codesearchresults`,
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('Authorization')).toBe('Basic OkFCQw==');
|
||||
expect(req.body).toEqual({
|
||||
searchText: 'path:/catalog-info.yaml repo:*',
|
||||
$skip: 0,
|
||||
$top: 1000,
|
||||
});
|
||||
return res(ctx.json(response));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await expect(
|
||||
codeSearch(
|
||||
{ host: 'dev.azure.com', token: 'ABC' },
|
||||
'shopify',
|
||||
'engineering',
|
||||
'',
|
||||
'/catalog-info.yaml',
|
||||
),
|
||||
).resolves.toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns entries when request matches some files', async () => {
|
||||
const response: CodeSearchResponse = {
|
||||
count: 2,
|
||||
results: [
|
||||
{
|
||||
fileName: 'catalog-info.yaml',
|
||||
path: '/catalog-info.yaml',
|
||||
repository: {
|
||||
name: 'backstage',
|
||||
},
|
||||
},
|
||||
{
|
||||
fileName: 'catalog-info.yaml',
|
||||
path: '/catalog-info.yaml',
|
||||
repository: {
|
||||
name: 'ios-app',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
server.use(
|
||||
rest.post(
|
||||
`https://almsearch.dev.azure.com/shopify/engineering/_apis/search/codesearchresults`,
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('Authorization')).toBe('Basic OkFCQw==');
|
||||
expect(req.body).toEqual({
|
||||
searchText: 'path:/catalog-info.yaml repo:*',
|
||||
$skip: 0,
|
||||
$top: 1000,
|
||||
});
|
||||
return res(ctx.json(response));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await expect(
|
||||
codeSearch(
|
||||
{ host: 'dev.azure.com', token: 'ABC' },
|
||||
'shopify',
|
||||
'engineering',
|
||||
'',
|
||||
'/catalog-info.yaml',
|
||||
),
|
||||
).resolves.toEqual(response.results);
|
||||
});
|
||||
|
||||
it('searches in specific repo if parameter is set', async () => {
|
||||
const response: CodeSearchResponse = {
|
||||
count: 1,
|
||||
results: [
|
||||
{
|
||||
fileName: 'catalog-info.yaml',
|
||||
path: '/catalog-info.yaml',
|
||||
repository: {
|
||||
name: 'backstage',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
server.use(
|
||||
rest.post(
|
||||
`https://almsearch.dev.azure.com/shopify/engineering/_apis/search/codesearchresults`,
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('Authorization')).toBe('Basic OkFCQw==');
|
||||
expect(req.body).toEqual({
|
||||
searchText: 'path:/catalog-info.yaml repo:backstage',
|
||||
$skip: 0,
|
||||
$top: 1000,
|
||||
});
|
||||
return res(ctx.json(response));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await expect(
|
||||
codeSearch(
|
||||
{ host: 'dev.azure.com', token: 'ABC' },
|
||||
'shopify',
|
||||
'engineering',
|
||||
'backstage',
|
||||
'/catalog-info.yaml',
|
||||
),
|
||||
).resolves.toEqual(response.results);
|
||||
});
|
||||
|
||||
it('can search using onpremise api', async () => {
|
||||
const response: CodeSearchResponse = {
|
||||
count: 1,
|
||||
results: [
|
||||
{
|
||||
fileName: 'catalog-info.yaml',
|
||||
path: '/catalog-info.yaml',
|
||||
repository: {
|
||||
name: 'backstage',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
server.use(
|
||||
rest.post(
|
||||
`https://azuredevops.mycompany.com/shopify/engineering/_apis/search/codesearchresults`,
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('Authorization')).toBe('Basic OkFCQw==');
|
||||
expect(req.body).toEqual({
|
||||
searchText: 'path:/catalog-info.yaml repo:*',
|
||||
$skip: 0,
|
||||
$top: 1000,
|
||||
});
|
||||
return res(ctx.json(response));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await expect(
|
||||
codeSearch(
|
||||
{ host: 'azuredevops.mycompany.com', token: 'ABC' },
|
||||
'shopify',
|
||||
'engineering',
|
||||
'',
|
||||
'/catalog-info.yaml',
|
||||
),
|
||||
).resolves.toEqual(response.results);
|
||||
});
|
||||
|
||||
it('searches multiple pages if response contains many items', async () => {
|
||||
const totalCount = 2401;
|
||||
const generateItems = (count: number) => {
|
||||
return Array.from(Array(count).keys()).map(_ => ({
|
||||
fileName: 'catalog-info.yaml',
|
||||
path: '/catalog-info.yaml',
|
||||
repository: {
|
||||
name: 'backstage',
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
server.use(
|
||||
rest.post(
|
||||
`https://almsearch.dev.azure.com/shopify/engineering/_apis/search/codesearchresults`,
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('Authorization')).toBe('Basic OkFCQw==');
|
||||
expect(req.body).toMatchObject({
|
||||
searchText: 'path:/catalog-info.yaml repo:backstage',
|
||||
$top: 1000,
|
||||
});
|
||||
|
||||
const body = req.body as { $skip: number; $top: number };
|
||||
const countItemsToReturn =
|
||||
body.$top + body.$skip > totalCount
|
||||
? totalCount - body.$skip
|
||||
: body.$top;
|
||||
|
||||
return res(
|
||||
ctx.json({
|
||||
count: totalCount,
|
||||
results: generateItems(countItemsToReturn),
|
||||
}),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await expect(
|
||||
codeSearch(
|
||||
{ host: 'dev.azure.com', token: 'ABC' },
|
||||
'shopify',
|
||||
'engineering',
|
||||
'backstage',
|
||||
'/catalog-info.yaml',
|
||||
),
|
||||
).resolves.toHaveLength(totalCount);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fetch from 'cross-fetch';
|
||||
import {
|
||||
AzureIntegrationConfig,
|
||||
getAzureRequestOptions,
|
||||
} from '@backstage/integration';
|
||||
|
||||
export interface CodeSearchResponse {
|
||||
count: number;
|
||||
results: CodeSearchResultItem[];
|
||||
}
|
||||
|
||||
export interface CodeSearchResultItem {
|
||||
fileName: string;
|
||||
path: string;
|
||||
repository: {
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
|
||||
const isCloud = (host: string) => host === 'dev.azure.com';
|
||||
const PAGE_SIZE = 1000;
|
||||
|
||||
// codeSearch returns all files that matches the given search path.
|
||||
export async function codeSearch(
|
||||
azureConfig: AzureIntegrationConfig,
|
||||
org: string,
|
||||
project: string,
|
||||
repo: string,
|
||||
path: string,
|
||||
): Promise<CodeSearchResultItem[]> {
|
||||
const searchBaseUrl = isCloud(azureConfig.host)
|
||||
? 'https://almsearch.dev.azure.com'
|
||||
: `https://${azureConfig.host}`;
|
||||
const searchUrl = `${searchBaseUrl}/${org}/${project}/_apis/search/codesearchresults?api-version=6.0-preview.1`;
|
||||
|
||||
let items: CodeSearchResultItem[] = [];
|
||||
let hasMorePages = true;
|
||||
|
||||
do {
|
||||
const response = await fetch(searchUrl, {
|
||||
...getAzureRequestOptions(azureConfig, {
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
searchText: `path:${path} repo:${repo || '*'}`,
|
||||
$skip: items.length,
|
||||
$top: PAGE_SIZE,
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(
|
||||
`Azure DevOps search failed with response status ${response.status}`,
|
||||
);
|
||||
}
|
||||
|
||||
const body: CodeSearchResponse = await response.json();
|
||||
items = [...items, ...body.results];
|
||||
hasMorePages = body.count > items.length;
|
||||
} while (hasMorePages);
|
||||
|
||||
return items;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './azure';
|
||||
@@ -26,6 +26,7 @@ export { BuiltinKindsEntityProcessor } from './BuiltinKindsEntityProcessor';
|
||||
export { CodeOwnersProcessor } from './CodeOwnersProcessor';
|
||||
export { FileReaderProcessor } from './FileReaderProcessor';
|
||||
export { GithubDiscoveryProcessor } from './GithubDiscoveryProcessor';
|
||||
export { AzureDevOpsDiscoveryProcessor } from './AzureDevOpsDiscoveryProcessor';
|
||||
export { GithubOrgReaderProcessor } from './GithubOrgReaderProcessor';
|
||||
export { GithubMultiOrgReaderProcessor } from './GithubMultiOrgReaderProcessor';
|
||||
export { GitLabDiscoveryProcessor } from './GitLabDiscoveryProcessor';
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
CodeOwnersProcessor,
|
||||
FileReaderProcessor,
|
||||
GithubDiscoveryProcessor,
|
||||
AzureDevOpsDiscoveryProcessor,
|
||||
GithubOrgReaderProcessor,
|
||||
GitLabDiscoveryProcessor,
|
||||
LocationEntityProcessor,
|
||||
@@ -317,6 +318,7 @@ export class CatalogBuilder {
|
||||
new FileReaderProcessor(),
|
||||
BitbucketDiscoveryProcessor.fromConfig(config, { logger }),
|
||||
GithubDiscoveryProcessor.fromConfig(config, { logger }),
|
||||
AzureDevOpsDiscoveryProcessor.fromConfig(config, { logger }),
|
||||
GithubOrgReaderProcessor.fromConfig(config, { logger }),
|
||||
GitLabDiscoveryProcessor.fromConfig(config, { logger }),
|
||||
new UrlReaderProcessor({ reader, logger }),
|
||||
|
||||
@@ -148,6 +148,57 @@ describe('DefaultLocationServiceTest', () => {
|
||||
expect(result.exists).toBe(true);
|
||||
});
|
||||
|
||||
it('should fail when there are duplicate entities using dry run', async () => {
|
||||
store.listLocations.mockResolvedValueOnce([]);
|
||||
orchestrator.process.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
state: {},
|
||||
completedEntity: {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Location',
|
||||
metadata: {
|
||||
name: 'foo',
|
||||
},
|
||||
},
|
||||
deferredEntities: [
|
||||
{
|
||||
entity: {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Location',
|
||||
metadata: {
|
||||
name: 'foo',
|
||||
},
|
||||
},
|
||||
locationKey: 'file:///tmp/mock.yaml',
|
||||
},
|
||||
],
|
||||
relations: [],
|
||||
errors: [],
|
||||
});
|
||||
|
||||
orchestrator.process.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
state: {},
|
||||
completedEntity: {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Location',
|
||||
metadata: {
|
||||
name: 'foo',
|
||||
},
|
||||
},
|
||||
deferredEntities: [],
|
||||
relations: [],
|
||||
errors: [],
|
||||
});
|
||||
|
||||
await expect(
|
||||
locationService.createLocation(
|
||||
{ type: 'url', target: 'https://backstage.io/catalog-info.yaml' },
|
||||
true,
|
||||
),
|
||||
).rejects.toThrowError('Duplicate nested entity: location:default/foo');
|
||||
});
|
||||
|
||||
it('should return exists false when the location does not exist beforehand', async () => {
|
||||
orchestrator.process.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
LocationSpec,
|
||||
LOCATION_ANNOTATION,
|
||||
ORIGIN_LOCATION_ANNOTATION,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import {
|
||||
CatalogProcessingOrchestrator,
|
||||
@@ -54,6 +55,43 @@ export class DefaultLocationService implements LocationService {
|
||||
return this.store.deleteLocation(id);
|
||||
}
|
||||
|
||||
private async processEntities(
|
||||
unprocessedEntities: DeferredEntity[],
|
||||
): Promise<Entity[]> {
|
||||
const entities: Entity[] = [];
|
||||
while (unprocessedEntities.length) {
|
||||
const currentEntity = unprocessedEntities.pop();
|
||||
if (!currentEntity) {
|
||||
continue;
|
||||
}
|
||||
const processed = await this.orchestrator.process({
|
||||
entity: currentEntity.entity,
|
||||
state: {}, // we process without the existing cache
|
||||
});
|
||||
|
||||
if (processed.ok) {
|
||||
if (
|
||||
entities.some(
|
||||
e =>
|
||||
stringifyEntityRef(e) ===
|
||||
stringifyEntityRef(processed.completedEntity),
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
`Duplicate nested entity: ${stringifyEntityRef(
|
||||
processed.completedEntity,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
unprocessedEntities.push(...processed.deferredEntities);
|
||||
entities.push(processed.completedEntity);
|
||||
} else {
|
||||
throw Error(processed.errors.map(String).join(', '));
|
||||
}
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
private async dryRunCreateLocation(
|
||||
spec: LocationSpec,
|
||||
): Promise<{ location: Location; entities: Entity[]; exists?: boolean }> {
|
||||
@@ -86,24 +124,7 @@ export class DefaultLocationService implements LocationService {
|
||||
const unprocessedEntities: DeferredEntity[] = [
|
||||
{ entity, locationKey: `${spec.type}:${spec.target}` },
|
||||
];
|
||||
const entities: Entity[] = [];
|
||||
while (unprocessedEntities.length) {
|
||||
const currentEntity = unprocessedEntities.pop();
|
||||
if (!currentEntity) {
|
||||
continue;
|
||||
}
|
||||
const processed = await this.orchestrator.process({
|
||||
entity: currentEntity.entity,
|
||||
state: {}, // we process without the existing cache
|
||||
});
|
||||
|
||||
if (processed.ok) {
|
||||
unprocessedEntities.push(...processed.deferredEntities);
|
||||
entities.push(processed.completedEntity);
|
||||
} else {
|
||||
throw Error(processed.errors.map(String).join(', '));
|
||||
}
|
||||
}
|
||||
const entities: Entity[] = await this.processEntities(unprocessedEntities);
|
||||
|
||||
return {
|
||||
exists: await existsPromise,
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
CatalogProcessorParser,
|
||||
CodeOwnersProcessor,
|
||||
FileReaderProcessor,
|
||||
AzureDevOpsDiscoveryProcessor,
|
||||
GithubDiscoveryProcessor,
|
||||
GithubOrgReaderProcessor,
|
||||
GitLabDiscoveryProcessor,
|
||||
@@ -292,6 +293,7 @@ export class NextCatalogBuilder {
|
||||
return [
|
||||
new FileReaderProcessor(),
|
||||
BitbucketDiscoveryProcessor.fromConfig(config, { logger }),
|
||||
AzureDevOpsDiscoveryProcessor.fromConfig(config, { logger }),
|
||||
GithubDiscoveryProcessor.fromConfig(config, { logger }),
|
||||
GithubOrgReaderProcessor.fromConfig(config, { logger }),
|
||||
GitLabDiscoveryProcessor.fromConfig(config, { logger }),
|
||||
|
||||
@@ -435,5 +435,37 @@ describe('NextEntitiesCatalog', () => {
|
||||
expect(entities).toContainEqual(entity1);
|
||||
},
|
||||
);
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'should return no matches for an empty values array',
|
||||
// NOTE: An empty values array is not a sensible input in a realistic scenario.
|
||||
async databaseId => {
|
||||
const { knex } = await createDatabase(databaseId);
|
||||
const entity1: Entity = {
|
||||
apiVersion: 'a',
|
||||
kind: 'k',
|
||||
metadata: { name: 'one' },
|
||||
spec: {},
|
||||
};
|
||||
const entity2: Entity = {
|
||||
apiVersion: 'a',
|
||||
kind: 'k',
|
||||
metadata: { name: 'two' },
|
||||
spec: {},
|
||||
};
|
||||
await addEntityToSearch(knex, entity1);
|
||||
await addEntityToSearch(knex, entity2);
|
||||
const catalog = new NextEntitiesCatalog(knex);
|
||||
|
||||
const testFilter = {
|
||||
key: 'kind',
|
||||
values: [],
|
||||
};
|
||||
const request = { filter: testFilter };
|
||||
const { entities } = await catalog.entities(request);
|
||||
|
||||
expect(entities.length).toBe(0);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,7 +91,7 @@ function addCondition(
|
||||
if (filter.values) {
|
||||
if (filter.values.length === 1) {
|
||||
this.where({ value: filter.values[0].toLowerCase() });
|
||||
} else if (filter.values.length > 1) {
|
||||
} else {
|
||||
this.andWhere(
|
||||
'value',
|
||||
'in',
|
||||
|
||||
@@ -14,14 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import { analyticsApiRef } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
CatalogApi,
|
||||
catalogApiRef,
|
||||
EntityProvider,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { MockAnalyticsApi, renderInTestApp } from '@backstage/test-utils';
|
||||
import {
|
||||
MockAnalyticsApi,
|
||||
renderInTestApp,
|
||||
TestApiProvider,
|
||||
TestApiRegistry,
|
||||
} from '@backstage/test-utils';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { catalogEntityRouteRef, catalogGraphRouteRef } from '../../routes';
|
||||
@@ -31,7 +36,7 @@ describe('<CatalogGraphCard/>', () => {
|
||||
let entity: Entity;
|
||||
let wrapper: JSX.Element;
|
||||
let catalog: jest.Mocked<CatalogApi>;
|
||||
let apis: ApiRegistry;
|
||||
let apis: TestApiRegistry;
|
||||
|
||||
beforeAll(() => {
|
||||
Object.defineProperty(window.SVGElement.prototype, 'getBBox', {
|
||||
@@ -61,7 +66,7 @@ describe('<CatalogGraphCard/>', () => {
|
||||
refreshEntity: jest.fn(),
|
||||
getEntityAncestors: jest.fn(),
|
||||
};
|
||||
apis = ApiRegistry.with(catalogApiRef, catalog);
|
||||
apis = TestApiRegistry.from([catalogApiRef, catalog]);
|
||||
|
||||
wrapper = (
|
||||
<ApiProvider apis={apis}>
|
||||
@@ -123,9 +128,9 @@ describe('<CatalogGraphCard/>', () => {
|
||||
test('captures analytics event on click', async () => {
|
||||
const analyticsSpy = new MockAnalyticsApi();
|
||||
const { findByText } = await renderInTestApp(
|
||||
<ApiProvider apis={ApiRegistry.from([[analyticsApiRef, analyticsSpy]])}>
|
||||
<TestApiProvider apis={[[analyticsApiRef, analyticsSpy]]}>
|
||||
{wrapper}
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/entity/{kind}/{namespace}/{name}': catalogEntityRouteRef,
|
||||
|
||||
@@ -14,10 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { RELATION_HAS_PART, RELATION_PART_OF } from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { analyticsApiRef } from '@backstage/core-plugin-api';
|
||||
import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { MockAnalyticsApi, renderInTestApp } from '@backstage/test-utils';
|
||||
import {
|
||||
MockAnalyticsApi,
|
||||
renderInTestApp,
|
||||
TestApiProvider,
|
||||
} from '@backstage/test-utils';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { catalogEntityRouteRef } from '../../routes';
|
||||
@@ -90,10 +93,9 @@ describe('<CatalogGraphPage/>', () => {
|
||||
refreshEntity: jest.fn(),
|
||||
getEntityAncestors: jest.fn(),
|
||||
};
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalog);
|
||||
|
||||
wrapper = (
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider apis={[[catalogApiRef, catalog]]}>
|
||||
<CatalogGraphPage
|
||||
initialState={{
|
||||
showFilters: false,
|
||||
@@ -101,7 +103,7 @@ describe('<CatalogGraphPage/>', () => {
|
||||
selectedKinds: ['b'],
|
||||
}}
|
||||
/>
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -172,9 +174,9 @@ describe('<CatalogGraphPage/>', () => {
|
||||
test('should capture analytics event when selecting other entity', async () => {
|
||||
const analyticsSpy = new MockAnalyticsApi();
|
||||
const { getByText, findAllByTestId } = await renderInTestApp(
|
||||
<ApiProvider apis={ApiRegistry.from([[analyticsApiRef, analyticsSpy]])}>
|
||||
<TestApiProvider apis={[[analyticsApiRef, analyticsSpy]]}>
|
||||
{wrapper}
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/entity/{kind}/{namespace}/{name}': catalogEntityRouteRef,
|
||||
@@ -195,9 +197,9 @@ describe('<CatalogGraphPage/>', () => {
|
||||
test('should capture analytics event when navigating to entity', async () => {
|
||||
const analyticsSpy = new MockAnalyticsApi();
|
||||
const { getByText, findAllByTestId } = await renderInTestApp(
|
||||
<ApiProvider apis={ApiRegistry.from([[analyticsApiRef, analyticsSpy]])}>
|
||||
<TestApiProvider apis={[[analyticsApiRef, analyticsSpy]]}>
|
||||
{wrapper}
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/entity/{kind}/{namespace}/{name}': catalogEntityRouteRef,
|
||||
|
||||
+4
-4
@@ -21,9 +21,8 @@ import {
|
||||
RELATION_PART_OF,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { EntityRelationsGraph } from './EntityRelationsGraph';
|
||||
@@ -158,10 +157,11 @@ describe('<EntityRelationsGraph/>', () => {
|
||||
refreshEntity: jest.fn(),
|
||||
getEntityAncestors: jest.fn(),
|
||||
};
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalog);
|
||||
|
||||
Wrapper = ({ children }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider apis={[[catalogApiRef, catalog]]}>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -15,14 +15,10 @@
|
||||
*/
|
||||
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
import {
|
||||
ApiProvider,
|
||||
ApiRegistry,
|
||||
ConfigReader,
|
||||
} from '@backstage/core-app-api';
|
||||
import { ApiProvider, ConfigReader } from '@backstage/core-app-api';
|
||||
import { configApiRef } from '@backstage/core-plugin-api';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
import { catalogImportApiRef, CatalogImportClient } from '../../api';
|
||||
import { DefaultImportPage } from './DefaultImportPage';
|
||||
@@ -43,15 +39,13 @@ describe('<DefaultImportPage />', () => {
|
||||
},
|
||||
};
|
||||
|
||||
let apis: ApiRegistry;
|
||||
let apis: TestApiRegistry;
|
||||
|
||||
beforeEach(() => {
|
||||
apis = ApiRegistry.with(
|
||||
configApiRef,
|
||||
new ConfigReader({ integrations: {} }),
|
||||
)
|
||||
.with(catalogApiRef, new CatalogClient({ discoveryApi: {} as any }))
|
||||
.with(
|
||||
apis = TestApiRegistry.from(
|
||||
[configApiRef, new ConfigReader({ integrations: {} })],
|
||||
[catalogApiRef, new CatalogClient({ discoveryApi: {} as any })],
|
||||
[
|
||||
catalogImportApiRef,
|
||||
new CatalogImportClient({
|
||||
discoveryApi: {} as any,
|
||||
@@ -63,7 +57,8 @@ describe('<DefaultImportPage />', () => {
|
||||
catalogApi: {} as any,
|
||||
configApi: {} as any,
|
||||
}),
|
||||
);
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
it('renders without exploding', async () => {
|
||||
|
||||
@@ -14,19 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
ApiProvider,
|
||||
ApiRegistry,
|
||||
ConfigReader,
|
||||
} from '@backstage/core-app-api';
|
||||
import { ApiProvider, ConfigReader } from '@backstage/core-app-api';
|
||||
import { configApiRef } from '@backstage/core-plugin-api';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import {
|
||||
renderInTestApp,
|
||||
TestApiProvider,
|
||||
TestApiRegistry,
|
||||
} from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
import { CatalogImportApi, catalogImportApiRef } from '../../api';
|
||||
import { ImportInfoCard } from './ImportInfoCard';
|
||||
|
||||
describe('<ImportInfoCard />', () => {
|
||||
let apis: ApiRegistry;
|
||||
let apis: TestApiRegistry;
|
||||
let catalogImportApi: jest.Mocked<CatalogImportApi>;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -35,26 +35,29 @@ describe('<ImportInfoCard />', () => {
|
||||
submitPullRequest: jest.fn(),
|
||||
};
|
||||
|
||||
apis = ApiRegistry.with(
|
||||
configApiRef,
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [{ token: 'my-token' }],
|
||||
},
|
||||
}),
|
||||
).with(catalogImportApiRef, catalogImportApi);
|
||||
apis = TestApiRegistry.from(
|
||||
[
|
||||
configApiRef,
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [{ token: 'my-token' }],
|
||||
},
|
||||
}),
|
||||
],
|
||||
[catalogImportApiRef, catalogImportApi],
|
||||
);
|
||||
});
|
||||
|
||||
it('renders without exploding', async () => {
|
||||
apis = ApiRegistry.with(
|
||||
configApiRef,
|
||||
new ConfigReader({ integrations: {} }),
|
||||
).with(catalogImportApiRef, catalogImportApi);
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[configApiRef, new ConfigReader({ integrations: {} })],
|
||||
[catalogImportApiRef, catalogImportApi],
|
||||
]}
|
||||
>
|
||||
<ImportInfoCard />
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(getByText('Register an existing component')).toBeInTheDocument();
|
||||
|
||||
@@ -15,14 +15,10 @@
|
||||
*/
|
||||
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
import {
|
||||
ApiProvider,
|
||||
ApiRegistry,
|
||||
ConfigReader,
|
||||
} from '@backstage/core-app-api';
|
||||
import { ApiProvider, ConfigReader } from '@backstage/core-app-api';
|
||||
import { configApiRef } from '@backstage/core-plugin-api';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
import { useOutlet } from 'react-router';
|
||||
import { catalogImportApiRef, CatalogImportClient } from '../../api';
|
||||
@@ -49,15 +45,13 @@ describe('<ImportPage />', () => {
|
||||
},
|
||||
};
|
||||
|
||||
let apis: ApiRegistry;
|
||||
let apis: TestApiRegistry;
|
||||
|
||||
beforeEach(() => {
|
||||
apis = ApiRegistry.with(
|
||||
configApiRef,
|
||||
new ConfigReader({ integrations: {} }),
|
||||
)
|
||||
.with(catalogApiRef, new CatalogClient({ discoveryApi: {} as any }))
|
||||
.with(
|
||||
apis = TestApiRegistry.from(
|
||||
[configApiRef, new ConfigReader({ integrations: {} })],
|
||||
[catalogApiRef, new CatalogClient({ discoveryApi: {} as any })],
|
||||
[
|
||||
catalogImportApiRef,
|
||||
new CatalogImportClient({
|
||||
discoveryApi: {} as any,
|
||||
@@ -67,7 +61,8 @@ describe('<ImportPage />', () => {
|
||||
catalogApi: {} as any,
|
||||
configApi: new ConfigReader({}),
|
||||
}),
|
||||
);
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => jest.resetAllMocks());
|
||||
|
||||
+7
-7
@@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { errorApiRef } from '@backstage/core-plugin-api';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { act, render } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
@@ -34,14 +34,14 @@ describe('<StepInitAnalyzeUrl />', () => {
|
||||
};
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.with(catalogImportApiRef, catalogImportApi).with(
|
||||
errorApiRef,
|
||||
errorApi,
|
||||
)}
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[catalogImportApiRef, catalogImportApi],
|
||||
[errorApiRef, errorApi],
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
);
|
||||
|
||||
const location = {
|
||||
|
||||
+8
-6
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { errorApiRef } from '@backstage/core-plugin-api';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { TextField } from '@material-ui/core';
|
||||
import { act, render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
@@ -54,13 +54,15 @@ describe('<StepPrepareCreatePullRequest />', () => {
|
||||
};
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.with(catalogImportApiRef, catalogImportApi)
|
||||
.with(catalogApiRef, catalogApi)
|
||||
.with(errorApiRef, errorApi)}
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[catalogImportApiRef, catalogImportApi],
|
||||
[catalogApiRef, catalogApi],
|
||||
[errorApiRef, errorApi],
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
);
|
||||
|
||||
const onPrepareFn = jest.fn();
|
||||
|
||||
@@ -17,16 +17,15 @@
|
||||
import React from 'react';
|
||||
import { fireEvent, waitFor } from '@testing-library/react';
|
||||
import { capitalize } from 'lodash';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntityTypePicker } from './EntityTypePicker';
|
||||
import { MockEntityListContextProvider } from '../../testUtils/providers';
|
||||
import { catalogApiRef } from '../../api';
|
||||
import { EntityKindFilter, EntityTypeFilter } from '../../filters';
|
||||
|
||||
import { AlertApi, alertApiRef } from '@backstage/core-plugin-api';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { renderWithEffects } from '@backstage/test-utils';
|
||||
import { alertApiRef } from '@backstage/core-plugin-api';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils';
|
||||
|
||||
const entities: Entity[] = [
|
||||
{
|
||||
@@ -61,11 +60,20 @@ const entities: Entity[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const apis = ApiRegistry.with(catalogApiRef, {
|
||||
getEntities: jest.fn().mockResolvedValue({ items: entities }),
|
||||
} as unknown as CatalogApi).with(alertApiRef, {
|
||||
post: jest.fn(),
|
||||
} as unknown as AlertApi);
|
||||
const apis = TestApiRegistry.from(
|
||||
[
|
||||
catalogApiRef,
|
||||
{
|
||||
getEntities: jest.fn().mockResolvedValue({ items: entities }),
|
||||
},
|
||||
],
|
||||
[
|
||||
alertApiRef,
|
||||
{
|
||||
post: jest.fn(),
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
describe('<EntityTypePicker/>', () => {
|
||||
it('renders available entity types', async () => {
|
||||
|
||||
+9
-8
@@ -24,7 +24,7 @@ import { CatalogClient } from '@backstage/catalog-client';
|
||||
import { catalogApiRef } from '../../api';
|
||||
import { entityRouteRef } from '../../routes';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import * as state from './useUnregisterEntityDialogState';
|
||||
|
||||
import {
|
||||
@@ -32,7 +32,6 @@ import {
|
||||
alertApiRef,
|
||||
DiscoveryApi,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('UnregisterEntityDialog', () => {
|
||||
const discoveryApi: DiscoveryApi = {
|
||||
@@ -49,11 +48,6 @@ describe('UnregisterEntityDialog', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const apis = ApiRegistry.with(
|
||||
catalogApiRef,
|
||||
new CatalogClient({ discoveryApi }),
|
||||
).with(alertApiRef, alertApi);
|
||||
|
||||
const entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
@@ -68,7 +62,14 @@ describe('UnregisterEntityDialog', () => {
|
||||
};
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[catalogApiRef, new CatalogClient({ discoveryApi })],
|
||||
[alertApiRef, alertApi],
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
|
||||
const stateSpy = jest.spyOn(state, 'useUnregisterEntityDialogState');
|
||||
|
||||
+3
-3
@@ -31,7 +31,7 @@ import {
|
||||
UseUnregisterEntityDialogState,
|
||||
useUnregisterEntityDialogState,
|
||||
} from './useUnregisterEntityDialogState';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
|
||||
function defer<T>(): { promise: Promise<T>; resolve: (value: T) => void } {
|
||||
let resolve: (value: T) => void = () => {};
|
||||
@@ -51,9 +51,9 @@ describe('useUnregisterEntityDialogState', () => {
|
||||
const catalogApi = catalogApiMock as Partial<CatalogApi> as CatalogApi;
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={ApiRegistry.with(catalogApiRef, catalogApi)}>
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
);
|
||||
|
||||
let entity: Entity;
|
||||
|
||||
@@ -26,9 +26,9 @@ import { MockEntityListContextProvider } from '../../testUtils/providers';
|
||||
import { EntityTagFilter, UserListFilter } from '../../filters';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { catalogApiRef } from '../../api';
|
||||
import { MockStorageApi } from '@backstage/test-utils';
|
||||
import { MockStorageApi, TestApiRegistry } from '@backstage/test-utils';
|
||||
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import {
|
||||
ConfigApi,
|
||||
configApiRef,
|
||||
@@ -62,12 +62,12 @@ const mockIdentityApi = {
|
||||
getIdToken: async () => undefined,
|
||||
} as Partial<IdentityApi>;
|
||||
|
||||
const apis = ApiRegistry.from([
|
||||
const apis = TestApiRegistry.from(
|
||||
[configApiRef, mockConfigApi],
|
||||
[catalogApiRef, mockCatalogApi],
|
||||
[identityApiRef, mockIdentityApi],
|
||||
[storageApiRef, MockStorageApi.create()],
|
||||
]);
|
||||
);
|
||||
|
||||
const mockIsOwnedEntity = (entity: Entity) =>
|
||||
entity.metadata.name === 'component-1';
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
*/
|
||||
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { catalogApiRef } from '../api';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { useEntityKinds } from './useEntityKinds';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
|
||||
const entities: Entity[] = [
|
||||
{
|
||||
@@ -59,9 +59,9 @@ const mockCatalogApi: Partial<CatalogApi> = {
|
||||
|
||||
const wrapper = ({ children }: PropsWithChildren<{}>) => {
|
||||
return (
|
||||
<ApiProvider apis={ApiRegistry.with(catalogApiRef, mockCatalogApi)}>
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApi]]}>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import {
|
||||
ConfigApi,
|
||||
configApiRef,
|
||||
@@ -24,7 +23,7 @@ import {
|
||||
identityApiRef,
|
||||
storageApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { MockStorageApi } from '@backstage/test-utils';
|
||||
import { MockStorageApi, TestApiProvider } from '@backstage/test-utils';
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import qs from 'qs';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
@@ -76,16 +75,6 @@ const mockCatalogApi: Partial<CatalogApi> = {
|
||||
getEntities: jest.fn().mockImplementation(async () => ({ items: entities })),
|
||||
getEntityByName: async () => undefined,
|
||||
};
|
||||
const apis = ApiRegistry.from([
|
||||
[configApiRef, mockConfigApi],
|
||||
[catalogApiRef, mockCatalogApi],
|
||||
[identityApiRef, mockIdentityApi],
|
||||
[storageApiRef, MockStorageApi.create()],
|
||||
[
|
||||
starredEntitiesApiRef,
|
||||
new DefaultStarredEntitiesApi({ storageApi: MockStorageApi.create() }),
|
||||
],
|
||||
]);
|
||||
|
||||
const wrapper = ({
|
||||
userFilter,
|
||||
@@ -94,13 +83,26 @@ const wrapper = ({
|
||||
userFilter?: UserListFilterKind;
|
||||
}>) => {
|
||||
return (
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[configApiRef, mockConfigApi],
|
||||
[catalogApiRef, mockCatalogApi],
|
||||
[identityApiRef, mockIdentityApi],
|
||||
[storageApiRef, MockStorageApi.create()],
|
||||
[
|
||||
starredEntitiesApiRef,
|
||||
new DefaultStarredEntitiesApi({
|
||||
storageApi: MockStorageApi.create(),
|
||||
}),
|
||||
],
|
||||
]}
|
||||
>
|
||||
<EntityListProvider>
|
||||
<EntityKindPicker initialFilter="component" hidden />
|
||||
<UserListPicker initialFilter={userFilter} />
|
||||
{children}
|
||||
</EntityListProvider>
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ import {
|
||||
RELATION_OWNED_BY,
|
||||
UserEntity,
|
||||
} from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { IdentityApi, identityApiRef } from '@backstage/core-plugin-api';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import React from 'react';
|
||||
import { catalogApiRef } from '../api';
|
||||
@@ -50,14 +50,14 @@ describe('useEntityOwnership', () => {
|
||||
const catalogApi = mockCatalogApi as unknown as CatalogApi;
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.with(identityApiRef, identityApi).with(
|
||||
catalogApiRef,
|
||||
catalogApi,
|
||||
)}
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[identityApiRef, identityApi],
|
||||
[catalogApiRef, catalogApi],
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
);
|
||||
|
||||
const ownedEntity: ComponentEntity = {
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { StorageApi } from '@backstage/core-plugin-api';
|
||||
import { MockStorageApi } from '@backstage/test-utils';
|
||||
import { MockStorageApi, TestApiProvider } from '@backstage/test-utils';
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import { DefaultStarredEntitiesApi, starredEntitiesApiRef } from '../apis';
|
||||
@@ -47,14 +46,16 @@ describe('useStarredEntities', () => {
|
||||
beforeEach(() => {
|
||||
mockStorage = MockStorageApi.create();
|
||||
wrapper = ({ children }: PropsWithChildren<{}>) => (
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.with(
|
||||
starredEntitiesApiRef,
|
||||
new DefaultStarredEntitiesApi({ storageApi: mockStorage }),
|
||||
)}
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
starredEntitiesApiRef,
|
||||
new DefaultStarredEntitiesApi({ storageApi: mockStorage }),
|
||||
],
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { Entity, EntityName } from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import Observable from 'zen-observable';
|
||||
@@ -31,11 +31,9 @@ describe('useStarredEntity', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = ({ children }: PropsWithChildren<{}>) => (
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.with(starredEntitiesApiRef, mockStarredEntitiesApi)}
|
||||
>
|
||||
<TestApiProvider apis={[[starredEntitiesApiRef, mockStarredEntitiesApi]]}>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -15,11 +15,7 @@
|
||||
*/
|
||||
|
||||
import { RELATION_OWNED_BY } from '@backstage/catalog-model';
|
||||
import {
|
||||
ApiProvider,
|
||||
ApiRegistry,
|
||||
ConfigReader,
|
||||
} from '@backstage/core-app-api';
|
||||
import { ConfigReader } from '@backstage/core-app-api';
|
||||
import {
|
||||
ScmIntegrationsApi,
|
||||
scmIntegrationsApiRef,
|
||||
@@ -30,7 +26,7 @@ import {
|
||||
CatalogApi,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { viewTechDocRouteRef } from '../../routes';
|
||||
@@ -71,21 +67,25 @@ describe('<AboutCard />', () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
const apis = ApiRegistry.with(
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {},
|
||||
}),
|
||||
),
|
||||
).with(catalogApiRef, catalogApi);
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {},
|
||||
}),
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
<AboutCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
@@ -116,28 +116,32 @@ describe('<AboutCard />', () => {
|
||||
lifecycle: 'production',
|
||||
},
|
||||
};
|
||||
const apis = ApiRegistry.with(
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{
|
||||
host: 'github.com',
|
||||
token: '...',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
).with(catalogApiRef, catalogApi);
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{
|
||||
host: 'github.com',
|
||||
token: '...',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
<AboutCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
@@ -167,28 +171,32 @@ describe('<AboutCard />', () => {
|
||||
lifecycle: 'production',
|
||||
},
|
||||
};
|
||||
const apis = ApiRegistry.with(
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{
|
||||
host: 'github.com',
|
||||
token: '...',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
).with(catalogApiRef, catalogApi);
|
||||
|
||||
const { getByTitle } = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{
|
||||
host: 'github.com',
|
||||
token: '...',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
<AboutCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
@@ -216,17 +224,21 @@ describe('<AboutCard />', () => {
|
||||
lifecycle: 'production',
|
||||
},
|
||||
};
|
||||
const apis = ApiRegistry.with(
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(new ConfigReader({})),
|
||||
).with(catalogApiRef, catalogApi);
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(new ConfigReader({})),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
<AboutCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
@@ -253,17 +265,21 @@ describe('<AboutCard />', () => {
|
||||
lifecycle: 'production',
|
||||
},
|
||||
};
|
||||
const apis = ApiRegistry.with(
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(new ConfigReader({})),
|
||||
).with(catalogApiRef, catalogApi);
|
||||
|
||||
const { getByTitle } = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(new ConfigReader({})),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
<AboutCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
@@ -295,17 +311,21 @@ describe('<AboutCard />', () => {
|
||||
lifecycle: 'production',
|
||||
},
|
||||
};
|
||||
const apis = ApiRegistry.with(
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(new ConfigReader({})),
|
||||
).with(catalogApiRef, catalogApi);
|
||||
|
||||
const { queryByTitle } = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(new ConfigReader({})),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
<AboutCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
@@ -332,28 +352,32 @@ describe('<AboutCard />', () => {
|
||||
lifecycle: 'production',
|
||||
},
|
||||
};
|
||||
const apis = ApiRegistry.with(
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{
|
||||
host: 'github.com',
|
||||
token: '...',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
).with(catalogApiRef, catalogApi);
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{
|
||||
host: 'github.com',
|
||||
token: '...',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
<AboutCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/docs/:namespace/:kind/:name': viewTechDocRouteRef,
|
||||
@@ -381,28 +405,32 @@ describe('<AboutCard />', () => {
|
||||
lifecycle: 'production',
|
||||
},
|
||||
};
|
||||
const apis = ApiRegistry.with(
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{
|
||||
host: 'github.com',
|
||||
token: '...',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
).with(catalogApiRef, catalogApi);
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{
|
||||
host: 'github.com',
|
||||
token: '...',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
<AboutCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
@@ -429,28 +457,32 @@ describe('<AboutCard />', () => {
|
||||
lifecycle: 'production',
|
||||
},
|
||||
};
|
||||
const apis = ApiRegistry.with(
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{
|
||||
host: 'github.com',
|
||||
token: '...',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
).with(catalogApiRef, catalogApi);
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{
|
||||
host: 'github.com',
|
||||
token: '...',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
],
|
||||
[catalogApiRef, catalogApi],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
<AboutCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
|
||||
@@ -18,13 +18,12 @@ import React from 'react';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import {
|
||||
CatalogApi,
|
||||
catalogApiRef,
|
||||
EntityKindFilter,
|
||||
MockEntityListContextProvider,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { renderWithEffects } from '@backstage/test-utils';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils';
|
||||
import { CatalogKindHeader } from './CatalogKindHeader';
|
||||
|
||||
const entities: Entity[] = [
|
||||
@@ -58,9 +57,12 @@ const entities: Entity[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const apis = ApiRegistry.with(catalogApiRef, {
|
||||
getEntities: jest.fn().mockResolvedValue({ items: entities }),
|
||||
} as Partial<CatalogApi>);
|
||||
const apis = TestApiRegistry.from([
|
||||
catalogApiRef,
|
||||
{
|
||||
getEntities: jest.fn().mockResolvedValue({ items: entities }),
|
||||
},
|
||||
]);
|
||||
|
||||
describe('<CatalogKindHeader />', () => {
|
||||
it('renders available kinds', async () => {
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
RELATION_MEMBER_OF,
|
||||
RELATION_OWNED_BY,
|
||||
} from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { TableColumn, TableProps } from '@backstage/core-components';
|
||||
import {
|
||||
IdentityApi,
|
||||
@@ -38,6 +37,7 @@ import {
|
||||
mockBreakpoint,
|
||||
MockStorageApi,
|
||||
renderWithEffects,
|
||||
TestApiProvider,
|
||||
wrapInTestApp,
|
||||
} from '@backstage/test-utils';
|
||||
import DashboardIcon from '@material-ui/icons/Dashboard';
|
||||
@@ -129,8 +129,8 @@ describe('CatalogPage', () => {
|
||||
const renderWrapped = (children: React.ReactNode) =>
|
||||
renderWithEffects(
|
||||
wrapInTestApp(
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.from([
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[catalogApiRef, catalogApi],
|
||||
[identityApiRef, identityApi],
|
||||
[storageApiRef, storageApi],
|
||||
@@ -138,10 +138,10 @@ describe('CatalogPage', () => {
|
||||
starredEntitiesApiRef,
|
||||
new DefaultStarredEntitiesApi({ storageApi }),
|
||||
],
|
||||
])}
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/create': createComponentRouteRef,
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
Entity,
|
||||
VIEW_URL_ANNOTATION,
|
||||
} from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import {
|
||||
entityRouteRef,
|
||||
DefaultStarredEntitiesApi,
|
||||
@@ -27,7 +27,11 @@ import {
|
||||
starredEntitiesApiRef,
|
||||
UserListFilter,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { MockStorageApi, renderInTestApp } from '@backstage/test-utils';
|
||||
import {
|
||||
MockStorageApi,
|
||||
renderInTestApp,
|
||||
TestApiRegistry,
|
||||
} from '@backstage/test-utils';
|
||||
import { act, fireEvent } from '@testing-library/react';
|
||||
import * as React from 'react';
|
||||
import { CatalogTable } from './CatalogTable';
|
||||
@@ -51,10 +55,10 @@ const entities: Entity[] = [
|
||||
];
|
||||
|
||||
describe('CatalogTable component', () => {
|
||||
const mockApis = ApiRegistry.with(
|
||||
const mockApis = TestApiRegistry.from([
|
||||
starredEntitiesApiRef,
|
||||
new DefaultStarredEntitiesApi({ storageApi: MockStorageApi.create() }),
|
||||
);
|
||||
]);
|
||||
|
||||
beforeEach(() => {
|
||||
window.open = jest.fn();
|
||||
|
||||
+6
-14
@@ -21,28 +21,20 @@ import {
|
||||
EntityProvider,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { DependencyOfComponentsCard } from './DependencyOfComponentsCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<DependencyOfComponentsCard />', () => {
|
||||
const catalogApi: jest.Mocked<CatalogApi> = {
|
||||
getLocationById: jest.fn(),
|
||||
getEntityByName: jest.fn(),
|
||||
getEntities: jest.fn(),
|
||||
addLocation: jest.fn(),
|
||||
getLocationByEntity: jest.fn(),
|
||||
removeEntityByUid: jest.fn(),
|
||||
} as any;
|
||||
const getEntities: jest.MockedFunction<CatalogApi['getEntities']> = jest.fn();
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogApi);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider apis={[[catalogApiRef, { getEntities }]]}>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -97,7 +89,7 @@ describe('<DependencyOfComponentsCard />', () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
catalogApi.getEntities.mockResolvedValue({
|
||||
getEntities.mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
apiVersion: 'v1',
|
||||
|
||||
+6
-14
@@ -21,28 +21,20 @@ import {
|
||||
EntityProvider,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { DependsOnComponentsCard } from './DependsOnComponentsCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<DependsOnComponentsCard />', () => {
|
||||
const catalogApi: jest.Mocked<CatalogApi> = {
|
||||
getLocationById: jest.fn(),
|
||||
getEntityByName: jest.fn(),
|
||||
getEntities: jest.fn(),
|
||||
addLocation: jest.fn(),
|
||||
getLocationByEntity: jest.fn(),
|
||||
removeEntityByUid: jest.fn(),
|
||||
} as any;
|
||||
const getEntities: jest.MockedFunction<CatalogApi['getEntities']> = jest.fn();
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogApi);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider apis={[[catalogApiRef, { getEntities }]]}>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -97,7 +89,7 @@ describe('<DependsOnComponentsCard />', () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
catalogApi.getEntities.mockResolvedValue({
|
||||
getEntities.mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
apiVersion: 'v1',
|
||||
|
||||
+6
-14
@@ -21,28 +21,20 @@ import {
|
||||
EntityProvider,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { DependsOnResourcesCard } from './DependsOnResourcesCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<DependsOnResourcesCard />', () => {
|
||||
const catalogApi: jest.Mocked<CatalogApi> = {
|
||||
getLocationById: jest.fn(),
|
||||
getEntityByName: jest.fn(),
|
||||
getEntities: jest.fn(),
|
||||
addLocation: jest.fn(),
|
||||
getLocationByEntity: jest.fn(),
|
||||
removeEntityByUid: jest.fn(),
|
||||
} as any;
|
||||
const getEntities: jest.MockedFunction<CatalogApi['getEntities']> = jest.fn();
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogApi);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider apis={[[catalogApiRef, { getEntities }]]}>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -97,7 +89,7 @@ describe('<DependsOnResourcesCard />', () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
catalogApi.getEntities.mockResolvedValue({
|
||||
getEntities.mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
apiVersion: 'v1',
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import { AlertApi, alertApiRef } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
AsyncEntityProvider,
|
||||
@@ -26,7 +26,11 @@ import {
|
||||
entityRouteRef,
|
||||
starredEntitiesApiRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { MockStorageApi, renderInTestApp } from '@backstage/test-utils';
|
||||
import {
|
||||
MockStorageApi,
|
||||
renderInTestApp,
|
||||
TestApiRegistry,
|
||||
} from '@backstage/test-utils';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
@@ -40,12 +44,14 @@ const mockEntity = {
|
||||
},
|
||||
} as Entity;
|
||||
|
||||
const mockApis = ApiRegistry.with(catalogApiRef, {} as CatalogApi)
|
||||
.with(alertApiRef, {} as AlertApi)
|
||||
.with(
|
||||
const mockApis = TestApiRegistry.from(
|
||||
[catalogApiRef, {} as CatalogApi],
|
||||
[alertApiRef, {} as AlertApi],
|
||||
[
|
||||
starredEntitiesApiRef,
|
||||
new DefaultStarredEntitiesApi({ storageApi: MockStorageApi.create() }),
|
||||
);
|
||||
],
|
||||
);
|
||||
|
||||
describe('EntityLayout', () => {
|
||||
it('renders simplest case', async () => {
|
||||
|
||||
@@ -20,10 +20,9 @@ import { ORIGIN_LOCATION_ANNOTATION } from '@backstage/catalog-model';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
|
||||
import { AlertApi, alertApiRef } from '@backstage/core-plugin-api';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('DeleteEntityDialog', () => {
|
||||
const alertApi: jest.Mocked<AlertApi> = {
|
||||
@@ -34,10 +33,6 @@ describe('DeleteEntityDialog', () => {
|
||||
const catalogClient: jest.Mocked<CatalogApi> = {
|
||||
removeEntityByUid: jest.fn(),
|
||||
} as any;
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogClient).with(
|
||||
alertApiRef,
|
||||
alertApi,
|
||||
);
|
||||
|
||||
const entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
@@ -54,7 +49,14 @@ describe('DeleteEntityDialog', () => {
|
||||
};
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[catalogApiRef, catalogClient],
|
||||
[alertApiRef, alertApi],
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -15,23 +15,16 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
CatalogApi,
|
||||
catalogApiRef,
|
||||
catalogRouteRef,
|
||||
EntityProvider,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
import { EntityOrphanWarning } from './EntityOrphanWarning';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<EntityOrphanWarning />', () => {
|
||||
const catalogClient: jest.Mocked<CatalogApi> = {
|
||||
removeEntityByUid: jest.fn(),
|
||||
} as any;
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogClient);
|
||||
|
||||
it('renders EntityOrphanWarning if the entity is orphan', async () => {
|
||||
const entity = {
|
||||
apiVersion: 'v1',
|
||||
@@ -50,11 +43,20 @@ describe('<EntityOrphanWarning />', () => {
|
||||
};
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
catalogApiRef,
|
||||
{
|
||||
removeEntityByUid: jest.fn(),
|
||||
},
|
||||
],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={entity}>
|
||||
<EntityOrphanWarning />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/create': catalogRouteRef,
|
||||
|
||||
+8
-8
@@ -21,17 +21,17 @@ import {
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
import { EntityProcessingErrorsPanel } from './EntityProcessingErrorsPanel';
|
||||
import { Entity, getEntityName } from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
|
||||
describe('<EntityProcessErrors />', () => {
|
||||
const catalogClient: jest.Mocked<CatalogApi> = {
|
||||
getEntityAncestors: jest.fn(),
|
||||
} as any;
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogClient);
|
||||
const getEntityAncestors: jest.MockedFunction<
|
||||
CatalogApi['getEntityAncestors']
|
||||
> = jest.fn();
|
||||
const apis = TestApiRegistry.from([catalogApiRef, { getEntityAncestors }]);
|
||||
|
||||
it('renders EntityProcessErrors if the entity has errors', async () => {
|
||||
const entity: Entity = {
|
||||
@@ -97,7 +97,7 @@ describe('<EntityProcessErrors />', () => {
|
||||
},
|
||||
};
|
||||
|
||||
catalogClient.getEntityAncestors.mockResolvedValue({
|
||||
getEntityAncestors.mockResolvedValue({
|
||||
root: getEntityName(entity),
|
||||
items: [{ entity, parents: [] }],
|
||||
});
|
||||
@@ -198,7 +198,7 @@ describe('<EntityProcessErrors />', () => {
|
||||
],
|
||||
},
|
||||
};
|
||||
catalogClient.getEntityAncestors.mockResolvedValue({
|
||||
getEntityAncestors.mockResolvedValue({
|
||||
root: getEntityName(entity),
|
||||
items: [
|
||||
{ entity, parents: [getEntityName(parent)] },
|
||||
|
||||
@@ -21,17 +21,14 @@ import React from 'react';
|
||||
import { isKind } from './conditions';
|
||||
import { EntitySwitch } from './EntitySwitch';
|
||||
import { featureFlagsApiRef } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
LocalStorageFeatureFlags,
|
||||
ApiProvider,
|
||||
ApiRegistry,
|
||||
} from '@backstage/core-app-api';
|
||||
import { LocalStorageFeatureFlags } from '@backstage/core-app-api';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
|
||||
const mockFeatureFlagsApi = new LocalStorageFeatureFlags();
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={ApiRegistry.with(featureFlagsApiRef, mockFeatureFlagsApi)}>
|
||||
<TestApiProvider apis={[[featureFlagsApiRef, mockFeatureFlagsApi]]}>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
);
|
||||
|
||||
describe('EntitySwitch', () => {
|
||||
|
||||
@@ -21,28 +21,20 @@ import {
|
||||
EntityProvider,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { HasComponentsCard } from './HasComponentsCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<HasComponentsCard />', () => {
|
||||
const catalogApi: jest.Mocked<CatalogApi> = {
|
||||
getLocationById: jest.fn(),
|
||||
getEntityByName: jest.fn(),
|
||||
getEntities: jest.fn(),
|
||||
addLocation: jest.fn(),
|
||||
getLocationByEntity: jest.fn(),
|
||||
removeEntityByUid: jest.fn(),
|
||||
} as any;
|
||||
const getEntities: jest.MockedFunction<CatalogApi['getEntities']> = jest.fn();
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogApi);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider apis={[[catalogApiRef, { getEntities }]]}>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -97,7 +89,7 @@ describe('<HasComponentsCard />', () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
catalogApi.getEntities.mockResolvedValue({
|
||||
getEntities.mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
apiVersion: 'v1',
|
||||
|
||||
@@ -21,28 +21,20 @@ import {
|
||||
EntityProvider,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { HasResourcesCard } from './HasResourcesCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<HasResourcesCard />', () => {
|
||||
const catalogApi: jest.Mocked<CatalogApi> = {
|
||||
getLocationById: jest.fn(),
|
||||
getEntityByName: jest.fn(),
|
||||
getEntities: jest.fn(),
|
||||
addLocation: jest.fn(),
|
||||
getLocationByEntity: jest.fn(),
|
||||
removeEntityByUid: jest.fn(),
|
||||
} as any;
|
||||
const getEntities: jest.MockedFunction<CatalogApi['getEntities']> = jest.fn();
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogApi);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider apis={[[catalogApiRef, { getEntities }]]}>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -92,7 +84,7 @@ describe('<HasResourcesCard />', () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
catalogApi.getEntities.mockResolvedValue({
|
||||
getEntities.mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
apiVersion: 'v1',
|
||||
|
||||
@@ -21,28 +21,20 @@ import {
|
||||
EntityProvider,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { HasSubcomponentsCard } from './HasSubcomponentsCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<HasSubcomponentsCard />', () => {
|
||||
const catalogApi: jest.Mocked<CatalogApi> = {
|
||||
getLocationById: jest.fn(),
|
||||
getEntityByName: jest.fn(),
|
||||
getEntities: jest.fn(),
|
||||
addLocation: jest.fn(),
|
||||
getLocationByEntity: jest.fn(),
|
||||
removeEntityByUid: jest.fn(),
|
||||
} as any;
|
||||
const getEntities: jest.MockedFunction<CatalogApi['getEntities']> = jest.fn();
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogApi);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider apis={[[catalogApiRef, { getEntities }]]}>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -97,7 +89,7 @@ describe('<HasSubcomponentsCard />', () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
catalogApi.getEntities.mockResolvedValue({
|
||||
getEntities.mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
apiVersion: 'v1',
|
||||
|
||||
@@ -21,28 +21,20 @@ import {
|
||||
EntityProvider,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { HasSystemsCard } from './HasSystemsCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<HasSystemsCard />', () => {
|
||||
const catalogApi: jest.Mocked<CatalogApi> = {
|
||||
getLocationById: jest.fn(),
|
||||
getEntityByName: jest.fn(),
|
||||
getEntities: jest.fn(),
|
||||
addLocation: jest.fn(),
|
||||
getLocationByEntity: jest.fn(),
|
||||
removeEntityByUid: jest.fn(),
|
||||
} as any;
|
||||
const getEntities: jest.MockedFunction<CatalogApi['getEntities']> = jest.fn();
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(catalogApiRef, catalogApi);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider apis={[[catalogApiRef, { getEntities }]]}>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -95,7 +87,7 @@ describe('<HasSystemsCard />', () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
catalogApi.getEntities.mockResolvedValue({
|
||||
getEntities.mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
apiVersion: 'v1',
|
||||
|
||||
@@ -21,10 +21,9 @@ import {
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { Entity, RELATION_PART_OF } from '@backstage/catalog-model';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
import { SystemDiagramCard } from './SystemDiagramCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<SystemDiagramCard />', () => {
|
||||
beforeAll(() => {
|
||||
@@ -55,11 +54,11 @@ describe('<SystemDiagramCard />', () => {
|
||||
};
|
||||
|
||||
const { queryByText } = await renderInTestApp(
|
||||
<ApiProvider apis={ApiRegistry.from([[catalogApiRef, catalogApi]])}>
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
<EntityProvider entity={entity}>
|
||||
<SystemDiagramCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
@@ -114,11 +113,11 @@ describe('<SystemDiagramCard />', () => {
|
||||
};
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={ApiRegistry.from([[catalogApiRef, catalogApi]])}>
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
<EntityProvider entity={entity}>
|
||||
<SystemDiagramCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
@@ -173,11 +172,11 @@ describe('<SystemDiagramCard />', () => {
|
||||
};
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={ApiRegistry.from([[catalogApiRef, catalogApi]])}>
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
<EntityProvider entity={entity}>
|
||||
<SystemDiagramCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
|
||||
import { CostInsightsHeader } from './CostInsightsHeader';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import { IdentityApi, identityApiRef } from '@backstage/core-plugin-api';
|
||||
|
||||
describe('<CostInsightsHeader/>', () => {
|
||||
@@ -29,7 +29,7 @@ describe('<CostInsightsHeader/>', () => {
|
||||
}),
|
||||
};
|
||||
|
||||
const apis = ApiRegistry.from([[identityApiRef, identityApi]]);
|
||||
const apis = TestApiRegistry.from([identityApiRef, identityApi]);
|
||||
|
||||
it('Shows nothing to do when no alerts exist', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
|
||||
@@ -34,9 +34,7 @@ export const unlabeledDataflowAlertRef = createRouteRef({
|
||||
|
||||
export const costInsightsPlugin = createPlugin({
|
||||
id: 'cost-insights',
|
||||
register({ featureFlags }) {
|
||||
featureFlags.register('cost-insights-currencies');
|
||||
},
|
||||
featureFlags: [{ name: 'cost-insights-currencies' }],
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
growthAlerts: projectGrowthAlertRef,
|
||||
|
||||
@@ -30,8 +30,9 @@ import { Group, Duration } from '../types';
|
||||
|
||||
// TODO(Rugvip): Could be good to have a clear place to put test utils that is linted accordingly
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { IdentityApi, identityApiRef } from '@backstage/core-plugin-api';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
|
||||
type PartialPropsWithChildren<T> = PropsWithChildren<Partial<T>>;
|
||||
|
||||
@@ -199,16 +200,17 @@ export const MockCostInsightsApiProvider = ({
|
||||
getUserGroups: jest.fn(),
|
||||
};
|
||||
|
||||
// TODO: defaultConfigApiRef: ConfigApiRef
|
||||
|
||||
const defaultContext = ApiRegistry.from([
|
||||
[identityApiRef, { ...defaultIdentityApi, ...context.identityApi }],
|
||||
[
|
||||
costInsightsApiRef,
|
||||
{ ...defaultCostInsightsApi, ...context.costInsightsApi },
|
||||
],
|
||||
// [configApiRef, { ...defaultConfigApiRef, ...context.configApiRef }]
|
||||
]);
|
||||
|
||||
return <ApiProvider apis={defaultContext}>{children}</ApiProvider>;
|
||||
return (
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[identityApiRef, { ...defaultIdentityApi, ...context.identityApi }],
|
||||
[
|
||||
costInsightsApiRef,
|
||||
{ ...defaultCostInsightsApi, ...context.costInsightsApi },
|
||||
],
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -15,11 +15,10 @@
|
||||
*/
|
||||
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor, getByText } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { DefaultExplorePage } from './DefaultExplorePage';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<DefaultExplorePage />', () => {
|
||||
const catalogApi: jest.Mocked<typeof catalogApiRef.T> = {
|
||||
@@ -36,9 +35,9 @@ describe('<DefaultExplorePage />', () => {
|
||||
};
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={ApiRegistry.with(catalogApiRef, catalogApi)}>
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -16,12 +16,11 @@
|
||||
|
||||
import { DomainEntity } from '@backstage/catalog-model';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { catalogEntityRouteRef } from '../../routes';
|
||||
import { DomainExplorerContent } from './DomainExplorerContent';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<DomainExplorerContent />', () => {
|
||||
const catalogApi: jest.Mocked<typeof catalogApiRef.T> = {
|
||||
@@ -38,9 +37,9 @@ describe('<DomainExplorerContent />', () => {
|
||||
};
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={ApiRegistry.with(catalogApiRef, catalogApi)}>
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
);
|
||||
|
||||
const mountedRoutes = {
|
||||
|
||||
@@ -14,16 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
ApiProvider,
|
||||
ApiRegistry,
|
||||
FeatureFlagged,
|
||||
} from '@backstage/core-app-api';
|
||||
import { FeatureFlagged } from '@backstage/core-app-api';
|
||||
import {
|
||||
FeatureFlagsApi,
|
||||
featureFlagsApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { ExploreLayout } from './ExploreLayout';
|
||||
@@ -35,11 +31,11 @@ const featureFlagsApi: jest.Mocked<FeatureFlagsApi> = {
|
||||
registerFlag: jest.fn(),
|
||||
};
|
||||
|
||||
const mockApis = ApiRegistry.with(featureFlagsApiRef, featureFlagsApi);
|
||||
|
||||
describe('<ExploreLayout />', () => {
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={mockApis}>{children}</ApiProvider>
|
||||
<TestApiProvider apis={[[featureFlagsApiRef, featureFlagsApi]]}>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -20,10 +20,9 @@ import {
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
import { GroupsDiagram } from './GroupsDiagram';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<GroupsDiagram />', () => {
|
||||
beforeAll(() => {
|
||||
@@ -57,9 +56,9 @@ describe('<GroupsDiagram />', () => {
|
||||
};
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={ApiRegistry.from([[catalogApiRef, catalogApi]])}>
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
<GroupsDiagram />
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
|
||||
@@ -16,11 +16,10 @@
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { GroupsExplorerContent } from '../GroupsExplorerContent';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<GroupsExplorerContent />', () => {
|
||||
const catalogApi: jest.Mocked<typeof catalogApiRef.T> = {
|
||||
@@ -37,9 +36,9 @@ describe('<GroupsExplorerContent />', () => {
|
||||
};
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={ApiRegistry.with(catalogApiRef, catalogApi)}>
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
);
|
||||
|
||||
const mountedRoutes = {
|
||||
|
||||
@@ -18,13 +18,12 @@ import {
|
||||
ExploreTool,
|
||||
exploreToolsConfigRef,
|
||||
} from '@backstage/plugin-explore-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { lightTheme } from '@backstage/theme';
|
||||
import { ThemeProvider } from '@material-ui/core';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { ToolExplorerContent } from './ToolExplorerContent';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<ToolExplorerContent />', () => {
|
||||
const exploreToolsConfigApi: jest.Mocked<typeof exploreToolsConfigRef.T> = {
|
||||
@@ -33,11 +32,9 @@ describe('<ToolExplorerContent />', () => {
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.with(exploreToolsConfigRef, exploreToolsConfigApi)}
|
||||
>
|
||||
<TestApiProvider apis={[[exploreToolsConfigRef, exploreToolsConfigApi]]}>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
</TestApiProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
|
||||
@@ -14,19 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import { fireHydrantApiRef } from '../../api';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { ServiceDetailsCard } from './ServiceDetailsCard';
|
||||
import { Service, Incident } from '../types';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils';
|
||||
|
||||
const mockFireHydrantApi = {
|
||||
getServiceDetails: () => {},
|
||||
getServiceAnalytics: () => {},
|
||||
getServiceDetails: jest.fn(),
|
||||
getServiceAnalytics: jest.fn(),
|
||||
};
|
||||
|
||||
const apis = ApiRegistry.from([[fireHydrantApiRef, mockFireHydrantApi]]);
|
||||
const apis = TestApiRegistry.from([fireHydrantApiRef, mockFireHydrantApi]);
|
||||
|
||||
jest.mock('@backstage/plugin-catalog-react', () => ({
|
||||
useEntity: () => {
|
||||
|
||||
@@ -16,11 +16,10 @@
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntityProvider } from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
import { FossaApi, fossaApiRef } from '../../api';
|
||||
import { FossaCard } from './FossaCard';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('<FossaCard />', () => {
|
||||
const fossaApi: jest.Mocked<FossaApi> = {
|
||||
@@ -30,10 +29,10 @@ describe('<FossaCard />', () => {
|
||||
let Wrapper: React.ComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
const apis = ApiRegistry.with(fossaApiRef, fossaApi);
|
||||
|
||||
Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ApiProvider apis={apis}>{children}</ApiProvider>
|
||||
<TestApiProvider apis={[[fossaApiRef, fossaApi]]}>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user