// 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() }]) }) }); }) });