ImagesTable: Convert ImagesTable to Typescript & RTK Query
This commit converts the Images Table to Typescript and converts all API calls to image-builder to use RTK Query hooks. This should increase the performance of the app significantly. Previously our calls to the image-builder API were made in series. They are now made in parallel. We may want to investigate the possibility of hitting rate limiting now that we will be issuing requests in much more rapid succession. In the tests, moving to RTK Query hooks has allowed us to remove virtually all Jest mocking. However, this means that some of our previous tests which tested against implementation details were broken. Most notably, we no longer check the Redux store to verify that clones have been added correctly and we no longer check that compose requests were issued successfully. Test coverage will be restored in a follow-up PR where the dev-dependency @msw/data is added. Adding a persistent data layer to the tests using @msw/data will allow us to verify that our POST requests (creating composes and cloning them) are working by testing that the Images Table has been updated.
This commit is contained in:
parent
155a0cf57c
commit
7b9e726151
34 changed files with 2397 additions and 2499 deletions
|
|
@ -75,7 +75,11 @@ const getSourceDropdown = async () => {
|
|||
describe('Step Upload to Azure', () => {
|
||||
const user = userEvent.setup();
|
||||
const setUp = async () => {
|
||||
({ router } = renderCustomRoutesWithReduxRouter('imagewizard', {}, routes));
|
||||
({ router } = await renderCustomRoutesWithReduxRouter(
|
||||
'imagewizard',
|
||||
{},
|
||||
routes
|
||||
));
|
||||
// select Azure as upload destination
|
||||
await user.click(await screen.findByTestId('upload-azure'));
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,6 @@ import userEvent from '@testing-library/user-event';
|
|||
import api from '../../../api.js';
|
||||
import CreateImageWizard from '../../../Components/CreateImageWizard/CreateImageWizard';
|
||||
import ShareImageModal from '../../../Components/ShareImageModal/ShareImageModal';
|
||||
import { store } from '../../../store/index.js';
|
||||
import {
|
||||
mockComposesEmpty,
|
||||
mockStateRecreateImage,
|
||||
} from '../../fixtures/composes';
|
||||
import {
|
||||
mockPkgResultAlpha,
|
||||
mockPkgResultAlphaContentSources,
|
||||
|
|
@ -63,14 +58,6 @@ jest.mock('@redhat-cloud-services/frontend-components/useChrome', () => ({
|
|||
}),
|
||||
}));
|
||||
|
||||
// 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));
|
||||
|
||||
const searchForAvailablePackages = async (searchbox, searchTerm) => {
|
||||
const user = userEvent.setup();
|
||||
await user.type(searchbox, searchTerm);
|
||||
|
|
@ -117,7 +104,7 @@ describe('Step Packages', () => {
|
|||
const setUp = async () => {
|
||||
mockContentSourcesEnabled = false;
|
||||
|
||||
({ router } = renderCustomRoutesWithReduxRouter(
|
||||
({ router } = await renderCustomRoutesWithReduxRouter(
|
||||
'imagewizard',
|
||||
{},
|
||||
routes
|
||||
|
|
@ -877,35 +864,21 @@ describe('Step Custom repositories', () => {
|
|||
describe('On Recreate', () => {
|
||||
const user = userEvent.setup();
|
||||
const setUp = async () => {
|
||||
jest.mock('../../../store/index.js');
|
||||
|
||||
const state = mockStateRecreateImage;
|
||||
|
||||
store.getState = () => state;
|
||||
|
||||
({ router } = renderWithReduxRouter(
|
||||
'imagewizard/hyk93673-8dcc-4a61-ac30-e9f4940d8346',
|
||||
state
|
||||
'imagewizard/hyk93673-8dcc-4a61-ac30-e9f4940d8346'
|
||||
));
|
||||
};
|
||||
|
||||
const setUpUnavailableRepo = async () => {
|
||||
jest.mock('../../../store/index.js');
|
||||
|
||||
const state = mockStateRecreateImage;
|
||||
|
||||
store.getState = () => state;
|
||||
|
||||
({ router } = renderWithReduxRouter(
|
||||
'imagewizard/b7193673-8dcc-4a5f-ac30-e9f4940d8346',
|
||||
state
|
||||
'imagewizard/b7193673-8dcc-4a5f-ac30-e9f4940d8346'
|
||||
));
|
||||
};
|
||||
|
||||
test('with valid repositories', async () => {
|
||||
await setUp();
|
||||
|
||||
screen.getByRole('heading', { name: /review/i });
|
||||
await screen.findByRole('heading', { name: /review/i });
|
||||
expect(
|
||||
screen.queryByText('Previously added custom repository unavailable')
|
||||
).not.toBeInTheDocument();
|
||||
|
|
@ -943,7 +916,7 @@ describe('On Recreate', () => {
|
|||
test('with repositories that are no longer available', async () => {
|
||||
await setUpUnavailableRepo();
|
||||
|
||||
screen.getByRole('heading', { name: /review/i });
|
||||
await screen.findByRole('heading', { name: /review/i });
|
||||
await screen.findByText('Previously added custom repository unavailable');
|
||||
|
||||
const createImageButton = await screen.findByRole('button', {
|
||||
|
|
|
|||
|
|
@ -11,12 +11,9 @@ import {
|
|||
import userEvent from '@testing-library/user-event';
|
||||
import { rest } from 'msw';
|
||||
|
||||
import api from '../../../api.js';
|
||||
import CreateImageWizard from '../../../Components/CreateImageWizard/CreateImageWizard';
|
||||
import ShareImageModal from '../../../Components/ShareImageModal/ShareImageModal';
|
||||
import { RHEL_8, RHEL_9, PROVISIONING_API } from '../../../constants.js';
|
||||
import { mockComposesEmpty } from '../../fixtures/composes';
|
||||
import { customizations } from '../../fixtures/customizations';
|
||||
import { PROVISIONING_API } from '../../../constants.js';
|
||||
import { server } from '../../mocks/server.js';
|
||||
import {
|
||||
clickBack,
|
||||
|
|
@ -48,9 +45,9 @@ let router = undefined;
|
|||
// 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
|
||||
// .spyOn(api, 'getComposes')
|
||||
// .mockImplementation(() => Promise.resolve(mockComposesEmpty));
|
||||
|
||||
jest.mock('@redhat-cloud-services/frontend-components/useChrome', () => ({
|
||||
useChrome: () => ({
|
||||
|
|
@ -137,7 +134,11 @@ describe('Create Image Wizard', () => {
|
|||
describe('Step Image output', () => {
|
||||
const user = userEvent.setup();
|
||||
const setUp = async () => {
|
||||
({ router } = renderCustomRoutesWithReduxRouter('imagewizard', {}, routes));
|
||||
({ router } = await renderCustomRoutesWithReduxRouter(
|
||||
'imagewizard',
|
||||
{},
|
||||
routes
|
||||
));
|
||||
|
||||
// select aws as upload destination
|
||||
await user.click(await screen.findByTestId('upload-aws'));
|
||||
|
|
@ -270,7 +271,7 @@ describe('Step Image output', () => {
|
|||
describe('Step Upload to AWS', () => {
|
||||
const user = userEvent.setup();
|
||||
const setUp = async () => {
|
||||
({ router, store } = renderCustomRoutesWithReduxRouter(
|
||||
({ router, store } = await renderCustomRoutesWithReduxRouter(
|
||||
'imagewizard',
|
||||
{},
|
||||
routes
|
||||
|
|
@ -399,44 +400,38 @@ describe('Step Upload to AWS', () => {
|
|||
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 });
|
||||
});
|
||||
// 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/ }));
|
||||
|
||||
// API request sent to backend
|
||||
await waitFor(() => expect(composeImage).toHaveBeenCalledTimes(1));
|
||||
|
||||
// returns back to the landing page
|
||||
await waitFor(() =>
|
||||
expect(router.state.location.pathname).toBe('/insights/image-builder')
|
||||
);
|
||||
expect(store.getState().composes.allIds).toEqual([
|
||||
'edbae1c2-62bc-42c1-ae0c-3110ab718f5a',
|
||||
]);
|
||||
// set test timeout of 10 seconds
|
||||
}, 10000);
|
||||
});
|
||||
|
|
@ -444,7 +439,11 @@ describe('Step Upload to AWS', () => {
|
|||
describe('Step Upload to Google', () => {
|
||||
const user = userEvent.setup();
|
||||
const setUp = async () => {
|
||||
({ router } = renderCustomRoutesWithReduxRouter('imagewizard', {}, routes));
|
||||
({ router } = await renderCustomRoutesWithReduxRouter(
|
||||
'imagewizard',
|
||||
{},
|
||||
routes
|
||||
));
|
||||
|
||||
// select gcp as upload destination
|
||||
user.click(screen.getByTestId('upload-google'));
|
||||
|
|
@ -507,7 +506,11 @@ describe('Step Upload to Google', () => {
|
|||
describe('Step Registration', () => {
|
||||
const user = userEvent.setup();
|
||||
const setUp = async () => {
|
||||
({ router } = renderCustomRoutesWithReduxRouter('imagewizard', {}, routes));
|
||||
({ router } = await renderCustomRoutesWithReduxRouter(
|
||||
'imagewizard',
|
||||
{},
|
||||
routes
|
||||
));
|
||||
|
||||
// select aws as upload destination
|
||||
user.click(screen.getByTestId('upload-aws'));
|
||||
|
|
@ -693,7 +696,11 @@ describe('Step Registration', () => {
|
|||
describe('Step File system configuration', () => {
|
||||
const user = userEvent.setup();
|
||||
const setUp = async () => {
|
||||
({ router } = renderCustomRoutesWithReduxRouter('imagewizard', {}, routes));
|
||||
({ router } = await renderCustomRoutesWithReduxRouter(
|
||||
'imagewizard',
|
||||
{},
|
||||
routes
|
||||
));
|
||||
|
||||
// select aws as upload destination
|
||||
user.click(screen.getByTestId('upload-aws'));
|
||||
|
|
@ -768,7 +775,11 @@ describe('Step File system configuration', () => {
|
|||
describe('Step Details', () => {
|
||||
const user = userEvent.setup();
|
||||
const setUp = async () => {
|
||||
({ router } = renderCustomRoutesWithReduxRouter('imagewizard', {}, routes));
|
||||
({ router } = await renderCustomRoutesWithReduxRouter(
|
||||
'imagewizard',
|
||||
{},
|
||||
routes
|
||||
));
|
||||
|
||||
// select aws as upload destination
|
||||
user.click(screen.getByTestId('upload-aws'));
|
||||
|
|
@ -837,7 +848,11 @@ describe('Step Details', () => {
|
|||
describe('Step Review', () => {
|
||||
const user = userEvent.setup();
|
||||
const setUp = async () => {
|
||||
({ router } = renderCustomRoutesWithReduxRouter('imagewizard', {}, routes));
|
||||
({ router } = await renderCustomRoutesWithReduxRouter(
|
||||
'imagewizard',
|
||||
{},
|
||||
routes
|
||||
));
|
||||
|
||||
// select aws as upload destination
|
||||
user.click(screen.getByTestId('upload-aws'));
|
||||
|
|
@ -984,7 +999,7 @@ describe('Step Review', () => {
|
|||
describe('Click through all steps', () => {
|
||||
const user = userEvent.setup();
|
||||
const setUp = async () => {
|
||||
({ router, store } = renderCustomRoutesWithReduxRouter(
|
||||
({ router, store } = await renderCustomRoutesWithReduxRouter(
|
||||
'imagewizard',
|
||||
{},
|
||||
routes
|
||||
|
|
@ -1051,7 +1066,10 @@ describe('Click through all steps', () => {
|
|||
await clickNext();
|
||||
|
||||
// fsc
|
||||
(await screen.findByTestId('file-system-config-radio-manual')).click();
|
||||
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);
|
||||
|
|
@ -1188,146 +1206,146 @@ describe('Click through all steps', () => {
|
|||
);
|
||||
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);
|
||||
// // 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 });
|
||||
});
|
||||
// ids.unshift(id);
|
||||
// return Promise.resolve({ id });
|
||||
// });
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /Create/ }));
|
||||
|
||||
// API request sent to backend
|
||||
expect(composeImage).toHaveBeenCalledTimes(6);
|
||||
// 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);
|
||||
// expect(store.getState().composes.allIds).toEqual(ids);
|
||||
// set test timeout of 20 seconds
|
||||
}, 20000);
|
||||
});
|
||||
|
|
@ -1335,7 +1353,11 @@ describe('Click through all steps', () => {
|
|||
describe('Keyboard accessibility', () => {
|
||||
const user = userEvent.setup();
|
||||
const setUp = async () => {
|
||||
({ router } = renderCustomRoutesWithReduxRouter('imagewizard', {}, routes));
|
||||
({ router } = await renderCustomRoutesWithReduxRouter(
|
||||
'imagewizard',
|
||||
{},
|
||||
routes
|
||||
));
|
||||
await clickNext();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,21 +2,15 @@ import React from 'react';
|
|||
|
||||
import { screen, waitFor, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
|
||||
import api from '../../../api.js';
|
||||
import { ImageBuildStatus } from '../../../Components/ImagesTable/ImageBuildStatus';
|
||||
import ImageLink from '../../../Components/ImagesTable/ImageLink';
|
||||
import Target from '../../../Components/ImagesTable/Target';
|
||||
import '@testing-library/jest-dom';
|
||||
import {
|
||||
mockComposes,
|
||||
mockStatus,
|
||||
mockClones,
|
||||
mockCloneStatus,
|
||||
mockNoClones,
|
||||
} from '../../fixtures/composes';
|
||||
import { renderWithProvider, renderWithReduxRouter } from '../../testUtils';
|
||||
import { renderWithReduxRouter } from '../../testUtils';
|
||||
|
||||
jest.mock('@redhat-cloud-services/frontend-components/useChrome', () => ({
|
||||
useChrome: () => ({
|
||||
|
|
@ -31,24 +25,6 @@ jest.mock('@unleash/proxy-client-react', () => ({
|
|||
useFlag: jest.fn((flag) => (flag === 'edgeParity.image-list' ? false : true)),
|
||||
}));
|
||||
|
||||
jest
|
||||
.spyOn(api, 'getComposes')
|
||||
.mockImplementation(() => Promise.resolve(mockComposes));
|
||||
|
||||
jest.spyOn(api, 'getComposeStatus').mockImplementation((id) => {
|
||||
return Promise.resolve(mockStatus(id));
|
||||
});
|
||||
|
||||
jest.spyOn(api, 'getClones').mockImplementation((id) => {
|
||||
return id === '1579d95b-8f1d-4982-8c53-8c2afa4ab04c'
|
||||
? Promise.resolve(mockClones(id))
|
||||
: Promise.resolve(mockNoClones);
|
||||
});
|
||||
|
||||
jest.spyOn(api, 'getCloneStatus').mockImplementation((id) => {
|
||||
return Promise.resolve(mockCloneStatus(id));
|
||||
});
|
||||
|
||||
beforeAll(() => {
|
||||
// scrollTo is not defined in jsdom
|
||||
window.HTMLElement.prototype.scrollTo = function () {};
|
||||
|
|
@ -57,7 +33,7 @@ beforeAll(() => {
|
|||
describe('Images Table', () => {
|
||||
const user = userEvent.setup();
|
||||
test('render ImagesTable', async () => {
|
||||
const view = renderWithReduxRouter('', {});
|
||||
await renderWithReduxRouter('', {});
|
||||
|
||||
const table = await screen.findByTestId('images-table');
|
||||
|
||||
|
|
@ -65,8 +41,6 @@ describe('Images Table', () => {
|
|||
const emptyState = screen.queryByTestId('empty-state');
|
||||
expect(emptyState).not.toBeInTheDocument();
|
||||
|
||||
const state = view.store.getState();
|
||||
|
||||
// check table
|
||||
const { getAllByRole } = within(table);
|
||||
const rows = getAllByRole('row');
|
||||
|
|
@ -80,103 +54,107 @@ describe('Images Table', () => {
|
|||
expect(header.cells[5]).toHaveTextContent('Status');
|
||||
expect(header.cells[6]).toHaveTextContent('Instance');
|
||||
|
||||
const imageNameValues = mockComposes.map((compose) =>
|
||||
compose.image_name ? compose.image_name : compose.id
|
||||
);
|
||||
|
||||
const statusValues = [
|
||||
'Ready',
|
||||
'Image build failed',
|
||||
'Image build is pending',
|
||||
'Image build in progress',
|
||||
'Image upload in progress',
|
||||
'Cloud registration in progress',
|
||||
'Image build failed',
|
||||
'Ready',
|
||||
'Image build in progress',
|
||||
'Expired',
|
||||
];
|
||||
|
||||
const targetValues = [
|
||||
'Amazon Web Services (5)',
|
||||
'Google Cloud PlatformFAKE',
|
||||
'Amazon Web Services (1)',
|
||||
'Amazon Web Services (1)',
|
||||
'Amazon Web Services (1)',
|
||||
'Amazon Web Services (1)',
|
||||
'Amazon Web Services (1)',
|
||||
'Google Cloud Platform',
|
||||
'Microsoft Azure',
|
||||
'VMWare vSphere',
|
||||
];
|
||||
|
||||
const instanceValues = [
|
||||
'Launch',
|
||||
'Launch',
|
||||
'Launch',
|
||||
'Launch',
|
||||
'Launch',
|
||||
'Launch',
|
||||
'Launch',
|
||||
'Launch',
|
||||
'Launch',
|
||||
'Recreate image',
|
||||
];
|
||||
|
||||
// 10 rows for 10 images
|
||||
expect(rows).toHaveLength(10);
|
||||
for (const row of rows) {
|
||||
const col1 = row.cells[1].textContent;
|
||||
|
||||
// find compose with either the user defined image name or the uuid
|
||||
const compose = mockComposes.data.find(
|
||||
(compose) => compose?.image_name === col1 || compose.id === col1
|
||||
);
|
||||
expect(compose).toBeTruthy();
|
||||
|
||||
// date should match the month day and year of the timestamp.
|
||||
rows.forEach(async (row, index) => {
|
||||
expect(row.cells[1]).toHaveTextContent(imageNameValues[index]);
|
||||
expect(row.cells[2]).toHaveTextContent('Apr 27, 2021');
|
||||
expect(row.cells[3]).toHaveTextContent('RHEL 8.8');
|
||||
});
|
||||
|
||||
// render the expected <ImageBuildStatus /> and compare the text content
|
||||
const testElement = document.createElement('testElement');
|
||||
// render(<Target composeId={compose.id} />, { container: testElement });
|
||||
renderWithProvider(<Target composeId={compose.id} />, testElement, state);
|
||||
expect(row.cells[4]).toHaveTextContent(testElement.textContent);
|
||||
|
||||
let toTest = expect(row.cells[5]);
|
||||
// render the expected <ImageBuildStatus /> and compare the text content
|
||||
if (
|
||||
compose.created_at === '2021-04-27T12:31:12Z' &&
|
||||
compose.request.image_requests[0].upload_request.type === 'aws.s3'
|
||||
) {
|
||||
toTest.toHaveTextContent('Expired');
|
||||
} else {
|
||||
renderWithProvider(
|
||||
<ImageBuildStatus imageId={compose.id} isImagesTableRow={true} />,
|
||||
testElement,
|
||||
state
|
||||
);
|
||||
toTest.toHaveTextContent(testElement.textContent);
|
||||
}
|
||||
|
||||
toTest = expect(row.cells[6]);
|
||||
// render the expected <ImageLink /> and compare the text content for a link
|
||||
if (
|
||||
compose.created_at === '2021-04-27T12:31:12Z' &&
|
||||
compose.request.image_requests[0].upload_request.type === 'aws.s3'
|
||||
) {
|
||||
toTest.toHaveTextContent('Recreate image');
|
||||
} else {
|
||||
renderWithProvider(
|
||||
<BrowserRouter>
|
||||
<ImageLink imageId={compose.id} isInClonesTable={false} />
|
||||
</BrowserRouter>,
|
||||
testElement,
|
||||
state
|
||||
);
|
||||
toTest.toHaveTextContent(testElement.textContent);
|
||||
}
|
||||
}
|
||||
// TODO Test remaining table content.
|
||||
});
|
||||
|
||||
test('check recreate action', async () => {
|
||||
const { router } = renderWithReduxRouter('', {});
|
||||
const { router } = await renderWithReduxRouter('', {});
|
||||
|
||||
// get rows
|
||||
const table = await screen.findByTestId('images-table');
|
||||
const { getAllByRole } = within(table);
|
||||
const rows = getAllByRole('row');
|
||||
const { findAllByRole } = within(table);
|
||||
const rows = await findAllByRole('row');
|
||||
|
||||
// first row is header so look at index 1
|
||||
const imageId = rows[1].cells[1].textContent;
|
||||
|
||||
const actionsButton = within(rows[1]).getByRole('button', {
|
||||
const actionsButton = await within(rows[1]).findByRole('button', {
|
||||
name: 'Actions',
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(actionsButton).toBeEnabled();
|
||||
});
|
||||
|
||||
user.click(actionsButton);
|
||||
const recreateButton = await screen.findByRole('menuitem', {
|
||||
name: 'Recreate image',
|
||||
});
|
||||
user.click(recreateButton);
|
||||
|
||||
act(() => {
|
||||
user.click(recreateButton);
|
||||
});
|
||||
|
||||
await waitFor(() =>
|
||||
expect(router.state.location.pathname).toBe(
|
||||
`/insights/image-builder/imagewizard/${imageId}`
|
||||
'/insights/image-builder/imagewizard/1579d95b-8f1d-4982-8c53-8c2afa4ab04c'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test('check download compose request action', async () => {
|
||||
renderWithReduxRouter('', {});
|
||||
await renderWithReduxRouter('', {});
|
||||
|
||||
// get rows
|
||||
const table = await screen.findByTestId('images-table');
|
||||
const { getAllByRole } = within(table);
|
||||
const rows = getAllByRole('row');
|
||||
const { findAllByRole } = within(table);
|
||||
const rows = await findAllByRole('row');
|
||||
|
||||
const expectedRequest = mockComposes[0].request;
|
||||
|
||||
// first row is header so look at index 1
|
||||
const imageId = rows[1].cells[1].textContent;
|
||||
const expectedRequest = mockComposes.data.filter((c) => c.id === imageId)[0]
|
||||
.request;
|
||||
|
||||
const actionsButton = within(rows[1]).getByRole('button', {
|
||||
const actionsButton = await within(rows[1]).findByRole('button', {
|
||||
name: 'Actions',
|
||||
});
|
||||
user.click(actionsButton);
|
||||
|
|
@ -202,31 +180,33 @@ describe('Images Table', () => {
|
|||
});
|
||||
|
||||
test('check expandable row toggle', async () => {
|
||||
renderWithReduxRouter('', {});
|
||||
await renderWithReduxRouter('', {});
|
||||
|
||||
const table = await screen.findByTestId('images-table');
|
||||
const { getAllByRole } = within(table);
|
||||
const rows = getAllByRole('row');
|
||||
const { findAllByRole } = within(table);
|
||||
const rows = await findAllByRole('row');
|
||||
|
||||
const toggleButton = within(rows[1]).getByRole('button', {
|
||||
const toggleButton = await within(rows[1]).findByRole('button', {
|
||||
name: /details/i,
|
||||
});
|
||||
|
||||
expect(screen.getByText(/ami-0e778053cd490ad21/i)).not.toBeVisible();
|
||||
expect(await screen.findByText(/ami-0e778053cd490ad21/i)).not.toBeVisible();
|
||||
await user.click(toggleButton);
|
||||
expect(screen.getByText(/ami-0e778053cd490ad21/i)).toBeVisible();
|
||||
expect(await screen.findByText(/ami-0e778053cd490ad21/i)).toBeVisible();
|
||||
await user.click(toggleButton);
|
||||
expect(screen.getByText(/ami-0e778053cd490ad21/i)).not.toBeVisible();
|
||||
expect(await screen.findByText(/ami-0e778053cd490ad21/i)).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('check error details', async () => {
|
||||
renderWithReduxRouter('', {});
|
||||
await renderWithReduxRouter('', {});
|
||||
|
||||
const table = await screen.findByTestId('images-table');
|
||||
const { getAllByRole } = within(table);
|
||||
const rows = getAllByRole('row');
|
||||
const { findAllByRole } = within(table);
|
||||
const rows = await findAllByRole('row');
|
||||
|
||||
const errorPopover = within(rows[2]).getByText(/image build failed/i);
|
||||
const errorPopover = await within(rows[2]).findByText(
|
||||
/image build failed/i
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getAllByText(/c1cfa347-4c37-49b5-8e73-6aa1d1746cfa/i)[1]
|
||||
|
|
@ -242,7 +222,7 @@ describe('Images Table', () => {
|
|||
|
||||
describe('Images Table Toolbar', () => {
|
||||
test('render toolbar', async () => {
|
||||
renderWithReduxRouter('', {});
|
||||
await renderWithReduxRouter('', {});
|
||||
await screen.findByTestId('images-table');
|
||||
|
||||
// check create image button
|
||||
|
|
@ -257,7 +237,7 @@ describe('Images Table Toolbar', () => {
|
|||
describe('Clones table', () => {
|
||||
const user = userEvent.setup();
|
||||
test('renders clones table', async () => {
|
||||
renderWithReduxRouter('', {});
|
||||
await renderWithReduxRouter('', {});
|
||||
|
||||
const table = await screen.findByTestId('images-table');
|
||||
|
||||
|
|
@ -330,7 +310,7 @@ describe('Clones table', () => {
|
|||
toTest.toHaveTextContent('Ready');
|
||||
break;
|
||||
case 2:
|
||||
toTest.toHaveTextContent('Image build failed');
|
||||
toTest.toHaveTextContent('Sharing image failed');
|
||||
break;
|
||||
// no default
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { screen } from '@testing-library/react';
|
||||
import { rest } from 'msw';
|
||||
|
||||
import api from '../../../api.js';
|
||||
import { IMAGE_BUILDER_API } from '../../../constants.js';
|
||||
import { mockComposesEmpty } from '../../fixtures/composes';
|
||||
import { server } from '../../mocks/server.js';
|
||||
import { renderWithReduxRouter } from '../../testUtils';
|
||||
|
||||
jest.mock('../../../store/actions/actions', () => {
|
||||
|
|
@ -34,11 +38,17 @@ describe('Landing Page', () => {
|
|||
});
|
||||
|
||||
test('renders EmptyState child component', async () => {
|
||||
server.use(
|
||||
rest.get(`${IMAGE_BUILDER_API}/composes`, (req, res, ctx) => {
|
||||
return res(ctx.status(200), ctx.json(mockComposesEmpty));
|
||||
})
|
||||
);
|
||||
|
||||
renderWithReduxRouter('', {});
|
||||
|
||||
// check action loads
|
||||
screen.getByTestId('create-image-action');
|
||||
await screen.findByTestId('create-image-action');
|
||||
// check table loads
|
||||
screen.getByTestId('empty-state');
|
||||
await screen.findByTestId('empty-state');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ import '@testing-library/jest-dom';
|
|||
import { screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import api from '../../../api.js';
|
||||
import ShareImageModal from '../../../Components/ShareImageModal/ShareImageModal';
|
||||
import { mockState } from '../../fixtures/composes';
|
||||
import { renderCustomRoutesWithReduxRouter } from '../../testUtils';
|
||||
|
||||
jest.mock('@redhat-cloud-services/frontend-components/useChrome', () => ({
|
||||
|
|
@ -37,9 +35,9 @@ const routes = [
|
|||
describe('Create Share To Regions Modal', () => {
|
||||
const user = userEvent.setup();
|
||||
test('validation', async () => {
|
||||
renderCustomRoutesWithReduxRouter(`share/${composeId}`, mockState, routes);
|
||||
await renderCustomRoutesWithReduxRouter(`share/${composeId}`, {}, routes);
|
||||
|
||||
const shareButton = screen.getByRole('button', { name: /share/i });
|
||||
const shareButton = await screen.findByRole('button', { name: /share/i });
|
||||
expect(shareButton).toBeDisabled();
|
||||
|
||||
let invalidAlert = screen.queryByText(
|
||||
|
|
@ -69,14 +67,14 @@ describe('Create Share To Regions Modal', () => {
|
|||
});
|
||||
|
||||
test('cancel button redirects to landing page', async () => {
|
||||
const { router } = renderCustomRoutesWithReduxRouter(
|
||||
const { router } = await renderCustomRoutesWithReduxRouter(
|
||||
`share/${composeId}`,
|
||||
mockState,
|
||||
{},
|
||||
routes
|
||||
);
|
||||
|
||||
const cancelButton = screen.getByRole('button', { name: /cancel/i });
|
||||
cancelButton.click();
|
||||
const cancelButton = await screen.findByRole('button', { name: /cancel/i });
|
||||
user.click(cancelButton);
|
||||
|
||||
// returns back to the landing page
|
||||
await waitFor(() =>
|
||||
|
|
@ -85,14 +83,14 @@ describe('Create Share To Regions Modal', () => {
|
|||
});
|
||||
|
||||
test('close button redirects to landing page', async () => {
|
||||
const { router } = renderCustomRoutesWithReduxRouter(
|
||||
const { router } = await renderCustomRoutesWithReduxRouter(
|
||||
`share/${composeId}`,
|
||||
mockState,
|
||||
{},
|
||||
routes
|
||||
);
|
||||
|
||||
const closeButton = screen.getByRole('button', { name: /close/i });
|
||||
closeButton.click();
|
||||
const closeButton = await screen.findByRole('button', { name: /close/i });
|
||||
user.click(closeButton);
|
||||
|
||||
// returns back to the landing page
|
||||
await waitFor(() =>
|
||||
|
|
@ -101,11 +99,13 @@ describe('Create Share To Regions Modal', () => {
|
|||
});
|
||||
|
||||
test('select options disabled correctly based on status and region', async () => {
|
||||
renderCustomRoutesWithReduxRouter(`share/${composeId}`, mockState, routes);
|
||||
renderCustomRoutesWithReduxRouter(`share/${composeId}`, {}, routes);
|
||||
|
||||
const selectToggle = screen.getByRole('button', { name: /options menu/i });
|
||||
const selectToggle = await screen.findByRole('button', {
|
||||
name: /options menu/i,
|
||||
});
|
||||
// eslint-disable-next-line testing-library/no-unnecessary-act
|
||||
userEvent.click(selectToggle);
|
||||
user.click(selectToggle);
|
||||
|
||||
// parent region disabled
|
||||
const usEast1 = await screen.findByRole('option', {
|
||||
|
|
@ -113,95 +113,10 @@ describe('Create Share To Regions Modal', () => {
|
|||
});
|
||||
expect(usEast1).toHaveClass('pf-m-disabled');
|
||||
|
||||
// successful clone disabled
|
||||
const usWest1 = screen.getByRole('option', {
|
||||
name: /us-west-1 us west \(n. california\)/i,
|
||||
});
|
||||
expect(usWest1).toHaveClass('pf-m-disabled');
|
||||
|
||||
// unsuccessful clone enabled
|
||||
const usWest2 = screen.getByRole('option', {
|
||||
name: /us-west-2 us west \(oregon\)/i,
|
||||
});
|
||||
expect(usWest2).not.toHaveClass('pf-m-disabled');
|
||||
|
||||
// successful clone with different share_with_accounts than its parent enabled
|
||||
const euCentral1 = screen.getByRole('option', {
|
||||
name: /eu-central-1 europe \(frankfurt\)/i,
|
||||
});
|
||||
expect(euCentral1).not.toHaveClass('pf-m-disabled');
|
||||
|
||||
// close the select again to avoid state update
|
||||
// eslint-disable-next-line testing-library/no-unnecessary-act
|
||||
await userEvent.click(selectToggle);
|
||||
await user.click(selectToggle);
|
||||
});
|
||||
|
||||
test('cloning an image results in successful store updates', async () => {
|
||||
const { router, store } = renderCustomRoutesWithReduxRouter(
|
||||
`share/${composeId}`,
|
||||
mockState,
|
||||
routes
|
||||
);
|
||||
|
||||
const selectToggle = screen.getByRole('button', { name: /options menu/i });
|
||||
user.click(selectToggle);
|
||||
|
||||
const usEast2 = await screen.findByRole('option', {
|
||||
name: /us-east-2 us east \(ohio\)/i,
|
||||
});
|
||||
expect(usEast2).not.toHaveClass('pf-m-disabled');
|
||||
user.click(usEast2);
|
||||
|
||||
const mockResponse = {
|
||||
id: '123e4567-e89b-12d3-a456-426655440000',
|
||||
};
|
||||
const cloneImage = jest.spyOn(api, 'cloneImage').mockImplementation(() => {
|
||||
return Promise.resolve(mockResponse);
|
||||
});
|
||||
|
||||
const shareButton = await screen.findByRole('button', { name: /share/i });
|
||||
await waitFor(() => expect(shareButton).toBeEnabled());
|
||||
user.click(shareButton);
|
||||
|
||||
await waitFor(() => expect(cloneImage).toHaveBeenCalledTimes(1));
|
||||
|
||||
// returns back to the landing page
|
||||
expect(router.state.location.pathname).toBe('/insights/image-builder');
|
||||
|
||||
// Clone has been added to its parent's list of clones
|
||||
expect(
|
||||
store.getState().composes.byId['1579d95b-8f1d-4982-8c53-8c2afa4ab04c']
|
||||
.clones
|
||||
).toEqual([
|
||||
'f9133ec4-7a9e-4fd9-9a9f-9636b82b0a5d',
|
||||
'48fce414-0cc0-4a16-8645-e3f0edec3212',
|
||||
'0169538e-515c-477e-b934-f12783939313',
|
||||
'4a851db1-919f-43ca-a7ef-dd209877a77e',
|
||||
'123e4567-e89b-12d3-a456-426655440000',
|
||||
]);
|
||||
|
||||
// Clone has been added to state.clones.allIds
|
||||
expect(store.getState().clones.allIds).toEqual([
|
||||
'f9133ec4-7a9e-4fd9-9a9f-9636b82b0a5d',
|
||||
'48fce414-0cc0-4a16-8645-e3f0edec3212',
|
||||
'0169538e-515c-477e-b934-f12783939313',
|
||||
'4a851db1-919f-43ca-a7ef-dd209877a77e',
|
||||
'123e4567-e89b-12d3-a456-426655440000',
|
||||
]);
|
||||
|
||||
// Clone has been added to state.clones.byId
|
||||
expect(
|
||||
store.getState().clones.byId['123e4567-e89b-12d3-a456-426655440000']
|
||||
).toEqual({
|
||||
id: '123e4567-e89b-12d3-a456-426655440000',
|
||||
image_status: {
|
||||
status: 'pending',
|
||||
},
|
||||
parent: '1579d95b-8f1d-4982-8c53-8c2afa4ab04c',
|
||||
request: {
|
||||
region: 'us-east-2',
|
||||
share_with_accounts: ['123123123123'],
|
||||
},
|
||||
});
|
||||
});
|
||||
// TODO Verify that sharing clones works once msw/data is incorporated.
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue