diff --git a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx
index 9d3f29fe7b..70d2193470 100644
--- a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx
+++ b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx
@@ -16,10 +16,20 @@
import { OngoingTask } from './OngoingTask';
import React from 'react';
-import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
+import {
+ renderInTestApp,
+ TestApiProvider,
+ MockPermissionApi,
+} from '@backstage/test-utils';
import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
import { act, fireEvent, waitFor, within } from '@testing-library/react';
+import {
+ PermissionApi,
+ permissionApiRef,
+} from '@backstage/plugin-permission-react';
import { rootRouteRef } from '../../routes';
+import { AuthorizeResult } from '@backstage/plugin-permission-common';
+import { SWRConfig } from 'swr';
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
@@ -49,18 +59,29 @@ describe('OngoingTask', () => {
getTask: jest.fn().mockImplementation(async () => {}),
};
- beforeEach(() => {
+ beforeEach(async () => {
jest.clearAllMocks();
});
- it('should trigger cancel api on "Cancel" click in context menu', async () => {
- const cancelOptionLabel = 'Cancel';
- const rendered = await renderInTestApp(
-
-
- ,
+ const render = (permissionApi?: PermissionApi) => {
+ // SWR used by the usePermission hook needs cache to be reset for each test
+ return renderInTestApp(
+ new Map() }}>
+
+
+
+ ,
{ mountedRoutes: { '/': rootRouteRef } },
);
+ };
+ it('should trigger cancel api on "Cancel" click in context menu', async () => {
+ const rendered = await render();
+ const cancelOptionLabel = 'Cancel';
const { getByTestId } = rendered;
await act(async () => {
@@ -84,13 +105,9 @@ describe('OngoingTask', () => {
});
it('should trigger cancel api on "Cancel" button click', async () => {
+ const rendered = await render();
const cancelOptionLabel = 'Cancel';
- const rendered = await renderInTestApp(
-
-
- ,
- { mountedRoutes: { '/': rootRouteRef } },
- );
+
const { getByTestId } = rendered;
await act(async () => {
@@ -114,22 +131,12 @@ describe('OngoingTask', () => {
});
it('should initially do not display logs', async () => {
- const rendered = await renderInTestApp(
-
-
- ,
- { mountedRoutes: { '/': rootRouteRef } },
- );
+ const rendered = await render();
await expect(rendered.findByText('Show Logs')).resolves.toBeInTheDocument();
});
it('should toggle logs visibility', async () => {
- const rendered = await renderInTestApp(
-
-
- ,
- { mountedRoutes: { '/': rootRouteRef } },
- );
+ const rendered = await render();
await act(async () => {
const element = await rendered.findByText('Show Logs');
fireEvent.click(element);
@@ -137,4 +144,22 @@ describe('OngoingTask', () => {
await expect(rendered.findByText('Hide Logs')).resolves.toBeInTheDocument();
});
+
+ it('should have cancel and start over buttons be disabled without the proper permissions', async () => {
+ const mockAuthorize = jest
+ .fn()
+ .mockImplementation(async () => ({ result: AuthorizeResult.DENY }));
+ const permissionApi: PermissionApi = { authorize: mockAuthorize };
+ const rendered = await render(permissionApi);
+
+ const { getByTestId } = rendered;
+ expect(getByTestId('cancel-button')).toHaveClass('Mui-disabled');
+ expect(getByTestId('start-over-button')).toHaveClass('Mui-disabled');
+
+ await act(async () => {
+ fireEvent.click(getByTestId('menu-button'));
+ });
+ expect(getByTestId('cancel-task')).toHaveClass('Mui-disabled');
+ expect(getByTestId('start-over-task')).toHaveClass('Mui-disabled');
+ });
});
diff --git a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx
index 3e7a3b8f52..d5f6dcfb0d 100644
--- a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx
+++ b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx
@@ -35,6 +35,12 @@ import {
TaskSteps,
} from '@backstage/plugin-scaffolder-react/alpha';
import { useAsync } from '@react-hookz/web';
+import { usePermission } from '@backstage/plugin-permission-react';
+import {
+ taskCancelPermission,
+ taskReadPermission,
+ taskCreatePermission,
+} from '@backstage/plugin-scaffolder-common/alpha';
const useStyles = makeStyles(theme => ({
contentWrapper: {
@@ -81,6 +87,28 @@ export const OngoingTask = (props: {
const [logsVisible, setLogVisibleState] = useState(false);
const [buttonBarVisible, setButtonBarVisibleState] = useState(true);
+ // Used dummy string value for `resourceRef` since `allowed` field will always return `false` if `resourceRef` is `undefined`
+ const { allowed: canCancelTask } = usePermission({
+ permission: taskCancelPermission,
+ resourceRef: 'task',
+ });
+
+ const { allowed: canReadTask } = usePermission({
+ permission: taskReadPermission,
+ resourceRef: 'task',
+ });
+
+ const { allowed: canCreateTask } = usePermission({
+ permission: taskCreatePermission,
+ resourceRef: 'task',
+ });
+
+ // Cancel endpoint requires user to have both read and cancel permissions
+ const cancelNotAllowed = !(canReadTask && canCancelTask);
+
+ // Start Over endpoint requires user to have both read (to grab parameters) and create (to create new task) permissions
+ const canStartOver = canReadTask && canCreateTask;
+
useEffect(() => {
if (taskStream.error) {
setLogVisibleState(true);
@@ -192,7 +220,11 @@ export const OngoingTask = (props: {