diff --git a/plugins/catalog-react/src/hooks/useEntity.test.tsx b/plugins/catalog-react/src/hooks/useEntity.test.tsx
new file mode 100644
index 0000000000..9e73ed1475
--- /dev/null
+++ b/plugins/catalog-react/src/hooks/useEntity.test.tsx
@@ -0,0 +1,140 @@
+/*
+ * 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 React from 'react';
+import { renderHook } from '@testing-library/react-hooks';
+import {
+ useEntity,
+ EntityProvider,
+ AsyncEntityProvider,
+ EntityContext,
+} from './useEntity';
+import { Entity } from '@backstage/catalog-model';
+
+describe('EntityProvider', () => {
+ it('should provide no entity', async () => {
+ const { result } = renderHook(() => useEntity(), {
+ wrapper: ({ children }) => ,
+ });
+
+ expect(result.current.entity).toBe(undefined);
+ expect(result.current.loading).toBe(true);
+ expect(result.current.error).toBe(undefined);
+ expect(result.current.refresh).toBe(undefined);
+ });
+
+ it('should provide an entity', async () => {
+ const entity = { kind: 'MyEntity' } as Entity;
+ const { result } = renderHook(() => useEntity(), {
+ wrapper: ({ children }) => (
+
+ ),
+ });
+
+ expect(result.current.entity).toBe(entity);
+ expect(result.current.loading).toBe(false);
+ expect(result.current.error).toBe(undefined);
+ expect(result.current.refresh).toBe(undefined);
+ });
+});
+
+describe('AsyncEntityProvider', () => {
+ it('should provide no entity', async () => {
+ const { result } = renderHook(() => useEntity(), {
+ wrapper: ({ children }) => (
+
+ ),
+ });
+
+ expect(result.current.entity).toBe(undefined);
+ expect(result.current.loading).toBe(false);
+ expect(result.current.error).toBe(undefined);
+ expect(result.current.refresh).toBe(undefined);
+ });
+
+ it('should provide an entity', async () => {
+ const entity = { kind: 'MyEntity' } as Entity;
+ const refresh = () => {};
+ const { result } = renderHook(() => useEntity(), {
+ wrapper: ({ children }) => (
+
+ ),
+ });
+
+ expect(result.current.entity).toBe(entity);
+ expect(result.current.loading).toBe(false);
+ expect(result.current.error).toBe(undefined);
+ expect(result.current.refresh).toBe(refresh);
+ });
+
+ it('should provide an error', async () => {
+ const error = new Error('oh no');
+ const { result } = renderHook(() => useEntity(), {
+ wrapper: ({ children }) => (
+
+ ),
+ });
+
+ expect(result.current.entity).toBe(undefined);
+ expect(result.current.loading).toBe(false);
+ expect(result.current.error).toBe(error);
+ expect(result.current.refresh).toBe(undefined);
+ });
+});
+
+describe('EntityContext.Provider', () => {
+ it('should provide no entity', async () => {
+ const { result } = renderHook(() => useEntity(), {
+ wrapper: ({ children }) => (
+
+ ),
+ });
+
+ expect(result.current.entity).toBe(undefined);
+ expect(result.current.loading).toBe(false);
+ expect(result.current.error).toBe(undefined);
+ expect(result.current.refresh).toBe(undefined);
+ });
+
+ it('should provide an entity', async () => {
+ const entity = { kind: 'MyEntity' } as Entity;
+ const { result } = renderHook(() => useEntity(), {
+ wrapper: ({ children }) => (
+
+ ),
+ });
+
+ expect(result.current.entity).toBe(entity);
+ expect(result.current.loading).toBe(false);
+ expect(result.current.error).toBe(undefined);
+ expect(result.current.refresh).toBe(undefined);
+ });
+});
diff --git a/plugins/catalog-react/src/hooks/useEntity.tsx b/plugins/catalog-react/src/hooks/useEntity.tsx
index 4734039a4f..1f438f19a0 100644
--- a/plugins/catalog-react/src/hooks/useEntity.tsx
+++ b/plugins/catalog-react/src/hooks/useEntity.tsx
@@ -24,7 +24,6 @@ import React, {
ReactNode,
useEffect,
createContext,
- useContext,
Provider,
Context,
} from 'react';
@@ -51,6 +50,7 @@ export const EntityContext: Context =
error: undefined,
refresh: () => {},
});
+// We grab this for use in the new provider, since we're overriding it later on
const OldEntityProvider = EntityContext.Provider;
// This context has support for multiple concurrent versions of this package.
@@ -85,6 +85,8 @@ export const AsyncEntityProvider = ({
refresh,
}: AsyncEntityProviderProps) => {
const value = { entity, loading, error, refresh };
+ // We provide both the old and the new context, since
+ // consumers might be doing things like `useContext(EntityContext)`
return (
@@ -120,8 +122,16 @@ export const EntityProvider = ({ entity, children }: EntityProviderProps) => (
);
// This is used for forwards compatibility with the new entity context
-EntityContext.Provider =
- EntityProvider as unknown as Provider;
+const CompatibilityProvider = ({
+ value,
+ children,
+}: {
+ value: EntityLoadingStatus;
+ children: ReactNode;
+}) => {
+ return ;
+};
+EntityContext.Provider = CompatibilityProvider as Provider;
export const useEntityFromUrl = (): EntityLoadingStatus => {
const { kind, namespace, name } = useEntityCompoundName();
@@ -157,20 +167,12 @@ export const useEntityFromUrl = (): EntityLoadingStatus => {
export function useEntity() {
const versionedHolder =
useVersionedContext<{ 1: EntityLoadingStatus }>('entity-context');
- const oldContextValue = useContext(EntityContext);
if (!versionedHolder) {
- const { entity, loading, error, refresh } = oldContextValue;
- return { entity: entity as T, loading, error, refresh };
-
- // TODO(Rugvip): Throw this once we fully migrate to the new context
- // throw new Error(
- // 'Component can not be used outside of the context of an Entity',
- // );
+ throw new Error('Entity context is not available');
}
const value = versionedHolder.atVersion(1);
-
if (!value) {
throw new Error('EntityContext v1 not available');
}