import React from 'react'; import '@testing-library/jest-dom'; import { screen, waitFor, waitForElementToBeRemoved, within, } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { rest } from 'msw'; import CreateImageWizard from '../../../Components/CreateImageWizard/CreateImageWizard'; import ShareImageModal from '../../../Components/ShareImageModal/ShareImageModal'; import { PROVISIONING_API } from '../../../constants.js'; import { server } from '../../mocks/server.js'; import { clickBack, clickNext, getNextButton, renderCustomRoutesWithReduxRouter, verifyCancelButton, } from '../../testUtils'; const routes = [ { path: 'insights/image-builder/*', element:
, }, { path: 'insights/image-builder/imagewizard/:composeId?', element: , }, { path: 'insights/image-builder/share /:composeId', element: , }, ]; let store = undefined; let router = undefined; // Mocking getComposes is necessary because in many tests we call navigate() // to navigate to the images table (via useNavigate hook), which will in turn // result in a call to getComposes. If it is not mocked, tests fail due to MSW // being unable to resolve that endpoint. // jest // .spyOn(api, 'getComposes') // .mockImplementation(() => Promise.resolve(mockComposesEmpty)); jest.mock('@redhat-cloud-services/frontend-components/useChrome', () => ({ useChrome: () => ({ auth: { getUser: () => { return { identity: { internal: { org_id: 5, }, }, }; }, }, isBeta: () => false, isProd: () => true, getEnvironment: () => 'prod', }), })); jest.mock('@unleash/proxy-client-react', () => ({ useUnleashContext: () => jest.fn(), useFlag: jest.fn((flag) => flag === 'image-builder.enable-content-sources' ? true : false ), })); const searchForAvailablePackages = async (searchbox, searchTerm) => { const user = userEvent.setup(); await user.type(searchbox, searchTerm); user.click( await screen.findByRole('button', { name: /search button for available packages/i, }) ); }; const switchToAWSManual = async () => { const user = userEvent.setup(); const manualRadio = screen.getByRole('radio', { name: /manually enter an account id\./i, }); await user.click(manualRadio); return manualRadio; }; const getSourceDropdown = async () => { const sourceDropdown = screen.getByRole('textbox', { name: /select source/i, }); await waitFor(() => expect(sourceDropdown).toBeEnabled()); return sourceDropdown; }; beforeAll(() => { // scrollTo is not defined in jsdom window.HTMLElement.prototype.scrollTo = function () {}; }); afterEach(() => { jest.clearAllMocks(); router = undefined; server.resetHandlers(); }); describe('Create Image Wizard', () => { test('renders component', () => { renderCustomRoutesWithReduxRouter('imagewizard', {}, routes); // check heading screen.getByRole('heading', { name: /Image Builder/ }); screen.getByRole('button', { name: 'Image output' }); screen.getByRole('button', { name: 'Register' }); screen.getByRole('button', { name: 'File system configuration' }); screen.getByRole('button', { name: 'Content' }); screen.getByRole('button', { name: 'Additional Red Hat packages' }); screen.getByRole('button', { name: 'Custom repositories' }); screen.getByRole('button', { name: 'Details' }); screen.getByRole('button', { name: 'Review' }); }); }); describe('Step Image output', () => { const user = userEvent.setup(); const setUp = async () => { ({ router } = await renderCustomRoutesWithReduxRouter( 'imagewizard', {}, routes )); // select aws as upload destination await user.click(await screen.findByTestId('upload-aws')); expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent( 'Image output' ); }; test('clicking Next loads Upload to AWS', async () => { await setUp(); await clickNext(); await switchToAWSManual(); await screen.findByText('AWS account ID'); }); test('clicking Cancel loads landing page', async () => { await setUp(); await clickNext(); await verifyCancelButton(router); }); test('target environment is required', async () => { await setUp(); const destination = screen.getByTestId('target-select'); const required = within(destination).getByText('*'); expect(destination).toBeEnabled(); expect(destination).toContainElement(required); }); test('selecting and deselecting a tile disables the next button', async () => { await setUp(); const nextButton = await getNextButton(); const awsTile = screen.getByTestId('upload-aws'); // this has already been clicked once in the setup function user.click(awsTile); // deselect const googleTile = screen.getByTestId('upload-google'); user.click(googleTile); // select user.click(googleTile); // deselect const azureTile = screen.getByTestId('upload-azure'); user.click(azureTile); // select user.click(azureTile); // deselect await waitFor(() => expect(nextButton).toBeDisabled()); }); test('expect only RHEL releases before expansion', async () => { await setUp(); const releaseMenu = screen.getByRole('button', { name: /options menu/i, }); await user.click(releaseMenu); await screen.findByRole('option', { name: 'Red Hat Enterprise Linux (RHEL) 8', }); await screen.findByRole('option', { name: 'Red Hat Enterprise Linux (RHEL) 9', }); await screen.findByRole('button', { name: 'Show options for further development of RHEL', }); await user.click(releaseMenu); }); test('expect all releases after expansion', async () => { await setUp(); const releaseMenu = screen.getByRole('button', { name: /options menu/i, }); await user.click(releaseMenu); const showOptionsButton = screen.getByRole('button', { name: 'Show options for further development of RHEL', }); await user.click(showOptionsButton); await screen.findByRole('option', { name: 'Red Hat Enterprise Linux (RHEL) 8', }); await screen.findByRole('option', { name: 'Red Hat Enterprise Linux (RHEL) 9', }); await screen.findByRole('option', { name: 'CentOS Stream 8', }); await screen.findByRole('option', { name: 'CentOS Stream 9', }); expect(showOptionsButton).not.toBeInTheDocument(); await user.click(releaseMenu); }); test('CentOS acknowledgement appears', async () => { await setUp(); const releaseMenu = screen.getByRole('button', { name: /options menu/i, }); await user.click(releaseMenu); const showOptionsButton = screen.getByRole('button', { name: 'Show options for further development of RHEL', }); await user.click(showOptionsButton); const centOSButton = screen.getByRole('option', { name: 'CentOS Stream 9', }); await user.click(centOSButton); await screen.findByText( 'CentOS Stream builds are intended for the development of future versions of RHEL and are not supported for production workloads or other use cases.' ); }); }); describe('Step Upload to AWS', () => { const user = userEvent.setup(); const setUp = async () => { ({ router, store } = await renderCustomRoutesWithReduxRouter( 'imagewizard', {}, routes )); // select aws as upload destination user.click(screen.getByTestId('upload-aws')); await clickNext(); expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent( 'Target environment - Amazon Web Services' ); }; test('clicking Next loads Registration', async () => { await setUp(); await switchToAWSManual(); await user.type( await screen.findByTestId('aws-account-id'), '012345678901' ); await clickNext(); await screen.findByRole('textbox', { name: 'Select activation key', }); screen.getByText('Automatically register and enable advanced capabilities'); }); test('clicking Back loads Release', async () => { await setUp(); await clickBack(); screen.getByTestId('upload-aws'); }); test('clicking Cancel loads landing page', async () => { await setUp(); await verifyCancelButton(router); }); test('component renders error state correctly', async () => { server.use( rest.get(`${PROVISIONING_API}/sources`, (req, res, ctx) => res(ctx.status(500)) ) ); await setUp(); await screen.findByText( /sources cannot be reached, try again later or enter an aws account id manually\./i ); }); test('validation works', async () => { await setUp(); const nextButton = await getNextButton(); // jsdom seems to render the next button differently than the browser. The // next button is enabled briefly during the test. This does not occur in // the browser. Using findByRole instead of getByRole to get the next // button allows us to capture its 'final' state. expect(nextButton).toHaveClass('pf-m-disabled'); await user.click( screen.getByRole('radio', { name: /manually enter an account id\./i }) ); expect(nextButton).toHaveClass('pf-m-disabled'); const awsAccId = screen.getByTestId('aws-account-id'); expect(awsAccId).toHaveValue(''); expect(awsAccId).toBeEnabled(); await user.type(awsAccId, '012345678901'); expect(nextButton).not.toHaveClass('pf-m-disabled'); user.click( screen.getByRole('radio', { name: /use an account configured from sources\./i, }) ); await waitFor(() => expect(nextButton).toHaveClass('pf-m-disabled')); const sourceDropdown = await getSourceDropdown(); user.click(sourceDropdown); const source = await screen.findByRole('option', { name: /my_source/i, }); user.click(source); await waitFor(() => expect(nextButton).not.toHaveClass('pf-m-disabled')); }); test('compose request share_with_sources field is correct', async () => { await setUp(); const sourceDropdown = await getSourceDropdown(); user.click(sourceDropdown); const source = await screen.findByRole('option', { name: /my_source/i, }); user.click(source); await clickNext(); // registration await screen.findByRole('textbox', { name: 'Select activation key', }); const registerLaterRadio = screen.getByLabelText('Register later'); await user.click(registerLaterRadio); // click through to review step await clickNext(); await clickNext(); await clickNext(); await clickNext(); await clickNext(); // const composeImage = jest // .spyOn(api, 'composeImage') // .mockImplementation((body) => { // expect(body).toEqual({ // distribution: RHEL_9, // image_name: undefined, // customizations: { // packages: undefined, // }, // image_requests: [ // { // architecture: 'x86_64', // image_type: 'aws', // upload_request: { // type: 'aws', // options: { // share_with_sources: ['123'], // }, // }, // }, // ], // }); // const id = 'edbae1c2-62bc-42c1-ae0c-3110ab718f5a'; // return Promise.resolve({ id }); // }); user.click(screen.getByRole('button', { name: /Create/ })); // returns back to the landing page await waitFor(() => expect(router.state.location.pathname).toBe('/insights/image-builder') ); // set test timeout of 10 seconds }, 10000); }); describe('Step Upload to Google', () => { const user = userEvent.setup(); const setUp = async () => { ({ router } = await renderCustomRoutesWithReduxRouter( 'imagewizard', {}, routes )); // select gcp as upload destination user.click(screen.getByTestId('upload-google')); await clickNext(); expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent( 'Target environment - Google Cloud Platform' ); }; test('clicking Next loads Registration', async () => { await setUp(); const shareRadioButton = await screen.findByRole('radio', { name: /share image with a google account/i, }); await user.click(shareRadioButton); const googleEmailInput = await screen.findByTestId('input-google-email'); await user.type(googleEmailInput, 'test@test.com'); await clickNext(); await screen.findByRole('textbox', { name: 'Select activation key', }); screen.getByText('Automatically register and enable advanced capabilities'); }); test('clicking Back loads Release', async () => { await setUp(); await clickBack(); screen.getByTestId('upload-google'); }); test('clicking Cancel loads landing page', async () => { await setUp(); await verifyCancelButton(router); }); test('the google account id field is shown and required', async () => { await setUp(); await waitFor(() => { screen.getByTestId('account-sharing'); }); user.click(screen.getByTestId('account-sharing')); const accessKeyId = await screen.findByTestId('input-google-email'); expect(accessKeyId).toHaveValue(''); expect(accessKeyId).toBeEnabled(); // expect(accessKeyId).toBeRequired(); // DDf does not support required value }); test('the google email field must be a valid email', async () => { await setUp(); await user.click(screen.getByTestId('account-sharing')); await user.type(screen.getByTestId('input-google-email'), 'a'); expect(await getNextButton()).toHaveClass('pf-m-disabled'); expect(await getNextButton()).toBeDisabled(); await user.type(screen.getByTestId('input-google-email'), 'test@test.com'); expect(await getNextButton()).not.toHaveClass('pf-m-disabled'); expect(await getNextButton()).toBeEnabled(); }); }); describe('Step Registration', () => { const user = userEvent.setup(); const setUp = async () => { ({ router } = await renderCustomRoutesWithReduxRouter( 'imagewizard', {}, routes )); // select aws as upload destination user.click(screen.getByTestId('upload-aws')); await clickNext(); await user.click( screen.getByRole('radio', { name: /manually enter an account id\./i }) ); await user.type(screen.getByTestId('aws-account-id'), '012345678901'); await clickNext(); await screen.findByRole('textbox', { name: 'Select activation key', }); }; test('clicking Next loads file system configuration', async () => { await setUp(); const registerLaterRadio = screen.getByTestId('registration-radio-later'); await user.click(registerLaterRadio); await clickNext(); screen.getByRole('heading', { name: /file system configuration/i }); }); test('clicking Back loads Upload to AWS', async () => { await setUp(); await clickBack(); await user.click( screen.getByRole('radio', { name: /manually enter an account id\./i }) ); screen.getByText('AWS account ID'); }); test('clicking Cancel loads landing page', async () => { await setUp(); await verifyCancelButton(router); }); test('should allow registering with rhc', async () => { await setUp(); const activationKeyDropdown = await screen.findByRole('textbox', { name: 'Select activation key', }); await user.click(activationKeyDropdown); const activationKey = await screen.findByRole('option', { name: 'name0', }); await user.click(activationKey); screen.getByDisplayValue('name0'); await clickNext(); await clickNext(); await clickNext(); await clickNext(); await clickNext(); const review = screen.getByTestId('review-registration'); expect(review).toHaveTextContent( 'Register with Red Hat Subscription Manager (RHSM)' ); expect(review).toHaveTextContent('Connect to Red Hat Insights'); expect(review).toHaveTextContent( 'Use remote host configuration (rhc) utility' ); screen.getAllByText('012345678901'); }); test('should allow registering without rhc', async () => { await setUp(); await user.click(screen.getByTestId('registration-additional-options')); await user.click(screen.getByTestId('registration-checkbox-rhc')); // going back and forward when rhc isn't selected should keep additional options shown await clickBack(); await screen.findByTestId('aws-account-id'); await clickNext(); screen.getByTestId('registration-checkbox-insights'); screen.getByTestId('registration-checkbox-rhc'); const activationKeyDropdown = await screen.findByRole('textbox', { name: 'Select activation key', }); await user.click(activationKeyDropdown); const activationKey = await screen.findByRole('option', { name: 'name0', }); await user.click(activationKey); screen.getByDisplayValue('name0'); await clickNext(); await clickNext(); await clickNext(); await clickNext(); await clickNext(); const review = screen.getByTestId('review-registration'); expect(review).toHaveTextContent( 'Register with Red Hat Subscription Manager (RHSM)' ); expect(review).toHaveTextContent('Connect to Red Hat Insights'); screen.getAllByText('012345678901'); expect(review).not.toHaveTextContent( 'Use remote host configuration (rhc) utility' ); }); test('should allow registering without insights or rhc', async () => { await setUp(); await user.click(screen.getByTestId('registration-additional-options')); await user.click(screen.getByTestId('registration-checkbox-insights')); // going back and forward when neither rhc or insights is selected should keep additional options shown await clickBack(); await screen.findByTestId('aws-account-id'); await clickNext(); screen.getByTestId('registration-checkbox-insights'); screen.getByTestId('registration-checkbox-rhc'); const activationKeyDropdown = await screen.findByRole('textbox', { name: 'Select activation key', }); await user.click(activationKeyDropdown); const activationKey = await screen.findByRole('option', { name: 'name0', }); await user.click(activationKey); screen.getByDisplayValue('name0'); await clickNext(); await clickNext(); await clickNext(); await clickNext(); await clickNext(); const review = screen.getByTestId('review-registration'); expect(review).toHaveTextContent( 'Register with Red Hat Subscription Manager (RHSM)' ); screen.getAllByText('012345678901'); expect(review).not.toHaveTextContent('Connect to Red Hat Insights'); expect(review).not.toHaveTextContent( 'Use remote host configuration (rhc) utility' ); }); test('should hide input fields when clicking Register the system later', async () => { await setUp(); const removeKeyInformation = waitForElementToBeRemoved(() => [ screen.getByTestId('subscription-activation-key'), ]); // click the later radio button which should remove any input fields user.click(screen.getByTestId('registration-radio-later')); await removeKeyInformation; await clickNext(); await clickNext(); await clickNext(); await clickNext(); await clickNext(); screen.getByText('Register the system later'); }); test('registering with rhc implies registering with insights', async () => { await setUp(); await user.click(screen.getByTestId('registration-additional-options')); await user.click(screen.getByTestId('registration-checkbox-insights')); expect(screen.getByTestId('registration-checkbox-rhc')).not.toBeChecked(); await user.click(screen.getByTestId('registration-checkbox-rhc')); expect(screen.getByTestId('registration-checkbox-insights')).toBeChecked(); }); }); describe('Step File system configuration', () => { const user = userEvent.setup(); const setUp = async () => { ({ router } = await renderCustomRoutesWithReduxRouter( 'imagewizard', {}, routes )); // select aws as upload destination user.click(screen.getByTestId('upload-aws')); await clickNext(); // aws step await switchToAWSManual(); await user.type( await screen.findByTestId('aws-account-id'), '012345678901' ); await clickNext(); // skip registration await screen.findByRole('textbox', { name: 'Select activation key', }); const registerLaterRadio = screen.getByTestId('registration-radio-later'); await user.click(registerLaterRadio); await clickNext(); }; test('Error validation occurs upon clicking next button', async () => { await setUp(); const manuallyConfigurePartitions = screen.getByText( /manually configure partitions/i ); user.click(manuallyConfigurePartitions); const addPartition = await screen.findByTestId('file-system-add-partition'); // Create duplicate partitions user.click(addPartition); user.click(addPartition); expect(await getNextButton()).toBeEnabled(); // Clicking next causes errors to appear await clickNext(); const mountPointWarning = screen.getByRole('heading', { name: /danger alert: duplicate mount points: all mount points must be unique\. remove the duplicate or choose a new mount point\./i, hidden: true, }); const mountPointAlerts = screen.getAllByRole('heading', { name: /danger alert: duplicate mount point\./i, }); const tbody = screen.getByTestId('file-system-configuration-tbody'); const rows = within(tbody).getAllByRole('row'); expect(rows).toHaveLength(3); // Change mountpoint of final row to /var, resolving errors const mountPointOptions = within(rows[2]).getAllByRole('button', { name: 'Options menu', })[0]; user.click(mountPointOptions); const varButton = await within(rows[2]).findByRole('option', { name: '/var', }); user.click(varButton); await waitFor(() => expect(mountPointWarning).not.toBeInTheDocument()); await waitFor(() => expect(mountPointAlerts[0]).not.toBeInTheDocument()); await waitFor(() => expect(mountPointAlerts[1]).not.toBeInTheDocument()); expect(await getNextButton()).toBeEnabled(); }); }); describe('Step Details', () => { const user = userEvent.setup(); const setUp = async () => { ({ router } = await renderCustomRoutesWithReduxRouter( 'imagewizard', {}, routes )); // select aws as upload destination user.click(screen.getByTestId('upload-aws')); await clickNext(); // aws step await switchToAWSManual(); await user.type( await screen.findByTestId('aws-account-id'), '012345678901' ); await clickNext(); // skip registration await screen.findByRole('textbox', { name: 'Select activation key', }); const registerLaterRadio = screen.getByTestId('registration-radio-later'); await user.click(registerLaterRadio); await clickNext(); // skip fsc await clickNext(); // skip packages await clickNext(); // skip repositories await clickNext(); }; test('image name invalid for more than 63 chars', async () => { await setUp(); // Enter image name const nameInput = screen.getByRole('textbox', { name: 'Image Name', }); // 101 character name const invalidName = 'a'.repeat(64); await user.type(nameInput, invalidName); expect(await getNextButton()).toHaveClass('pf-m-disabled'); expect(await getNextButton()).toBeDisabled(); await user.clear(nameInput); await user.type(nameInput, 'valid-name'); expect(await getNextButton()).not.toHaveClass('pf-m-disabled'); expect(await getNextButton()).toBeEnabled(); // Enter description image const descriptionInput = screen.getByRole('textbox', { name: /description/i, }); const invalidDescription = 'a'.repeat(251); await user.type(descriptionInput, invalidDescription); expect(await getNextButton()).toHaveClass('pf-m-disabled'); expect(await getNextButton()).toBeDisabled(); await user.clear(descriptionInput); await user.type(descriptionInput, 'valid-description'); expect(await getNextButton()).not.toHaveClass('pf-m-disabled'); expect(await getNextButton()).toBeEnabled(); }); }); describe('Step Review', () => { const user = userEvent.setup(); const setUp = async () => { ({ router } = await renderCustomRoutesWithReduxRouter( 'imagewizard', {}, routes )); // select aws as upload destination user.click(screen.getByTestId('upload-aws')); await clickNext(); // aws step await switchToAWSManual(); await user.type( await screen.findByTestId('aws-account-id'), '012345678901' ); await clickNext(); await screen.findByRole('textbox', { name: 'Select activation key', }); // skip registration const registerLaterRadio = screen.getByTestId('registration-radio-later'); await user.click(registerLaterRadio); await clickNext(); // skip fsc await clickNext(); // skip packages await clickNext(); // skip repositories await clickNext(); // skip name await clickNext(); }; // eslint-disable-next-line no-unused-vars const setUpCentOS = async () => { ({ router } = renderCustomRoutesWithReduxRouter('imagewizard', {}, routes)); const releaseMenu = screen.getByRole('button', { name: /options menu/i, }); await user.click(releaseMenu); const showOptionsButton = screen.getByRole('button', { name: 'Show options for further development of RHEL', }); await user.click(showOptionsButton); const centos = screen.getByRole('option', { name: 'CentOS Stream 8', }); await user.click(centos); // select aws as upload destination user.click(await screen.findByTestId('upload-aws')); await clickNext(); // aws step await switchToAWSManual(); await user.type( await screen.findByTestId('aws-account-id'), '012345678901' ); await clickNext(); // skip fsc await clickNext(); // skip packages await clickNext(); // skip repositories await clickNext(); // skip name await clickNext(); }; test('has 3 buttons', async () => { await setUp(); screen.getByRole('button', { name: /Create/ }); screen.getByRole('button', { name: /Back/ }); screen.getByRole('button', { name: /Cancel/ }); }); test('clicking Back loads Image name', async () => { await setUp(); await clickBack(); screen.getByRole('heading', { name: 'Details', }); }); test('clicking Cancel loads landing page', async () => { await setUp(); await verifyCancelButton(router); }); test('has Registration expandable section for rhel', async () => { await setUp(); const targetExpandable = screen.getByTestId( 'target-environments-expandable' ); const registrationExpandable = screen.getByTestId( 'registration-expandable' ); const contentExpandable = screen.getByTestId('content-expandable'); const fscExpandable = screen.getByTestId( 'file-system-configuration-expandable' ); await user.click(targetExpandable); screen.getByText('AWS'); await user.click(registrationExpandable); screen.getByText('Register the system later'); await user.click(contentExpandable); screen.getByText('Additional Red Hatand 3rd party packages'); await user.click(fscExpandable); screen.getByText('Configuration type'); }); test('has no Registration expandable for centos', async () => { await setUpCentOS(); const targetExpandable = await screen.findByTestId( 'target-environments-expandable' ); const contentExpandable = await screen.findByTestId('content-expandable'); const fscExpandable = await screen.findByTestId( 'file-system-configuration-expandable' ); expect( screen.queryByTestId('registration-expandable') ).not.toBeInTheDocument(); await user.click(targetExpandable); screen.getByText('AWS'); await user.click(contentExpandable); screen.getByText('Additional Red Hatand 3rd party packages'); await user.click(fscExpandable); screen.getByText('Configuration type'); }); }); describe('Click through all steps', () => { const user = userEvent.setup(); const setUp = async () => { ({ router, store } = await renderCustomRoutesWithReduxRouter( 'imagewizard', {}, routes )); }; test('with valid values', async () => { await setUp(); // select image output const releaseMenu = screen.getByRole('button', { name: /options menu/i, }); await user.click(releaseMenu); const releaseOption = screen.getByRole('option', { name: 'Red Hat Enterprise Linux (RHEL) 8', }); await user.click(releaseOption); await user.click(screen.getByTestId('upload-aws')); await user.click(screen.getByTestId('upload-azure')); await user.click(screen.getByTestId('upload-google')); await user.click(screen.getByTestId('checkbox-vmware')); await user.click(screen.getByTestId('checkbox-guest-image')); await user.click(screen.getByTestId('checkbox-image-installer')); await clickNext(); await user.click( screen.getByRole('radio', { name: /manually enter an account id\./i }) ); await user.type(screen.getByTestId('aws-account-id'), '012345678901'); await clickNext(); await user.click(screen.getByTestId('account-sharing')); await user.type(screen.getByTestId('input-google-email'), 'test@test.com'); await user.click(await screen.findByTestId('image-sharing')); await clickNext(); await user.click(screen.getByTestId('azure-radio-manual')); // Randomly generated GUID await user.type( screen.getByTestId('azure-tenant-id-manual'), 'b8f86d22-4371-46ce-95e7-65c415f3b1e2' ); await user.type( screen.getByTestId('azure-subscription-id-manual'), '60631143-a7dc-4d15-988b-ba83f3c99711' ); await user.type( screen.getByTestId('azure-resource-group-manual'), 'testResourceGroup' ); await clickNext(); // registration const activationKeyDropdown = await screen.findByRole('textbox', { name: 'Select activation key', }); await user.click(activationKeyDropdown); const activationKey = await screen.findByRole('option', { name: 'name0', }); await user.click(activationKey); screen.getByDisplayValue('name0'); await clickNext(); // fsc const fscToggle = await screen.findByTestId( 'file-system-config-radio-manual' ); await user.click(fscToggle); const addPartition = await screen.findByTestId('file-system-add-partition'); await user.click(addPartition); await user.click(addPartition); const tbody = screen.getByTestId('file-system-configuration-tbody'); const rows = within(tbody).getAllByRole('row'); await waitFor(() => expect(rows).toHaveLength(3)); await clickNext(); // set mountpoint of final row to /var/tmp const mountPointMenu = within(rows[2]).getAllByRole('button', { name: 'Options menu', })[0]; user.click(mountPointMenu); const varButton = await within(rows[2]).findByRole('option', { name: '/var', }); user.click(varButton); await waitForElementToBeRemoved(() => screen.queryAllByRole('heading', { name: 'Danger alert: Duplicate mount point.', }) ); await user.type( within(rows[2]).getByRole('textbox', { name: 'Mount point suffix text input', }), '/tmp' ); // set size of the final row to 100 MiB await user.type( within(rows[2]).getByRole('textbox', { name: 'Size text input' }), '{backspace}100' ); const unitMenu = within(rows[2]).getAllByRole('button', { name: 'Options menu', })[1]; user.click(unitMenu); const mibButton = await within(rows[2]).findByRole('option', { name: 'MiB', }); user.click(mibButton); await clickNext(); screen.getByText( /Images built with Image Builder include all required packages/i ); const searchbox = screen.getAllByRole('textbox')[0]; // searching by id doesn't update the input ref await waitFor(() => expect(searchbox).toBeEnabled()); await searchForAvailablePackages(searchbox, 'test'); user.click( await screen.findByRole('option', { name: /test summary for test package/, }) ); user.click(screen.getByRole('button', { name: /Add selected/ })); await clickNext(); // Custom repositories await user.click( await screen.findByRole('checkbox', { name: /select row 0/i }) ); await user.click( await screen.findByRole('checkbox', { name: /select row 1/i }) ); await clickNext(); // Custom packages await clickNext(); // Enter image name const nameInput = screen.getByRole('textbox', { name: 'Image Name', }); await user.type(nameInput, 'my-image-name'); // Enter description for image const descriptionInput = screen.getByRole('textbox', { name: /Description/, }); await user.type( descriptionInput, 'this is a perfect description for image' ); await clickNext(); // review const targetEnvironmentsExpandable = await screen.findByTestId( 'target-environments-expandable' ); user.click(targetEnvironmentsExpandable); await screen.findAllByText('AWS'); await screen.findAllByText('GCP'); await screen.findByText('VMWare vSphere (.ova)'); await screen.findByText('Virtualization - Guest image (.qcow2)'); await screen.findByText('Bare metal - Installer (.iso)'); const registrationExpandable = await screen.findByTestId( 'registration-expandable' ); user.click(registrationExpandable); const review = screen.getByTestId('review-registration'); expect(review).toHaveTextContent( 'Use remote host configuration (rhc) utility' ); const imageDetailsExpandable = await screen.findByTestId( 'image-details-expandable' ); user.click(imageDetailsExpandable); await screen.findByText('my-image-name'); await screen.findByText('this is a perfect description for image'); await screen.findByText('name0'); await screen.findByText('Self-Support'); await screen.findByText('Production'); user.click(screen.getByTestId('repositories-popover-button')); const repotbody = await screen.findByTestId( 'additional-repositories-table' ); expect(within(repotbody).getAllByRole('row')).toHaveLength(3); user.click(screen.getByTestId('file-system-configuration-popover')); const revtbody = await screen.findByTestId( 'file-system-configuration-tbody-review' ); expect(within(revtbody).getAllByRole('row')).toHaveLength(3); // // mock the backend API // const ids = []; // const composeImage = jest // .spyOn(api, 'composeImage') // .mockImplementation((body) => { // let id; // let expectedbody = {}; // if (body.image_requests[0].upload_request.type === 'aws') { // expectedbody = { // distribution: RHEL_8, // image_name: 'my-image-name', // image_description: 'this is a perfect description for image', // image_requests: [ // { // architecture: 'x86_64', // image_type: 'aws', // upload_request: { // type: 'aws', // options: { // share_with_accounts: ['012345678901'], // }, // }, // }, // ], // customizations: customizations, // }; // id = 'edbae1c2-62bc-42c1-ae0c-3110ab718f56'; // } else if (body.image_requests[0].upload_request.type === 'gcp') { // expectedbody = { // distribution: RHEL_8, // image_name: 'my-image-name', // image_description: 'this is a perfect description for image', // image_requests: [ // { // architecture: 'x86_64', // image_type: 'gcp', // upload_request: { // type: 'gcp', // options: { // share_with_accounts: ['user:test@test.com'], // }, // }, // }, // ], // customizations: customizations, // }; // id = 'edbae1c2-62bc-42c1-ae0c-3110ab718f57'; // } else if (body.image_requests[0].upload_request.type === 'azure') { // expectedbody = { // distribution: RHEL_8, // image_name: 'my-image-name', // image_description: 'this is a perfect description for image', // image_requests: [ // { // architecture: 'x86_64', // image_type: 'azure', // upload_request: { // type: 'azure', // options: { // tenant_id: 'b8f86d22-4371-46ce-95e7-65c415f3b1e2', // subscription_id: '60631143-a7dc-4d15-988b-ba83f3c99711', // resource_group: 'testResourceGroup', // }, // }, // }, // ], // customizations: customizations, // }; // id = 'edbae1c2-62bc-42c1-ae0c-3110ab718f58'; // } else if (body.image_requests[0].image_type === 'vsphere-ova') { // expectedbody = { // distribution: RHEL_8, // image_name: 'my-image-name', // image_description: 'this is a perfect description for image', // image_requests: [ // { // architecture: 'x86_64', // image_type: 'vsphere-ova', // upload_request: { // type: 'aws.s3', // options: {}, // }, // }, // ], // customizations: customizations, // }; // id = 'edbae1c2-62bc-42c1-ae0c-3110ab718f59'; // } else if (body.image_requests[0].image_type === 'guest-image') { // expectedbody = { // distribution: RHEL_8, // image_name: 'my-image-name', // image_description: 'this is a perfect description for image', // image_requests: [ // { // architecture: 'x86_64', // image_type: 'guest-image', // upload_request: { // type: 'aws.s3', // options: {}, // }, // }, // ], // customizations: customizations, // }; // id = 'edbae1c2-62bc-42c1-ae0c-3110ab718f5a'; // } else if (body.image_requests[0].image_type === 'image-installer') { // expectedbody = { // distribution: RHEL_8, // image_name: 'my-image-name', // image_description: 'this is a perfect description for image', // image_requests: [ // { // architecture: 'x86_64', // image_type: 'image-installer', // upload_request: { // type: 'aws.s3', // options: {}, // }, // }, // ], // customizations: customizations, // }; // id = 'edbae1c2-62bc-42c1-ae0c-3110ab718f5b'; // } // expect(body).toEqual(expectedbody); // ids.unshift(id); // return Promise.resolve({ id }); // }); await user.click(screen.getByRole('button', { name: /Create/ })); // API request sent to backend // expect(composeImage).toHaveBeenCalledTimes(6); // returns back to the landing page await waitFor(() => expect(router.state.location.pathname).toBe('/insights/image-builder') ); // expect(store.getState().composes.allIds).toEqual(ids); // set test timeout of 20 seconds }, 20000); }); describe('Keyboard accessibility', () => { const user = userEvent.setup(); const setUp = async () => { ({ router } = await renderCustomRoutesWithReduxRouter( 'imagewizard', {}, routes )); await clickNext(); }; const selectAllEnvironments = () => { user.click(screen.getByTestId('upload-aws')); user.click(screen.getByTestId('upload-google')); user.click(screen.getByTestId('upload-azure')); user.click( screen.getByRole('checkbox', { name: /virtualization guest image checkbox/i, }) ); }; test('autofocus on each step first input element', async () => { await setUp(); // Image output selectAllEnvironments(); await clickNext(); // Target environment aws expect(screen.getByTestId('aws-radio-source')).toHaveFocus(); const awsSourceDropdown = await getSourceDropdown(); user.click(awsSourceDropdown); const awsSource = await screen.findByRole('option', { name: /my_source/i, }); user.click(awsSource); await clickNext(); // Target environment google await user.click(screen.getByTestId('account-sharing')); expect(screen.getByTestId('account-sharing')).toHaveFocus(); await user.type(screen.getByTestId('input-google-email'), 'test@test.com'); await clickNext(); // Target environment azure expect(screen.getByTestId('azure-radio-source')).toHaveFocus(); const azureSourceDropdown = await getSourceDropdown(); user.click(azureSourceDropdown); const azureSource = await screen.findByRole('option', { name: /azureSource1/i, }); user.click(azureSource); const resourceGroupDropdown = await screen.findByRole('textbox', { name: /select resource group/i, }); await user.click(resourceGroupDropdown); await user.click(screen.getByLabelText('Resource group myResourceGroup1')); await clickNext(); // Registration await screen.findByText( 'Automatically register and enable advanced capabilities' ); const registerRadio = screen.getByTestId('registration-radio-now'); expect(registerRadio).toHaveFocus(); await screen.findByRole('textbox', { name: 'Select activation key', }); // skip registration const registerLaterRadio = screen.getByTestId('registration-radio-later'); await user.click(registerLaterRadio); await clickNext(); // File system configuration await clickNext(); // Packages const view = screen.getByTestId('search-available-pkgs-input'); const availablePackagesInput = within(view).getByRole('textbox', { name: /search input/i, }); await waitFor(() => expect(availablePackagesInput).toBeEnabled()); expect(availablePackagesInput).toHaveFocus(); await clickNext(); // TODO: what should have focus on Custom Repos step? await clickNext(); // Name const nameInput = screen.getByRole('textbox', { name: /image name/i }); expect(nameInput).toHaveFocus(); await clickNext(); }); test('pressing Esc closes the wizard', async () => { await setUp(); // wizard needs to be interacted with for the esc key to work await user.click(screen.getByTestId('upload-aws')); await user.keyboard('{escape}'); expect(router.state.location.pathname).toBe('/insights/image-builder'); }); test('pressing Enter does not advance the wizard', async () => { await setUp(); await user.click(screen.getByTestId('upload-aws')); await user.keyboard('{enter}'); screen.getByRole('heading', { name: /image output/i, }); }); test('target environment tiles are keyboard selectable', async () => { const testTile = async (tile) => { tile.focus(); await user.keyboard('{space}'); expect(tile).toHaveClass('pf-m-selected'); await user.keyboard('{space}'); expect(tile).not.toHaveClass('pf-m-selected'); }; await setUp(); await clickNext(); testTile(screen.getByTestId('upload-aws')); testTile(screen.getByTestId('upload-google')); testTile(screen.getByTestId('upload-azure')); }); });