Merge pull request #9669 from backstage/rugvip/starred

catalog-react: move DefaultStarredEntitiesApi to catalog plugin + fixes
This commit is contained in:
Patrik Oldsberg
2022-03-01 10:47:21 +01:00
committed by GitHub
28 changed files with 328 additions and 224 deletions
+9
View File
@@ -0,0 +1,9 @@
---
'@backstage/plugin-catalog-react': patch
---
**BREAKING**: Moved **DefaultStarredEntitiesApi** to `@backstage/plugin-catalog`. If you were using this in tests, you can use the new `MockStarredEntitiesApi` from `@backstage/plugin-catalog-react` instead.
Fixed a risky behavior where `DefaultStarredEntitiesApi` forwarded values to observers that were later mutated.
Removed the `isStarred` method from `DefaultStarredEntitiesApi`, as it is not part of the `StarredEntitiesApi`.
@@ -16,15 +16,14 @@
import { EntityLayout } from '@backstage/plugin-catalog';
import {
DefaultStarredEntitiesApi,
EntityProvider,
starredEntitiesApiRef,
MockStarredEntitiesApi,
} from '@backstage/plugin-catalog-react';
import { githubActionsApiRef } from '@backstage/plugin-github-actions';
import { permissionApiRef } from '@backstage/plugin-permission-react';
import {
MockPermissionApi,
MockStorageApi,
renderInTestApp,
TestApiProvider,
} from '@backstage/test-utils';
@@ -59,12 +58,7 @@ describe('EntityPage Test', () => {
<TestApiProvider
apis={[
[githubActionsApiRef, mockedApi],
[
starredEntitiesApiRef,
new DefaultStarredEntitiesApi({
storageApi: MockStorageApi.create(),
}),
],
[starredEntitiesApiRef, new MockStarredEntitiesApi()],
[permissionApiRef, mockPermissionApi],
]}
>
@@ -22,11 +22,13 @@ import {
configApiRef,
storageApiRef,
} from '@backstage/core-plugin-api';
import { CatalogTableRow } from '@backstage/plugin-catalog';
import {
CatalogTableRow,
DefaultStarredEntitiesApi,
} from '@backstage/plugin-catalog';
import {
CatalogApi,
catalogApiRef,
DefaultStarredEntitiesApi,
entityRouteRef,
starredEntitiesApiRef,
} from '@backstage/plugin-catalog-react';
+8 -12
View File
@@ -24,7 +24,6 @@ import { default as React_2 } from 'react';
import { ReactNode } from 'react';
import { RouteRef } from '@backstage/core-plugin-api';
import { ScmIntegrationRegistry } from '@backstage/integration';
import { StorageApi } from '@backstage/core-plugin-api';
import { StyleRules } from '@material-ui/core/styles/withStyles';
import { SystemEntity } from '@backstage/catalog-model';
import { TableColumn } from '@backstage/core-components';
@@ -138,17 +137,6 @@ export type DefaultEntityFilters = {
text?: EntityTextFilter;
};
// @public
export class DefaultStarredEntitiesApi implements StarredEntitiesApi {
constructor(opts: { storageApi: StorageApi });
// (undocumented)
isStarred(entityRef: string): boolean;
// (undocumented)
starredEntitie$(): Observable<Set<string>>;
// (undocumented)
toggleStarred(entityRef: string): Promise<void>;
}
// @public (undocumented)
export type EntityFilter = {
getCatalogFilters?: () => Record<
@@ -488,6 +476,14 @@ export const MockEntityListContextProvider: ({
value?: Partial<EntityListContextProps<DefaultEntityFilters>> | undefined;
}>) => JSX.Element;
// @public
export class MockStarredEntitiesApi implements StarredEntitiesApi {
// (undocumented)
starredEntitie$(): Observable<Set<string>>;
// (undocumented)
toggleStarred(entityRef: string): Promise<void>;
}
// @public @deprecated (undocumented)
export function reduceCatalogFilters(
filters: EntityFilter[],
@@ -1,103 +0,0 @@
/*
* 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 { stringifyEntityRef } from '@backstage/catalog-model';
import { StorageApi } from '@backstage/core-plugin-api';
import { MockStorageApi } from '@backstage/test-utils';
import { DefaultStarredEntitiesApi } from './DefaultStarredEntitiesApi';
import { performMigrationToTheNewBucket } from './migration';
jest.mock('./migration');
describe('DefaultStarredEntitiesApi', () => {
let mockStorage: StorageApi;
let starredEntitiesApi: DefaultStarredEntitiesApi;
const mockEntityRef = stringifyEntityRef({
apiVersion: '1',
kind: 'Component',
metadata: {
name: 'mock',
},
});
beforeEach(() => {
(performMigrationToTheNewBucket as jest.Mock).mockResolvedValue(undefined);
mockStorage = MockStorageApi.create();
starredEntitiesApi = new DefaultStarredEntitiesApi({
storageApi: mockStorage,
});
});
afterEach(() => {
jest.resetAllMocks();
});
describe('constructor', () => {
it('should call migration', () => {
expect(performMigrationToTheNewBucket).toBeCalledTimes(1);
});
});
describe('toggleStarred', () => {
it('should star unstarred entity', async () => {
expect(starredEntitiesApi.isStarred(mockEntityRef)).toBe(false);
await starredEntitiesApi.toggleStarred(mockEntityRef);
expect(starredEntitiesApi.isStarred(mockEntityRef)).toBe(true);
});
it('should unstar starred entity', async () => {
const bucket = mockStorage.forBucket('starredEntities');
await bucket.set('entityRefs', ['component:default/mock']);
expect(starredEntitiesApi.isStarred(mockEntityRef)).toBe(true);
await starredEntitiesApi.toggleStarred(mockEntityRef);
expect(starredEntitiesApi.isStarred(mockEntityRef)).toBe(false);
});
});
describe('starredEntities$', () => {
const handler = jest.fn();
beforeEach(async () => {
await new Promise<void>(resolve => {
starredEntitiesApi.starredEntitie$().subscribe({
next: (...args) => {
handler(...args);
if (handler.mock.calls.length >= 2) {
resolve();
}
},
});
const bucket = mockStorage.forBucket('starredEntities');
bucket.set('entityRefs', ['component:default/mock']).then();
});
});
it('should receive updates', async () => {
expect(handler).toBeCalledTimes(2);
expect(handler).toBeCalledWith(new Set());
expect(handler).toBeCalledWith(new Set(['component:default/mock']));
});
});
});
@@ -0,0 +1,61 @@
/*
* 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 { MockStarredEntitiesApi } from './MockStarredEntitiesApi';
describe('MockStarredEntitiesApi', () => {
it('should toggle starred entities', async () => {
const api = new MockStarredEntitiesApi();
const updates1 = new Array<Set<string>>();
const sub1 = api
.starredEntitie$()
.subscribe(entities => updates1.push(entities));
api.toggleStarred('k:ns/e1');
api.toggleStarred('k:ns/e2');
await Promise.resolve();
expect(updates1).toEqual([
new Set(),
new Set(['k:ns/e1']),
new Set(['k:ns/e1', 'k:ns/e2']),
]);
const updates2 = new Array<Set<string>>();
const sub2 = api
.starredEntitie$()
.subscribe(entities => updates2.push(entities));
api.toggleStarred('k:ns/e2');
sub1.unsubscribe();
api.toggleStarred('k:ns/e2');
await Promise.resolve();
expect(updates1).toEqual([
new Set(),
new Set(['k:ns/e1']),
new Set(['k:ns/e1', 'k:ns/e2']),
new Set(['k:ns/e1']),
]);
expect(updates2).toEqual([
new Set(['k:ns/e1', 'k:ns/e2']),
new Set(['k:ns/e1']),
new Set(['k:ns/e1', 'k:ns/e2']),
]);
sub2.unsubscribe();
});
});
@@ -0,0 +1,54 @@
/*
* 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 { Observable } from '@backstage/types';
import ObservableImpl from 'zen-observable';
import { StarredEntitiesApi } from './StarredEntitiesApi';
/**
* An in-memory mock implementation of the StarredEntitiesApi.
*
* @public
*/
export class MockStarredEntitiesApi implements StarredEntitiesApi {
private readonly starredEntities = new Set<string>();
private readonly subscribers = new Set<
ZenObservable.SubscriptionObserver<Set<string>>
>();
private readonly observable = new ObservableImpl<Set<string>>(subscriber => {
subscriber.next(new Set(this.starredEntities));
this.subscribers.add(subscriber);
return () => {
this.subscribers.delete(subscriber);
};
});
async toggleStarred(entityRef: string): Promise<void> {
if (!this.starredEntities.delete(entityRef)) {
this.starredEntities.add(entityRef);
}
for (const subscription of this.subscribers) {
subscription.next(new Set(this.starredEntities));
}
}
starredEntitie$(): Observable<Set<string>> {
return this.observable;
}
}
@@ -14,6 +14,6 @@
* limitations under the License.
*/
export { DefaultStarredEntitiesApi } from './DefaultStarredEntitiesApi';
export { starredEntitiesApiRef } from './StarredEntitiesApi';
export type { StarredEntitiesApi } from './StarredEntitiesApi';
export { MockStarredEntitiesApi } from './MockStarredEntitiesApi';
@@ -29,7 +29,7 @@ import qs from 'qs';
import React, { PropsWithChildren } from 'react';
import { MemoryRouter } from 'react-router';
import { catalogApiRef } from '../api';
import { DefaultStarredEntitiesApi, starredEntitiesApiRef } from '../apis';
import { starredEntitiesApiRef, MockStarredEntitiesApi } from '../apis';
import { EntityKindPicker, UserListPicker } from '../components';
import { EntityKindFilter, EntityTypeFilter, UserListFilter } from '../filters';
import { UserListFilterKind } from '../types';
@@ -95,12 +95,7 @@ const wrapper = ({
[catalogApiRef, mockCatalogApi],
[identityApiRef, mockIdentityApi],
[storageApiRef, MockStorageApi.create()],
[
starredEntitiesApiRef,
new DefaultStarredEntitiesApi({
storageApi: MockStorageApi.create(),
}),
],
[starredEntitiesApiRef, new MockStarredEntitiesApi()],
]}
>
<EntityListProvider>
@@ -15,15 +15,18 @@
*/
import { Entity } from '@backstage/catalog-model';
import { StorageApi } from '@backstage/core-plugin-api';
import { MockStorageApi, TestApiProvider } from '@backstage/test-utils';
import { TestApiProvider } from '@backstage/test-utils';
import { act, renderHook } from '@testing-library/react-hooks';
import React, { PropsWithChildren } from 'react';
import { DefaultStarredEntitiesApi, starredEntitiesApiRef } from '../apis';
import {
starredEntitiesApiRef,
StarredEntitiesApi,
MockStarredEntitiesApi,
} from '../apis';
import { useStarredEntities } from './useStarredEntities';
describe('useStarredEntities', () => {
let mockStorage: StorageApi;
let mockApi: StarredEntitiesApi;
let wrapper: React.ComponentType;
const mockEntity: Entity = {
@@ -44,22 +47,15 @@ describe('useStarredEntities', () => {
};
beforeEach(() => {
mockStorage = MockStorageApi.create();
mockApi = new MockStarredEntitiesApi();
wrapper = ({ children }: PropsWithChildren<{}>) => (
<TestApiProvider
apis={[
[
starredEntitiesApiRef,
new DefaultStarredEntitiesApi({ storageApi: mockStorage }),
],
]}
>
<TestApiProvider apis={[[starredEntitiesApiRef, mockApi]]}>
{children}
</TestApiProvider>
);
});
it('should return an empty set for when there is no items in storage', async () => {
it('should return an empty set', async () => {
const { result, waitForNextUpdate } = renderHook(
() => useStarredEntities(),
{ wrapper },
@@ -70,10 +66,11 @@ describe('useStarredEntities', () => {
expect(result.current.starredEntities.size).toBe(0);
});
it('should return a set with the current items when there are items in storage', async () => {
it('should return a set with the current items', async () => {
const expectedIds = ['i', 'am', 'some', 'test', 'ids'];
const store = mockStorage?.forBucket('starredEntities');
await store?.set('entityRefs', expectedIds);
for (const id of expectedIds) {
mockApi.toggleStarred(id);
}
const { result, waitForNextUpdate } = renderHook(
() => useStarredEntities(),
+12
View File
@@ -13,10 +13,13 @@ import { ExternalRouteRef } from '@backstage/core-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { IndexableDocument } from '@backstage/search-common';
import { InfoCardVariants } from '@backstage/core-components';
import { Observable } from '@backstage/types';
import { Overrides } from '@material-ui/core/styles/overrides';
import { default as React_2 } from 'react';
import { ReactNode } from 'react';
import { RouteRef } from '@backstage/core-plugin-api';
import { StarredEntitiesApi } from '@backstage/plugin-catalog-react';
import { StorageApi } from '@backstage/core-plugin-api';
import { StyleRules } from '@material-ui/core/styles/withStyles';
import { TableColumn } from '@backstage/core-components';
import { TableProps } from '@backstage/core-components';
@@ -170,6 +173,15 @@ export interface DefaultCatalogPageProps {
initiallySelectedFilter?: UserListFilterKind;
}
// @public
export class DefaultStarredEntitiesApi implements StarredEntitiesApi {
constructor(opts: { storageApi: StorageApi });
// (undocumented)
starredEntitie$(): Observable<Set<string>>;
// (undocumented)
toggleStarred(entityRef: string): Promise<void>;
}
// @public (undocumented)
export interface DependencyOfComponentsCardProps {
// (undocumented)
+3 -1
View File
@@ -44,6 +44,7 @@
"@backstage/plugin-catalog-react": "^0.7.0",
"@backstage/search-common": "^0.2.4",
"@backstage/theme": "^0.2.15",
"@backstage/types": "^0.1.2",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
@@ -51,7 +52,8 @@
"lodash": "^4.17.21",
"react-helmet": "6.1.0",
"react-router": "6.0.0-beta.0",
"react-use": "^17.2.4"
"react-use": "^17.2.4",
"zen-observable": "^0.8.15"
},
"peerDependencies": {
"@types/react": "^16.13.1 || ^17.0.0",
@@ -0,0 +1,95 @@
/*
* 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 { MockStorageApi } from '@backstage/test-utils';
import { DefaultStarredEntitiesApi } from './DefaultStarredEntitiesApi';
import { performMigrationToTheNewBucket } from './migration';
jest.mock('./migration');
function getStarred(api: DefaultStarredEntitiesApi) {
return new Promise((resolve, reject) => {
const subscription = api.starredEntitie$().subscribe({
next(starred) {
resolve(starred);
subscription.unsubscribe();
},
error: reject,
});
});
}
describe('DefaultStarredEntitiesApi', () => {
beforeEach(() => {
(performMigrationToTheNewBucket as jest.Mock).mockResolvedValue(undefined);
});
afterEach(() => {
jest.resetAllMocks();
});
describe('constructor', () => {
it('should call migration', () => {
const api = new DefaultStarredEntitiesApi({
storageApi: MockStorageApi.create(),
});
expect(performMigrationToTheNewBucket).toBeCalledTimes(1);
expect(api).toBeDefined();
});
});
it('should notify and toggle starred entities', async () => {
const entityRef = 'component:default/mock';
const storageApi = MockStorageApi.create();
const storageBucket = storageApi.forBucket('starredEntities');
const api = new DefaultStarredEntitiesApi({ storageApi });
const values = new Array<Set<string>>();
api.starredEntitie$().subscribe({
next: value => {
values.push(value);
},
});
await expect(getStarred(api)).resolves.toEqual(new Set());
await api.toggleStarred(entityRef);
await expect(getStarred(api)).resolves.toEqual(new Set([entityRef]));
expect(storageBucket.snapshot('entityRefs')).toEqual(
expect.objectContaining({ presence: 'present', value: [entityRef] }),
);
await api.toggleStarred(entityRef);
await expect(getStarred(api)).resolves.toEqual(new Set());
expect(storageBucket.snapshot('entityRefs')).toEqual(
expect.objectContaining({ presence: 'present', value: [] }),
);
expect(values).toEqual([new Set(), new Set([entityRef]), new Set()]);
});
it('should read starred entities from storage', async () => {
const entityRef = 'component:default/mock';
const storageApi = MockStorageApi.create();
const storageBucket = storageApi.forBucket('starredEntities');
storageBucket.set('entityRefs', [entityRef]);
const api = new DefaultStarredEntitiesApi({ storageApi });
await expect(getStarred(api)).resolves.toEqual(new Set([entityRef]));
});
});
@@ -15,10 +15,10 @@
*/
import { StorageApi } from '@backstage/core-plugin-api';
import { StarredEntitiesApi } from '@backstage/plugin-catalog-react';
import { Observable } from '@backstage/types';
import ObservableImpl from 'zen-observable';
import { performMigrationToTheNewBucket } from './migration';
import { StarredEntitiesApi } from './StarredEntitiesApi';
/**
* Default implementation of the StarredEntitiesApi that is backed by the StorageApi.
@@ -64,17 +64,13 @@ export class DefaultStarredEntitiesApi implements StarredEntitiesApi {
return this.observable;
}
isStarred(entityRef: string): boolean {
return this.starredEntities.has(entityRef);
}
private readonly subscribers = new Set<
ZenObservable.SubscriptionObserver<Set<string>>
>();
private readonly observable = new ObservableImpl<Set<string>>(subscriber => {
// forward the the latest value
subscriber.next(this.starredEntities);
subscriber.next(new Set(this.starredEntities));
this.subscribers.add(subscriber);
return () => {
@@ -84,7 +80,7 @@ export class DefaultStarredEntitiesApi implements StarredEntitiesApi {
private notifyChanges() {
for (const subscription of this.subscribers) {
subscription.next(this.starredEntities);
subscription.next(new Set(this.starredEntities));
}
}
}
@@ -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 { DefaultStarredEntitiesApi } from './DefaultStarredEntitiesApi';
+17
View File
@@ -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 './StarredEntitiesApi';
@@ -29,9 +29,9 @@ import {
} from '@backstage/core-plugin-api';
import {
catalogApiRef,
DefaultStarredEntitiesApi,
entityRouteRef,
starredEntitiesApiRef,
MockStarredEntitiesApi,
} from '@backstage/plugin-catalog-react';
import {
mockBreakpoint,
@@ -141,10 +141,7 @@ describe('DefaultCatalogPage', () => {
[catalogApiRef, catalogApi],
[identityApiRef, identityApi],
[storageApiRef, storageApi],
[
starredEntitiesApiRef,
new DefaultStarredEntitiesApi({ storageApi }),
],
[starredEntitiesApiRef, new MockStarredEntitiesApi()],
]}
>
{children}
@@ -22,16 +22,12 @@ import {
import { ApiProvider } from '@backstage/core-app-api';
import {
entityRouteRef,
DefaultStarredEntitiesApi,
MockEntityListContextProvider,
starredEntitiesApiRef,
UserListFilter,
MockStarredEntitiesApi,
} from '@backstage/plugin-catalog-react';
import {
MockStorageApi,
renderInTestApp,
TestApiRegistry,
} from '@backstage/test-utils';
import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils';
import { act, fireEvent } from '@testing-library/react';
import * as React from 'react';
import { CatalogTable } from './CatalogTable';
@@ -57,7 +53,7 @@ const entities: Entity[] = [
describe('CatalogTable component', () => {
const mockApis = TestApiRegistry.from([
starredEntitiesApiRef,
new DefaultStarredEntitiesApi({ storageApi: MockStorageApi.create() }),
new MockStarredEntitiesApi(),
]);
beforeEach(() => {
@@ -21,15 +21,14 @@ import { AlertApi, alertApiRef } from '@backstage/core-plugin-api';
import {
AsyncEntityProvider,
catalogApiRef,
DefaultStarredEntitiesApi,
EntityProvider,
entityRouteRef,
starredEntitiesApiRef,
MockStarredEntitiesApi,
} from '@backstage/plugin-catalog-react';
import { permissionApiRef } from '@backstage/plugin-permission-react';
import {
MockPermissionApi,
MockStorageApi,
renderInTestApp,
TestApiRegistry,
} from '@backstage/test-utils';
@@ -48,10 +47,7 @@ const mockEntity = {
const mockApis = TestApiRegistry.from(
[catalogApiRef, {} as CatalogApi],
[alertApiRef, {} as AlertApi],
[
starredEntitiesApiRef,
new DefaultStarredEntitiesApi({ storageApi: MockStorageApi.create() }),
],
[starredEntitiesApiRef, new MockStarredEntitiesApi()],
[permissionApiRef, new MockPermissionApi()],
);
+2
View File
@@ -20,6 +20,8 @@
* @packageDocumentation
*/
export * from './apis';
export * from './components/AboutCard';
export * from './components/CatalogKindHeader';
export * from './components/CatalogSearchResultListItem';
+1 -1
View File
@@ -19,7 +19,6 @@ import { Entity } from '@backstage/catalog-model';
import {
catalogApiRef,
catalogRouteRef,
DefaultStarredEntitiesApi,
entityRouteRef,
starredEntitiesApiRef,
} from '@backstage/plugin-catalog-react';
@@ -33,6 +32,7 @@ import {
fetchApiRef,
storageApiRef,
} from '@backstage/core-plugin-api';
import { DefaultStarredEntitiesApi } from './apis';
import { AboutCardProps } from './components/AboutCard';
import { DefaultCatalogPageProps } from './components/CatalogPage';
import { DependencyOfComponentsCardProps } from './components/DependencyOfComponentsCard';
@@ -13,40 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
renderInTestApp,
TestApiProvider,
MockStorageApi,
} from '@backstage/test-utils';
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
import {
starredEntitiesApiRef,
MockStarredEntitiesApi,
entityRouteRef,
DefaultStarredEntitiesApi,
} from '@backstage/plugin-catalog-react';
import React from 'react';
import { Content } from './Content';
describe('StarredEntitiesContent', () => {
it('should render list of tools', async () => {
const mockStorageApi = MockStorageApi.create();
await mockStorageApi
.forBucket('starredEntities')
.set('entityRefs', [
'component:default/mock-starred-entity',
'component:default/mock-starred-entity-2',
]);
const mockedApi = new MockStarredEntitiesApi();
mockedApi.toggleStarred('component:default/mock-starred-entity');
mockedApi.toggleStarred('component:default/mock-starred-entity-2');
const { getByText } = await renderInTestApp(
<TestApiProvider
apis={[
[
starredEntitiesApiRef,
new DefaultStarredEntitiesApi({
storageApi: mockStorageApi,
}),
],
]}
>
<TestApiProvider apis={[[starredEntitiesApiRef, mockedApi]]}>
<Content />
</TestApiProvider>,
{
@@ -22,8 +22,8 @@ import {
} from '@backstage/test-utils';
import {
starredEntitiesApiRef,
MockStarredEntitiesApi,
entityRouteRef,
DefaultStarredEntitiesApi,
} from '@backstage/plugin-catalog-react';
import { Grid } from '@material-ui/core';
import React, { ComponentType } from 'react';
@@ -44,14 +44,7 @@ export default {
(Story: ComponentType<{}>) =>
wrapInTestApp(
<TestApiProvider
apis={[
[
starredEntitiesApiRef,
new DefaultStarredEntitiesApi({
storageApi: mockStorageApi,
}),
],
]}
apis={[[starredEntitiesApiRef, new MockStarredEntitiesApi()]]}
>
<Story />
</TestApiProvider>,
@@ -25,8 +25,8 @@ import { wrapInTestApp, TestApiProvider, MockStorageApi} from '@backstage/test-u
import { Content, Page, InfoCard } from '@backstage/core-components';
import {
starredEntitiesApiRef,
MockStarredEntitiesApi,
entityRouteRef,
DefaultStarredEntitiesApi
} from '@backstage/plugin-catalog-react';
import {
HomePageSearchBar,
@@ -57,9 +57,7 @@ export default {
apis={[
[
starredEntitiesApiRef,
new DefaultStarredEntitiesApi({
storageApi: mockStorageApi,
}),
new MockStarredEntitiesApi(),
],
[searchApiRef, { query: () => Promise.resolve({ results: [] }) }],
]}
@@ -153,4 +151,3 @@ export const DefaultTemplate = () => {
</SearchContextProvider>
);
};
+4 -8
View File
@@ -20,16 +20,12 @@ import { scmIntegrationsApiRef } from '@backstage/integration-react';
import {
catalogApiRef,
starredEntitiesApiRef,
DefaultStarredEntitiesApi,
MockStarredEntitiesApi,
} from '@backstage/plugin-catalog-react';
import React from 'react';
import { scaffolderApiRef, ScaffolderClient } from '../src';
import { ScaffolderPage } from '../src/plugin';
import {
discoveryApiRef,
fetchApiRef,
storageApiRef,
} from '@backstage/core-plugin-api';
import { discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api';
import { CatalogEntityPage } from '@backstage/plugin-catalog';
createDevApp()
@@ -44,8 +40,8 @@ createDevApp()
})
.registerApi({
api: starredEntitiesApiRef,
deps: { storageApi: storageApiRef },
factory: ({ storageApi }) => new DefaultStarredEntitiesApi({ storageApi }),
deps: {},
factory: () => new MockStarredEntitiesApi(),
})
.registerApi({
api: scaffolderApiRef,
@@ -23,8 +23,8 @@ import {
import {
CatalogApi,
catalogApiRef,
DefaultStarredEntitiesApi,
starredEntitiesApiRef,
MockStarredEntitiesApi,
} from '@backstage/plugin-catalog-react';
import {
MockStorageApi,
@@ -73,7 +73,7 @@ describe('TechDocs Home', () => {
[catalogApiRef, mockCatalogApi],
[configApiRef, configApi],
[storageApiRef, storageApi],
[starredEntitiesApiRef, new DefaultStarredEntitiesApi({ storageApi })],
[starredEntitiesApiRef, new MockStarredEntitiesApi()],
);
it('should render a TechDocs home page', async () => {