// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import {render, screen, waitFor, within} from '@testing-library/react';
import {compareOptions, ConfigureScanPage} from "../components/resource-based-policies/ConfigureScanPage";
import {NotificationContext} from "../contexts/NotificationContext";
import {MemoryRouter} from "react-router-dom";
import userEvent from "@testing-library/user-event";
import {server} from "./mocks/server";
import {rest} from "msw";
import {newJob, sampleSelectionOptions} from "./mocks/handlers";
import {apiPathResourceBasedPolicies} from "../components/resource-based-policies/ResourceBasedPoliciesDefinitions";
it('compares options for dropdowns', async () => {
// ARRANGE
const usEast1 = {value: 'us-east-1'};
const usWest2 = {value: 'us-west-2'};
// ASSERT
expect(compareOptions(usEast1, usWest2)).toEqual(-1);
expect(compareOptions(usEast1, usEast1)).toEqual(0);
expect(compareOptions(usWest2, usEast1)).toEqual(1);
});
describe('the ConfigureScanPage', () => {
it('loads a stored config and populates the form', async () => {
// ARRANGE
server.use(
rest.get('scan-configs', (request, response, context) => {
return response(
context.status(200),
context.json(sampleSelectionOptions),
)
})
);
const setNotificationsMockFn = jest.fn();
render(
);
const selectConfigCard = screen.getByTitle('Select-Config');
const editConfigCard = screen.getByTitle('Edit-Config');
const loadConfigOption = within(selectConfigCard).getByRole('radio', {name: 'Load existing configuration'});
// ACT
await userEvent.click(loadConfigOption);
const selectConfigDropdown = await within(selectConfigCard).findByText("Choose saved configuration");
await userEvent.click(selectConfigDropdown);
const sampleConfigOption = await within(selectConfigCard).findByText("Sample-Config-US");
await userEvent.click(sampleConfigOption);
// ASSERT
const config = sampleSelectionOptions.SavedConfigurations[0];
expect(await within(editConfigCard).findByText(config.AccountIds[0])).toBeInTheDocument();
expect(await within(editConfigCard).findByText(config.Regions[0])).toBeInTheDocument();
expect(await within(editConfigCard).findByText(config.ServiceNames[0])).toBeInTheDocument();
});
describe('validating the config', () => {
it('succeeds with accountIds', async () => {
// ARRANGE
const activeJob = newJob('RESOURCE_BASED_POLICY');
activeJob.JobStatus = 'ACTIVE';
rest.post(apiPathResourceBasedPolicies, (request, response, context) => {
return response(
context.status(200),
context.json(activeJob)
)
})
const setNotificationsMockFn = jest.fn();
render(
);
const startScanButton = screen.getByRole('button', {name: 'Start Scan'});
const editConfigCard = screen.getByTitle('Edit-Config');
const accountSelectionStrategyOption = within(editConfigCard).getByRole('radio', {name: 'Accounts IDs specified below'});
await userEvent.click(accountSelectionStrategyOption);
const accountIdTextArea = await within(editConfigCard).findByPlaceholderText("111111222222,999999999999");
// ACT
await userEvent.click(accountIdTextArea);
await userEvent.type(accountIdTextArea, '111122223333,555555555555,\n444455556666');
await userEvent.click(startScanButton)
// ASSERT
await waitFor(() => {
expect(setNotificationsMockFn).toHaveBeenCalledWith([{
header: 'Scan started',
content: `Job with ID ${activeJob.JobId} in progress.`,
dismissible: true,
onDismiss: expect.anything()
}])
});
});
it('fails on invalid accountId', async () => {
// ARRANGE
const setNotificationsMockFn = jest.fn();
render(
);
const startScanButton = screen.getByRole('button', {name: 'Start Scan'});
const editConfigCard = screen.getByTitle('Edit-Config');
const accountSelectionStrategyOption = within(editConfigCard).getByRole('radio', {name: 'Accounts IDs specified below'});
await userEvent.click(accountSelectionStrategyOption);
const accountIdTextArea = await within(editConfigCard).findByPlaceholderText("111111222222,999999999999");
// ACT
await userEvent.click(accountIdTextArea);
await userEvent.type(accountIdTextArea, '111122223333,5555555555,\n44445555T666');
await userEvent.click(startScanButton)
// ASSERT
const errorText = "The following entries are not valid AWS account ids: 5555555555, 44445555T666";
const validationError = await within(editConfigCard).findByText(errorText);
expect(validationError).toBeInTheDocument()
});
it('succeeds with org unit ids', async () => {
// ARRANGE
const activeJob = newJob('RESOURCE_BASED_POLICY');
activeJob.JobStatus = 'ACTIVE';
rest.post(apiPathResourceBasedPolicies, (request, response, context) => {
return response(
context.status(200),
context.json(activeJob)
)
})
const setNotificationsMockFn = jest.fn();
render(
);
const startScanButton = screen.getByRole('button', {name: 'Start Scan'});
const editConfigCard = screen.getByTitle('Edit-Config');
const accountSelectionStrategyOption = within(editConfigCard).getByRole('radio', {name: 'Accounts in organizational units specified below'});
await userEvent.click(accountSelectionStrategyOption);
const orgUnitIdTextArea = await within(editConfigCard).findByPlaceholderText("ou-examplerootid111-exampleouid111,ou-examplerootid222-exampleouid222");
// ACT
await userEvent.click(orgUnitIdTextArea);
await userEvent.type(orgUnitIdTextArea, 'ou-1234-12345678, ou-1234-12345679');
await userEvent.click(startScanButton)
// ASSERT
await waitFor(() => {
expect(setNotificationsMockFn).toHaveBeenCalledWith([{
header: 'Scan started',
content: `Job with ID ${activeJob.JobId} in progress.`,
dismissible: true,
onDismiss: expect.anything()
}])
});
});
it('fails on invalid org unit id', async () => {
// ARRANGE
const setNotificationsMockFn = jest.fn();
render(
);
const startScanButton = screen.getByRole('button', {name: 'Start Scan'});
const editConfigCard = screen.getByTitle('Edit-Config');
const accountSelectionStrategyOption = within(editConfigCard).getByRole('radio', {name: 'Accounts in organizational units specified below'});
await userEvent.click(accountSelectionStrategyOption);
const orgUnitIdTextArea = await within(editConfigCard).findByPlaceholderText("ou-examplerootid111-exampleouid111,ou-examplerootid222-exampleouid222");
// ACT
await userEvent.click(orgUnitIdTextArea);
await userEvent.type(orgUnitIdTextArea, 'ou-abcd, ou-efgh');
await userEvent.click(startScanButton)
// ASSERT
const errorText = "The following entries are not valid organizational unit ids: ou-abcd, ou-efgh";
const validationError = await within(editConfigCard).findByText(errorText);
expect(validationError).toBeInTheDocument()
});
});
describe('clicking the scan button', () => {
it('starts a scan', async () => {
// ARRANGE
const activeJob = newJob('RESOURCE_BASED_POLICY');
activeJob.JobStatus = 'ACTIVE';
rest.post(apiPathResourceBasedPolicies, (request, response, context) => {
return response(
context.status(200),
context.json(activeJob)
)
})
const setNotificationsMockFn = jest.fn();
render(
);
const startScanButton = screen.getByRole('button', {name: 'Start Scan'});
// ACT
await userEvent.click(startScanButton)
// ASSERT
await waitFor(() => {
expect(setNotificationsMockFn).toHaveBeenCalledWith([{
header: 'Scan started',
content: `Job with ID ${activeJob.JobId} in progress.`,
dismissible: true,
onDismiss: expect.anything()
}])
});
});
it('renders an error message on failed scan', async () => {
// ARRANGE
const activeJob = newJob('RESOURCE_BASED_POLICY');
activeJob.JobStatus = 'FAILED';
server.use(
rest.post(apiPathResourceBasedPolicies, (request, response, context) => {
return response(
context.status(200),
context.json(activeJob)
)
})
)
const setNotificationsMockFn = jest.fn();
render(
);
const startScanButton = screen.getByRole('button', {name: 'Start Scan'});
// ACT
await userEvent.click(startScanButton)
// ASSERT
await waitFor(() => {
expect(setNotificationsMockFn).toHaveBeenCalledWith([{
header: 'Scan failed',
content: `Job with ID ${activeJob.JobId} failed. For details please check the Cloudwatch Logs.`,
type: 'error',
dismissible: true,
onDismiss: expect.anything()
}])
})
});
it('renders an error message on failed http response', async () => {
// ARRANGE
const activeJob = newJob('RESOURCE_BASED_POLICY');
activeJob.JobStatus = 'FAILED';
server.use(
rest.post(apiPathResourceBasedPolicies, (request, response, context) => {
return response(
context.status(400),
context.json({
Error: 'Region not supported',
Message: 'The following of your requested regions are not supported: us-east-1',
})
)
})
)
const setNotificationsMockFn = jest.fn();
render(
);
const startScanButton = screen.getByRole('button', {name: 'Start Scan'});
// ACT
await userEvent.click(startScanButton)
// ASSERT
await waitFor(() => {
expect(setNotificationsMockFn).toHaveBeenCalledWith([{
header: 'Region not supported',
content: `The following of your requested regions are not supported: us-east-1`,
type: 'error',
dismissible: true,
onDismiss: expect.anything()
}])
})
});
})
});