debian-image-builder-frontend/src/test/SmartComponents/CreateImageWizard/CreateImageWizard.test.js
Jacob Kozol f268441d41 CreateImageWizard: validate upload destination
The user should not be able to continue without selecting an upload
destination. The uploadDestination fields are now checked to see if one
is selected before enabling the Next button in the footer. Also, the
onSaveInProgress variable is changed to isSaveInProgress to conform with
the other validation variables.
2021-03-15 14:37:27 +01:00

567 lines
18 KiB
JavaScript

import '@testing-library/jest-dom';
import React from 'react';
import { screen, getByText, waitFor, waitForElementToBeRemoved, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { renderWithReduxRouter } from '../../testUtils';
import CreateImageWizard from '../../../SmartComponents/CreateImageWizard/CreateImageWizard';
import api from '../../../api.js';
let historySpy = undefined;
function verifyButtons() {
// these buttons exist everywhere
const next = screen.getByRole('button', { name: /Next/ });
const back = screen.getByRole('button', { name: /Back/ });
const cancel = screen.getByRole('button', { name: /Cancel/ });
return [ next, back, cancel ];
}
function verifyCancelButton(cancel, history) {
cancel.click();
// this goes back to the landing page
// but jsdom will not render the new page so we can't assert on that
expect(history).toHaveBeenCalledTimes(1);
expect(history).toHaveBeenCalledWith('/landing');
}
// mock the insights dependency
beforeAll(() => {
global.insights = {
chrome: {
auth: {
getUser: () => {
return {
identity: {
internal: {
org_id: 5
}
}
};
}
}
}
};
});
afterEach(() => {
jest.clearAllMocks();
historySpy = undefined;
});
// restore global mock
afterAll(() => {
global.insights = undefined;
});
describe('Create Image Wizard', () => {
beforeEach(() => {
renderWithReduxRouter(<CreateImageWizard />);
});
test('renders component', () => {
// check heading
screen.getByRole('heading', { name: /Create image/ });
// left sidebar navigation
const sidebar = screen.getByRole('navigation');
getByText(sidebar, 'Image output');
getByText(sidebar, 'Registration');
getByText(sidebar, 'Review');
});
});
describe('Step Image output', () => {
beforeEach(() => {
const { _component, history } = renderWithReduxRouter(<CreateImageWizard />);
historySpy = jest.spyOn(history, 'push');
// left sidebar navigation
const sidebar = screen.getByRole('navigation');
const anchor = getByText(sidebar, 'Image output');
// select aws as upload destination
const awsTile = screen.getByTestId('upload-aws');
awsTile.click();
// load from sidebar
anchor.click();
});
test('clicking Next loads Upload to AWS', () => {
const [ next, , ] = verifyButtons();
next.click();
screen.getByText('AWS account ID');
});
test('Back button is disabled', () => {
const [ , back, ] = verifyButtons();
// note: there is no `disabled` attribute and
// .toBeDissabled() fails
expect(back).toHaveClass('pf-m-disabled');
});
test('clicking Cancel loads landing page', () => {
const [ , , cancel ] = verifyButtons();
verifyCancelButton(cancel, historySpy);
});
test('allows chosing a release', () => {
const release = screen.getByTestId('release-select');
expect(release).toBeEnabled();
userEvent.selectOptions(release, [ 'rhel-8' ]);
});
test('target environment is required', () => {
const destination = screen.getByTestId('target-select');
const required = within(destination).getByText('*');
expect(destination).toBeEnabled();
expect(destination).toContainElement(required);
});
});
describe('Step Upload to AWS', () => {
beforeEach(() => {
const { _component, history } = renderWithReduxRouter(<CreateImageWizard />);
historySpy = jest.spyOn(history, 'push');
// select aws as upload destination
const awsTile = screen.getByTestId('upload-aws');
awsTile.click();
// left sidebar navigation
const sidebar = screen.getByRole('navigation');
const anchor = getByText(sidebar, 'Amazon Web Services');
// load from sidebar
anchor.click();
});
test('clicking Next loads Registration', () => {
const [ next, , ] = verifyButtons();
next.click();
screen.getByText('Register the system');
});
test('clicking Back loads Release', () => {
const [ , back, ] = verifyButtons();
back.click();
screen.getByTestId('release-select');
});
test('clicking Cancel loads landing page', () => {
const [ , , cancel ] = verifyButtons();
verifyCancelButton(cancel, historySpy);
});
test('the aws account id fieldis shown and required', () => {
const accessKeyId = screen.getByTestId('aws-account-id');
expect(accessKeyId).toHaveValue('');
expect(accessKeyId).toBeEnabled();
expect(accessKeyId).toBeRequired();
});
});
describe('Step Upload to Google', () => {
beforeEach(() => {
const { _component, history } = renderWithReduxRouter(<CreateImageWizard />);
historySpy = jest.spyOn(history, 'push');
// select aws as upload destination
const awsTile = screen.getByTestId('upload-google');
awsTile.click();
// left sidebar navigation
const sidebar = screen.getByRole('navigation');
const anchor = getByText(sidebar, 'Google Cloud Platform');
// load from sidebar
anchor.click();
});
test('clicking Next loads Registration', () => {
const [ next, , ] = verifyButtons();
next.click();
screen.getByText('Register the system');
});
test('clicking Back loads Release', () => {
const [ , back, ] = verifyButtons();
back.click();
screen.getByTestId('release-select');
});
test('clicking Cancel loads landing page', () => {
const [ , , cancel ] = verifyButtons();
verifyCancelButton(cancel, historySpy);
});
test('the google account id field is shown and required', () => {
const accessKeyId = screen.getByTestId('input-google-user');
expect(accessKeyId).toHaveValue('');
expect(accessKeyId).toBeEnabled();
expect(accessKeyId).toBeRequired();
});
});
describe('Step Upload to Azure', () => {
beforeEach(() => {
const { _component, history } = renderWithReduxRouter(<CreateImageWizard />);
historySpy = jest.spyOn(history, 'push');
// select aws as upload destination
const azureTile = screen.getByTestId('upload-azure');
azureTile.click();
// left sidebar navigation
const sidebar = screen.getByRole('navigation');
const anchor = getByText(sidebar, 'Microsoft Azure');
// load from sidebar
anchor.click();
});
test('clicking Next loads Registration', () => {
const [ next, , ] = verifyButtons();
next.click();
screen.getByText('Register the system');
});
test('clicking Back loads Release', () => {
const [ , back, ] = verifyButtons();
back.click();
screen.getByTestId('release-select');
});
test('clicking Cancel loads landing page', () => {
const [ , , cancel ] = verifyButtons();
verifyCancelButton(cancel, historySpy);
});
test('the azure upload fields are shown and required', () => {
const tenantId = screen.getByTestId('azure-tenant-id');
expect(tenantId).toHaveValue('');
expect(tenantId).toBeEnabled();
expect(tenantId).toBeRequired();
const subscription = screen.getByTestId('azure-subscription-id');
expect(subscription).toHaveValue('');
expect(subscription).toBeEnabled();
expect(subscription).toBeRequired();
const resourceGroup = screen.getByTestId('azure-resource-group');
expect(resourceGroup).toHaveValue('');
expect(resourceGroup).toBeEnabled();
expect(resourceGroup).toBeRequired();
});
});
describe('Step Registration', () => {
beforeEach(() => {
const { _component, history } = renderWithReduxRouter(<CreateImageWizard />);
historySpy = jest.spyOn(history, 'push');
// select aws as upload destination
const awsTile = screen.getByTestId('upload-aws');
awsTile.click();
// left sidebar navigation
const sidebar = screen.getByRole('navigation');
const anchor = getByText(sidebar, 'Registration');
// load from sidebar
anchor.click();
});
test('clicking Next loads Packages', () => {
const [ next, , ] = verifyButtons();
next.click();
screen.getByText('Optionally add additional packages to your image');
});
test('clicking Back loads Upload to AWS', () => {
const [ , back, ] = verifyButtons();
back.click();
screen.getByText('AWS account ID');
});
test('clicking Cancel loads landing page', () => {
const [ , , cancel ] = verifyButtons();
verifyCancelButton(cancel, historySpy);
});
test('should allow choosing activation keys', async () => {
screen
.getByLabelText('Embed an activation key and register systems on first boot')
.click();
const organizationId = screen.getByLabelText('Organization ID');
expect(organizationId).toHaveValue('5');
expect(organizationId).toBeDisabled();
// can't getByLabelText b/c the label contains an extra <span>
// with a `*` to denote required
const activationKey = screen.getByTestId('subscription-activation');
expect(activationKey).toHaveValue('');
expect(activationKey).toBeEnabled();
expect(activationKey).toBeRequired();
const sidebar = screen.getByRole('navigation');
const anchor = getByText(sidebar, 'Review');
anchor.click();
await screen.findByText('Register the system on first boot');
});
test('should hide input fields when clicking Register the system later', async () => {
// first check the other radio button which causes extra widgets to be shown
screen
.getByLabelText('Embed an activation key and register systems on first boot')
.click();
const p1 = waitForElementToBeRemoved(() => [
screen.getByTestId('organization-id'),
screen.getByTestId('subscription-activation'),
]);
// then click the first radio button which should remove any input fields
screen
.getByTestId('register-later-radio-button')
.click();
await p1;
const sidebar = screen.getByRole('navigation');
const anchor = getByText(sidebar, 'Review');
anchor.click();
await screen.findByText('Register the system later');
});
});
describe('Step Packages', () => {
beforeEach(() => {
const { _component, history } = renderWithReduxRouter(<CreateImageWizard />);
historySpy = jest.spyOn(history, 'push');
// select aws as upload destination
const awsTile = screen.getByTestId('upload-aws');
awsTile.click();
// left sidebar navigation
const sidebar = screen.getByRole('navigation');
const anchor = getByText(sidebar, 'Packages');
// load from sidebar
anchor.click();
});
test('clicking Next loads Review', () => {
const [ next, , ] = verifyButtons();
next.click();
screen.getByText('Review the information and click Create image to create the image using the following criteria.');
});
test('clicking Back loads Register', () => {
const back = screen.getByRole('button', { name: /Back/ });
back.click();
screen.getByText('Register the system');
});
test('clicking Cancel loads landing page', () => {
const [ , , cancel ] = verifyButtons();
verifyCancelButton(cancel, historySpy);
});
test('should display search bar and button', () => {
const search = screen.getByRole('searchbox', { name: 'Available search input' });
search.click();
userEvent.type(search, 'test');
screen.getByRole('button', {
name: 'Search button for available packages'
});
});
});
describe('Step Review', () => {
beforeEach(() => {
const { _component, history } = renderWithReduxRouter(<CreateImageWizard />);
historySpy = jest.spyOn(history, 'push');
// select aws as upload destination
const awsTile = screen.getByTestId('upload-aws');
awsTile.click();
// left sidebar navigation
const sidebar = screen.getByRole('navigation');
const anchor = getByText(sidebar, 'Review');
// load from sidebar
anchor.click();
});
test('has 3 buttons', () => {
screen.getByRole('button', { name: /Create/ });
screen.getByRole('button', { name: /Back/ });
screen.getByRole('button', { name: /Cancel/ });
});
test('clicking Back loads Packages', () => {
const back = screen.getByRole('button', { name: /Back/ });
back.click();
screen.getByText('Optionally add additional packages to your image');
});
test('clicking Cancel loads landing page', () => {
const cancel = screen.getByRole('button', { name: /Cancel/ });
verifyCancelButton(cancel, historySpy);
});
});
describe('Click through all steps', () => {
beforeEach(() => {
const { _component, history } = renderWithReduxRouter(<CreateImageWizard />);
historySpy = jest.spyOn(history, 'push');
});
test('with valid values', async () => {
const next = screen.getByRole('button', { name: /Next/ });
// select image output
userEvent.selectOptions(screen.getByTestId('release-select'), [ 'rhel-8' ]);
screen.getByTestId('upload-aws').click();
screen.getByTestId('upload-google').click();
next.click();
// select upload target
userEvent.type(screen.getByTestId('aws-account-id'), '012345678901');
next.click();
userEvent.type(screen.getByTestId('input-google-user'), 'test@test.com');
next.click();
// registration
screen
.getByLabelText('Embed an activation key and register systems on first boot')
.click();
await screen.findByTestId('subscription-activation');
userEvent.type(screen.getByTestId('subscription-activation'), '1234567890');
next.click();
// packages
screen.getByText('Optionally add additional packages to your image');
next.click();
// review
await screen.
findByText('Review the information and click Create image to create the image using the following criteria.');
const main = screen.getByRole('main', { name: 'Create image' });
within(main).getByText('Amazon Web Services');
within(main).getByText('Google Cloud Platform');
await screen.findByText('Register the system on first boot');
// mock the backend API
const composeImage = jest.spyOn(api, 'composeImage');
composeImage.mockResolvedValue({ id: 'test-me' });
const create = screen.getByRole('button', { name: /Create/ });
create.click();
// API request sent to backend
await expect(composeImage).toHaveBeenCalledTimes(2);
// returns back to the landing page
// but jsdom will not render the new page so we can't assert on that
await waitFor(() => expect(historySpy).toHaveBeenCalledTimes(1));
await expect(historySpy).toHaveBeenCalledWith('/landing');
});
test('with missing values', async () => {
const next = screen.getByRole('button', { name: /Next/ });
// select release
userEvent.selectOptions(screen.getByTestId('release-select'), [ 'rhel-8' ]);
screen.getByTestId('upload-aws').click();
next.click();
// leave AWS account id empty
next.click();
// registration
screen
.getByLabelText('Embed an activation key and register systems on first boot')
.click();
await screen.findByTestId('subscription-activation');
userEvent.clear(screen.getByTestId('subscription-activation'));
next.click();
// packages
screen.getByText('Optionally add additional packages to your image');
next.click();
await screen.
findByText('Review the information and click Create image to create the image using the following criteria.');
const main = screen.getByRole('main', { name: 'Create image' });
within(main).getByText('Amazon Web Services');
await screen.findByText('Register the system on first boot');
const errorMessages = await screen.findAllByText('A value is required');
expect(errorMessages.length).toBe(1);
const uploadErrorMessage = await screen.findAllByText('A 12-digit number is required');
expect(uploadErrorMessage.length).toBe(1);
});
test('with invalid values', async () => {
const next = screen.getByRole('button', { name: /Next/ });
// select release
userEvent.selectOptions(screen.getByTestId('release-select'), [ 'rhel-8' ]);
// select upload target
screen.getByTestId('upload-aws').click();
next.click();
userEvent.type(screen.getByTestId('aws-account-id'), 'invalid, isNaN');
next.click();
// registration
screen
.getByLabelText('Embed an activation key and register systems on first boot')
.click();
await screen.findByTestId('subscription-activation');
userEvent.clear(screen.getByTestId('subscription-activation'));
next.click();
// packages
screen.getByText('Optionally add additional packages to your image');
next.click();
await screen.
findByText('Review the information and click Create image to create the image using the following criteria.');
const main = screen.getByRole('main', { name: 'Create image' });
within(main).getByText('Amazon Web Services');
await screen.findByText('Register the system on first boot');
const errorMessages = await screen.findAllByText('A value is required');
expect(errorMessages.length).toBe(1);
const uploadErrorMessage = await screen.findAllByText('A 12-digit number is required');
expect(uploadErrorMessage.length).toBe(1);
});
});