From 8db4dc297a13d6f325d18960f57e76f4bafb559b Mon Sep 17 00:00:00 2001 From: blam Date: Sat, 5 Feb 2022 04:48:49 +0100 Subject: [PATCH] feat: added some nice tests for the TemplateListPage Signed-off-by: blam --- .../RegisterExistingButton.test.tsx | 57 +++++++++++++++ .../RegisterExistingButton.tsx | 66 +++++++++++++++++ .../TemplateListPage.test.tsx | 73 +++++++++++++++++++ .../TemplateListPage/TemplateListPage.tsx | 15 ++-- .../SecretsContext/SecretsContext.test.tsx | 43 +++++++++++ .../SecretsContext/SecretsContext.tsx | 73 +++++++++++++++++++ .../SecretsContext/index.ts | 20 +++++ .../TemplateWizardPage/TemplateWizardPage.tsx | 16 ++++ .../src/next/TemplateWizardPage/index.ts | 17 +++++ 9 files changed, 374 insertions(+), 6 deletions(-) create mode 100644 plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.test.tsx create mode 100644 plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.tsx create mode 100644 plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.test.tsx create mode 100644 plugins/scaffolder/src/next/TemplateWizardPage/SecretsContext/SecretsContext.test.tsx create mode 100644 plugins/scaffolder/src/next/TemplateWizardPage/SecretsContext/SecretsContext.tsx create mode 100644 plugins/scaffolder/src/next/TemplateWizardPage/SecretsContext/index.ts create mode 100644 plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx create mode 100644 plugins/scaffolder/src/next/TemplateWizardPage/index.ts diff --git a/plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.test.tsx b/plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.test.tsx new file mode 100644 index 0000000000..e6022d16fb --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.test.tsx @@ -0,0 +1,57 @@ +/* + * Copyright 2022 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 { renderInTestApp } from '@backstage/test-utils'; +import React from 'react'; +import { RegisterExistingButton } from './RegisterExistingButton'; +import { usePermission } from '@backstage/plugin-permission-react'; + +jest.mock('@backstage/plugin-permission-react', () => ({ + usePermission: jest.fn(), +})); + +describe('RegisterExistingButton', () => { + beforeEach(() => { + (usePermission as jest.Mock).mockClear(); + }); + + it('should not render if to is unset', async () => { + (usePermission as jest.Mock).mockReturnValue({ allowed: true }); + + const { queryByText } = await renderInTestApp( + , + ); + + expect(await queryByText('Pick me')).not.toBeInTheDocument(); + }); + + it('should not render if permissions are not allowed', async () => { + (usePermission as jest.Mock).mockReturnValue({ allowed: false }); + const { queryByText } = await renderInTestApp( + , + ); + + expect(await queryByText('Pick me')).not.toBeInTheDocument(); + }); + + it('should render the button with the text', async () => { + (usePermission as jest.Mock).mockReturnValue({ allowed: true }); + const { queryByText } = await renderInTestApp( + , + ); + + expect(await queryByText('Pick me')).toBeInTheDocument(); + }); +}); diff --git a/plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.tsx b/plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.tsx new file mode 100644 index 0000000000..be6b496a6c --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.tsx @@ -0,0 +1,66 @@ +/* + * 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 { BackstageTheme } from '@backstage/theme'; +import Button from '@material-ui/core/Button'; +import IconButton from '@material-ui/core/IconButton'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; +import React from 'react'; +import { Link as RouterLink, LinkProps } from 'react-router-dom'; +import AddCircleOutline from '@material-ui/icons/AddCircleOutline'; +import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common'; +import { usePermission } from '@backstage/plugin-permission-react'; + +/** + * Properties for {@link RegisterExistingButton} + * + * @public + */ +export type RegisterExistingButtonProps = { + title: string; +} & Partial>; + +/** + * A button that helps users to register an existing component. + * @public + */ +export const RegisterExistingButton = (props: RegisterExistingButtonProps) => { + const { title, to } = props; + const { allowed } = usePermission(catalogEntityCreatePermission); + const isXSScreen = useMediaQuery(theme => + theme.breakpoints.down('xs'), + ); + + if (!to || !allowed) { + return null; + } + + return isXSScreen ? ( + + + + ) : ( + + ); +}; diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.test.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.test.tsx new file mode 100644 index 0000000000..48ee53cdb7 --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.test.tsx @@ -0,0 +1,73 @@ +/* + * Copyright 2022 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 { + catalogApiRef, + DefaultStarredEntitiesApi, + starredEntitiesApiRef, +} from '@backstage/plugin-catalog-react'; +import { permissionApiRef } from '@backstage/plugin-permission-react'; +import { + MockStorageApi, + renderInTestApp, + TestApiProvider, +} from '@backstage/test-utils'; +import React from 'react'; +import { TemplateListPage } from './TemplateListPage'; + +describe('TemplateListPage', () => { + it('should render the search bar for templates', async () => { + const { getByPlaceholderText } = await renderInTestApp( + + + , + ); + + expect(getByPlaceholderText('Search')).toBeInTheDocument(); + }); + + it('should render the all and starred filters', async () => { + const { getByRole } = await renderInTestApp( + + + , + ); + + expect(getByRole('menuitem', { name: 'All' })).toBeInTheDocument(); + expect(getByRole('menuitem', { name: 'Starred' })).toBeInTheDocument(); + }); +}); diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx index dc736408ac..25441a6812 100644 --- a/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx +++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx @@ -35,6 +35,9 @@ import { UserListPicker, } from '@backstage/plugin-catalog-react'; import { CategoryPicker } from './CategoryPicker'; +import { RegisterExistingButton } from './RegisterExistingButton'; +import { useRouteRef } from '@backstage/core-plugin-api'; +import { registerComponentRouteRef } from '../../routes'; export type TemplateListGroup = { title?: string; @@ -60,6 +63,8 @@ const useStyles = makeStyles(theme => ({ export const TemplateListPage = (props: TemplateListPageProps) => { const styles = useStyles(); + const registerComponentLink = useRouteRef(registerComponentRouteRef); + return ( @@ -74,12 +79,10 @@ export const TemplateListPage = (props: TemplateListPageProps) => { /> - {/* {allowed && ( - - )} */} + Create new software components using standard templates. Different templates create different kinds of components (services, diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/SecretsContext/SecretsContext.test.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/SecretsContext/SecretsContext.test.tsx new file mode 100644 index 0000000000..37d35c9015 --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateWizardPage/SecretsContext/SecretsContext.test.tsx @@ -0,0 +1,43 @@ +/* + * Copyright 2022 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, { useContext } from 'react'; +import { + useTemplateSecrets, + SecretsContextProvider, + SecretsContext, +} from './SecretsContext'; +import { renderHook, act } from '@testing-library/react-hooks'; + +describe('SecretsContext', () => { + it('should allow the setting of secrets in the context', async () => { + const { result } = renderHook( + () => ({ + hook: useTemplateSecrets(), + context: useContext(SecretsContext), + }), + { + wrapper: ({ children }) => ( + {children} + ), + }, + ); + expect(result.current.context?.secrets.foo).toEqual(undefined); + + act(() => result.current.hook.setSecret({ foo: 'bar' })); + + expect(result.current.context?.secrets.foo).toEqual('bar'); + }); +}); diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/SecretsContext/SecretsContext.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/SecretsContext/SecretsContext.tsx new file mode 100644 index 0000000000..6570f9d901 --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateWizardPage/SecretsContext/SecretsContext.tsx @@ -0,0 +1,73 @@ +/* + * Copyright 2022 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, { + useState, + useCallback, + useContext, + createContext, + PropsWithChildren, +} from 'react'; + +type SecretsContextContents = { + secrets: Record; + setSecrets: React.Dispatch>>; +}; + +/** + * The actual context object. + */ +export const SecretsContext = createContext( + undefined, +); + +/** + * The Context Provider that holds the state for the secrets. + * + * @public + */ +export const SecretsContextProvider = ({ children }: PropsWithChildren<{}>) => { + const [secrets, setSecrets] = useState>({}); + + return ( + + {children} + + ); +}; + +/** + * Hook to access the secrets context. + * @public + */ +export const useTemplateSecrets = () => { + const value = useContext(SecretsContext); + if (!value) { + throw new Error( + 'useTemplateSecrets must be used within a SecretsContextProvider', + ); + } + + const { setSecrets } = value; + + const setSecret = useCallback( + (input: Record) => { + setSecrets(currentSecrets => ({ ...currentSecrets, ...input })); + }, + [setSecrets], + ); + + return { setSecret }; +}; diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/SecretsContext/index.ts b/plugins/scaffolder/src/next/TemplateWizardPage/SecretsContext/index.ts new file mode 100644 index 0000000000..65b530dea1 --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateWizardPage/SecretsContext/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2022 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 { + useTemplateSecrets, + SecretsContext, + SecretsContextProvider, +} from './SecretsContext'; diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx new file mode 100644 index 0000000000..94f592eb60 --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx @@ -0,0 +1,16 @@ +/* + * Copyright 2022 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 const TemplateWizardPage = () => {}; diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/index.ts b/plugins/scaffolder/src/next/TemplateWizardPage/index.ts new file mode 100644 index 0000000000..84ecf6b3d5 --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateWizardPage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 { TemplateWizardPage } from './TemplateWizardPage'; +export { useTemplateSecrets } from './SecretsContext';