/* eslint-disable no-useless-escape */ import React from 'react'; import { renderWithRedux, patientlyWaitFor, fireEvent } from 'react-testing-lib-wrapper'; import CONTENT_VIEWS_KEY from '../ContentViewsConstants'; import ContentViewsPage from '../../ContentViews'; import api from '../../../services/api'; import nock, { nockInstance, assertNockRequest, mockAutocomplete, mockSetting, } from '../../../test-utils/nockWrapper'; import createBasicCVs from './basicContentViews.fixtures'; import cvIndexData from './contentViewList.fixtures.json'; const cvIndexPath = api.getApiUrl('/content_views'); const autocompleteUrl = '/content_views/auto_complete_search'; const renderOptions = { apiNamespace: CONTENT_VIEWS_KEY }; let firstCV; let searchDelayScope; let autoSearchScope; let scopeBookmark; beforeEach(() => { const { results } = cvIndexData; [firstCV] = results; scopeBookmark = nockInstance .get('/api/v2/bookmarks') .query(true) .reply(200, {}); searchDelayScope = mockSetting(nockInstance, 'autosearch_delay', 0); autoSearchScope = mockSetting(nockInstance, 'autosearch_while_typing'); }); afterEach(() => { nock.cleanAll(); assertNockRequest(searchDelayScope); assertNockRequest(autoSearchScope); }); test('Can call API for CVs and show on screen on page load', async (done) => { const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl); const scope = nockInstance .get(cvIndexPath) .query(true) .reply(200, cvIndexData); const { queryByText } = renderWithRedux(<ContentViewsPage />, renderOptions); expect(queryByText(firstCV.name)).toBeNull(); // Assert that the CV name is now showing on the screen, but wait for it to appear. await patientlyWaitFor(() => { expect(queryByText(firstCV.name)).toBeInTheDocument(); expect(queryByText('Component content views')).toBeInTheDocument(); expect(queryByText('Composite content views')).toBeInTheDocument(); }); assertNockRequest(scopeBookmark); assertNockRequest(autocompleteScope); assertNockRequest(scope, done); }); test('Can show last task and link to it', async (done) => { const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl); const scope = nockInstance .get(cvIndexPath) .query(true) .reply(200, cvIndexData); const { getByText, queryByText } = renderWithRedux(<ContentViewsPage />, renderOptions); expect(queryByText(firstCV.name)).toBeNull(); await patientlyWaitFor(() => { expect(queryByText(firstCV.name)).toBeTruthy(); // Reads task details and displays link to the task expect(getByText('3 days ago').closest('a')) .toHaveAttribute('href', '/foreman_tasks/tasks/54088dac-b990-491c-a891-1d7d1a3f5161/'); // If no task is found display empty text N/A expect(queryByText('N/A')).toBeTruthy(); }); assertNockRequest(scopeBookmark); assertNockRequest(autocompleteScope); assertNockRequest(scope, done); // Pass jest callback to confirm test is done }); test('Can show latest version and link to it', async (done) => { const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl); const scope = nockInstance .get(cvIndexPath) .query(true) .reply(200, cvIndexData); const { getByText, queryByText, queryAllByText, } = renderWithRedux(<ContentViewsPage />, renderOptions); expect(queryByText(firstCV.name)).toBeNull(); await patientlyWaitFor(() => { expect(queryByText(firstCV.name)).toBeTruthy(); // Displays link to the latest version expect(getByText('Version 1.0').closest('a')) .toHaveAttribute('href', '/content_views/2/versions/11/'); // If no task is found display empty text Not yet published if latest version is null expect(queryAllByText('Not yet published')[0]).toBeTruthy(); // Able to display Environment labels with link to the environment expect(getByText('Library').closest('a')) .toHaveAttribute('href', '/lifecycle_environments/1'); expect(getByText('dev').closest('a')) .toHaveAttribute('href', '/lifecycle_environments/2'); }); assertNockRequest(scopeBookmark); assertNockRequest(autocompleteScope); assertNockRequest(scope, done); // Pass jest callback to confirm test is done }); test('Can expand cv and show activation keys and hosts', async (done) => { const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl); const scope = nockInstance .get(cvIndexPath) .query(true) .reply(200, cvIndexData); const { queryByLabelText, getAllByLabelText, queryByText, } = renderWithRedux(<ContentViewsPage />, renderOptions); expect(queryByText(firstCV.name)).toBeNull(); await patientlyWaitFor(() => { expect(queryByText(firstCV.name)).toBeTruthy(); }); expect(getAllByLabelText('Details')[0]).toHaveAttribute('aria-expanded', 'false'); // Expand content for first CV getAllByLabelText('Details')[0].click(); await patientlyWaitFor(() => { expect(getAllByLabelText('Details')[0]).toHaveAttribute('aria-expanded', 'true'); // Displays activation key link with count expect(queryByLabelText('activation_keys_link_2')).toHaveAttribute('href', '/activation_keys?search=content_view_id+%3D+2'); expect(queryByLabelText('activation_keys_link_2').textContent).toEqual('1'); // Displays hosts link with count expect(queryByLabelText('host_link_2')).toHaveAttribute('href', '/hosts?search=content_view_id+%3D+2'); expect(queryByLabelText('host_link_2').textContent).toEqual('1'); }); assertNockRequest(autocompleteScope); assertNockRequest(scope, done); // Pass jest callback to confirm test is done }); test('Can handle no Content Views being present', async (done) => { const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl); const noResults = { total: 0, subtotal: 0, page: 1, per_page: 20, results: [], }; const scope = nockInstance .get(cvIndexPath) .query(true) .reply(200, noResults); const { queryByText } = renderWithRedux(<ContentViewsPage />, renderOptions); expect(queryByText(firstCV.name)).toBeNull(); await patientlyWaitFor(() => expect(queryByText(/don't have any Content views/i)).toBeInTheDocument()); assertNockRequest(autocompleteScope); assertNockRequest(scope, done); }); test('Can handle errored response', async (done) => { const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl); const scope = nockInstance .get(cvIndexPath) .query(true) .reply(500); const { queryByText } = renderWithRedux(<ContentViewsPage />, renderOptions); expect(queryByText(firstCV.name)).toBeNull(); await patientlyWaitFor(() => expect(queryByText(/Something went wrong! Please check server logs!/i)).toBeInTheDocument()); assertNockRequest(autocompleteScope); assertNockRequest(scope, done); }); test('Can handle unpublished Content Views', async (done) => { const { results } = cvIndexData; const unpublishedCVs = results.map(cv => ({ ...cv, last_published: null })); const unpublishedCVData = { ...cvIndexData, results: unpublishedCVs }; const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl); const scope = nockInstance .get(cvIndexPath) .query(true) .reply(200, unpublishedCVData); const { getAllByText } = renderWithRedux(<ContentViewsPage />, renderOptions); await patientlyWaitFor(() => expect(getAllByText(/not yet published/i).length).toBeGreaterThan(0)); assertNockRequest(autocompleteScope); assertNockRequest(scope, done); }); test('Can handle pagination', async (done) => { const cvIndexLarge = createBasicCVs(100); const { results } = cvIndexLarge; const cvIndexFirstPage = { ...cvIndexLarge, ...{ results: results.slice(0, 20) } }; const cvIndexSecondPage = { ...cvIndexLarge, page: 2, results: results.slice(20, 40) }; const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl); // Match first page API request const firstPageScope = nockInstance .get(cvIndexPath) // Using a custom query params matcher because parameters can be strings .query(actualQueryObject => (parseInt(actualQueryObject.page, 10) === 1)) .reply(200, cvIndexFirstPage); // Match second page API request const secondPageScope = nockInstance .get(cvIndexPath) // Using a custom query params matcher because parameters can be strings .query(actualQueryObject => (parseInt(actualQueryObject.page, 10) === 2)) .reply(200, cvIndexSecondPage); const { queryByText, getAllByLabelText } = renderWithRedux(<ContentViewsPage />, renderOptions); // Wait for first paginated page to load and assert only the first page of results are present await patientlyWaitFor(() => { expect(queryByText(results[0].name)).toBeInTheDocument(); expect(queryByText(results[19].name)).toBeInTheDocument(); expect(queryByText(results[21].name)).not.toBeInTheDocument(); }); // Label comes from patternfly, if this test fails, check if patternfly updated the label. const [top, bottom] = getAllByLabelText('Go to next page'); expect(top).toBeInTheDocument(); expect(bottom).toBeInTheDocument(); bottom.click(); // Wait for second paginated page to load and assert only the second page of results are present await patientlyWaitFor(() => { expect(queryByText(results[20].name)).toBeInTheDocument(); expect(queryByText(results[39].name)).toBeInTheDocument(); expect(queryByText(results[41].name)).not.toBeInTheDocument(); }); assertNockRequest(autocompleteScope); assertNockRequest(firstPageScope); assertNockRequest(secondPageScope, done); // Only pass jest callback to the last API request }); test('Can search for specific Content View', async (done) => { const cvname = 'composite one'; const { results } = cvIndexData; const matchQuery = actualParams => actualParams?.search?.includes(cvname); const searchResults = { ...cvIndexData, ...{ total: 1, subtotal: 1, results: results.slice(-1) }, }; const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl); const withSearchScope = mockAutocomplete(nockInstance, autocompleteUrl, matchQuery); const initialScope = nockInstance .get(cvIndexPath) .query(true) .reply(200, cvIndexData); const searchResultScope = nockInstance .get(cvIndexPath) .query(matchQuery) .reply(200, searchResults); const { getByLabelText, getByText, queryByText, } = renderWithRedux(<ContentViewsPage />, renderOptions); await patientlyWaitFor(() => expect(getByText(firstCV.name)).toBeInTheDocument()); const searchInput = getByLabelText(/text input for search/i); expect(searchInput).toBeInTheDocument(); fireEvent.change(searchInput, { target: { value: `name = \"${cvname}\"` } }); await patientlyWaitFor(() => { expect(getByText(cvname)).toBeInTheDocument(); expect(queryByText(firstCV.name)).not.toBeInTheDocument(); }); assertNockRequest(autocompleteScope); assertNockRequest(initialScope); assertNockRequest(withSearchScope); assertNockRequest(searchResultScope, done); }); test('No results message is shown for empty search', async (done) => { const cvname = 'notanactualname'; const query = `name = \"${cvname}\"`; const matchQuery = actualParams => actualParams?.search?.includes(cvname); const emptyResults = { total: 0, subtotal: 0, page: 1, per_page: 20, search: query, results: [], }; const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl); const withSearchScope = mockAutocomplete(nockInstance, autocompleteUrl, matchQuery); const initialScope = nockInstance .get(cvIndexPath) .query(true) .reply(200, cvIndexData); const searchResultScope = nockInstance .get(cvIndexPath) .query(matchQuery) .reply(200, emptyResults); const { getByLabelText, getByText } = renderWithRedux(<ContentViewsPage />, renderOptions); await patientlyWaitFor(() => expect(getByText(firstCV.name)).toBeInTheDocument()); fireEvent.change(getByLabelText(/text input for search/i), { target: { value: query } }); await patientlyWaitFor(() => expect(getByText(/No matching content views found/i)).toBeInTheDocument()); assertNockRequest(autocompleteScope); assertNockRequest(initialScope); assertNockRequest(withSearchScope); assertNockRequest(searchResultScope, done); }); test('Displays Create Content View and opens modal with Form', async () => { mockAutocomplete(nockInstance, autocompleteUrl); const noResults = { total: 0, subtotal: 0, page: 1, per_page: 20, can_create: true, results: [], }; nockInstance .get(cvIndexPath) .query(true) .reply(200, noResults); const { getByText, queryByText, getByLabelText, } = renderWithRedux(<ContentViewsPage />, renderOptions); await patientlyWaitFor(() => expect(queryByText('Create content view')).toBeInTheDocument()); expect(queryByText('Description')).not.toBeInTheDocument(); expect(queryByText('Name')).not.toBeInTheDocument(); expect(queryByText('Label')).not.toBeInTheDocument(); expect(queryByText('Composite content view')).not.toBeInTheDocument(); expect(queryByText('Component content view')).not.toBeInTheDocument(); expect(queryByText('Solve dependencies')).not.toBeInTheDocument(); expect(queryByText('Auto publish')).not.toBeInTheDocument(); expect(queryByText('Import only')).not.toBeInTheDocument(); getByLabelText('create_content_view').click(); expect(getByText('Description')).toBeInTheDocument(); expect(getByText('Name')).toBeInTheDocument(); expect(getByText('Label')).toBeInTheDocument(); expect(getByText('Composite content view')).toBeInTheDocument(); expect(getByText('Component content view')).toBeInTheDocument(); expect(getByText('Solve dependencies')).toBeInTheDocument(); expect(queryByText('Auto publish')).not.toBeInTheDocument(); expect(getByText('Import only')).toBeInTheDocument(); }); /* eslint-enable no-useless-escape */