123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- /*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- import { screen } from '@testing-library/react';
- import userEvent from '@testing-library/user-event';
- import * as React from 'react';
- import { mockFlowLocation, mockIssue, mockPaging } from '../../../../helpers/testMocks';
- import { renderComponent } from '../../../../helpers/testReactTestingUtils';
- import { byRole } from '../../../../helpers/testSelector';
- import { ComponentPropsType } from '../../../../helpers/testUtils';
- import { FlowType, Issue } from '../../../../types/types';
- import { VISIBLE_LOCATIONS_COLLAPSE } from '../IssueLocationsCrossFile';
- import SubnavigationIssuesList from '../SubnavigationIssuesList';
-
- const loc = mockFlowLocation();
- const issues = [
- mockIssue(false, {
- key: 'issue1',
- message: 'Issue 1',
- component: 'foo',
- componentLongName: 'Long Foo',
- }),
- mockIssue(false, {
- key: 'issue2',
- message: 'Issue 2',
- component: 'foo',
- componentLongName: 'Long Foo',
- }),
- mockIssue(false, {
- key: 'issue3',
- message: 'Issue 3',
- component: 'bar',
- componentLongName: 'Long Bar',
- }),
- mockIssue(false, {
- key: 'issue4',
- message: 'Issue 4',
- component: 'foo',
- componentLongName: 'Long Foo',
- flowsWithType: [
- {
- type: FlowType.DATA,
- description: 'Flow Foo',
- locations: [mockFlowLocation({ msg: 'loc 1' })],
- },
- ],
- }),
- ];
-
- describe('rendering', () => {
- it('should render concise issues without duplicating component', () => {
- renderConciseIssues(issues);
-
- expect(screen.getAllByTitle('Long Foo')).toHaveLength(2);
- expect(screen.getByTitle('Long Bar')).toBeInTheDocument();
- });
-
- it('should scroll issue into view when one of the issue is selected', () => {
- renderConciseIssues(issues, {
- selected: 'issue2',
- });
- });
-
- it('should show locations and flows when selected', () => {
- renderConciseIssues(issues, {
- selected: 'issue4',
- selectedFlowIndex: 0,
- });
-
- expect(screen.getByText('Flow Foo')).toBeInTheDocument();
- expect(screen.getByText('loc 1')).toBeInTheDocument();
- });
-
- it('should hide locations and flows when not selected', () => {
- renderConciseIssues(issues, {
- selected: 'issue2',
- });
-
- expect(screen.queryByText('Flow Foo')).not.toBeInTheDocument();
- expect(screen.queryByText('loc 1')).not.toBeInTheDocument();
- });
-
- it('should not render the expand button if below the collapse limit', () => {
- const { ui } = getPageObject();
- renderConciseIssues(
- [
- ...issues,
- mockIssue(false, {
- key: 'custom',
- message: 'Custom Issue',
- flows: Array.from({ length: VISIBLE_LOCATIONS_COLLAPSE }).map((i) => [
- mockFlowLocation({ component: `component-${i}` }),
- ]),
- }),
- ],
- {
- selected: 'custom',
- },
- );
-
- expect(ui.expandBadgesButton.query()).not.toBeInTheDocument();
- });
- });
-
- describe('interacting', () => {
- it('should scroll selected issue into view', () => {
- const scrollIntoView = jest.fn();
- const globalScrollView = window.HTMLElement.prototype.scrollIntoView;
- window.HTMLElement.prototype.scrollIntoView = scrollIntoView;
- const { override } = renderConciseIssues(issues, {
- selected: 'issue2',
- });
-
- expect(scrollIntoView).toHaveBeenCalledTimes(1);
-
- override(issues, {
- selected: 'issue4',
- });
- expect(scrollIntoView).toHaveBeenCalledTimes(2);
- window.HTMLElement.prototype.scrollIntoView = globalScrollView;
- });
-
- it('expand button should work correctly', async () => {
- const { ui } = getPageObject();
- const flow = Array.from({ length: VISIBLE_LOCATIONS_COLLAPSE + 1 }).map((_, i) =>
- mockFlowLocation({
- component: `component-${i}`,
- index: i,
- msg: `loc ${i}`,
- }),
- );
-
- renderConciseIssues(
- [
- mockIssue(false, {
- key: 'custom',
- component: 'issue-component',
- message: 'Custom Issue',
- flows: [flow],
- }),
- ],
- {
- selected: 'custom',
- selectedFlowIndex: 0,
- },
- );
-
- expect(ui.expandBadgesButton.get()).toBeInTheDocument();
- expect(screen.getAllByText(/loc \d/)).toHaveLength(VISIBLE_LOCATIONS_COLLAPSE);
- await ui.clickExpandBadgesButton();
-
- expect(ui.expandBadgesButton.query()).not.toBeInTheDocument();
- expect(screen.getAllByText(/loc \d/)).toHaveLength(VISIBLE_LOCATIONS_COLLAPSE + 1);
- });
-
- it('issue selection should correctly be handled', async () => {
- const { user } = getPageObject();
- const onIssueSelect = jest.fn();
- renderConciseIssues(issues, {
- onIssueSelect,
- selected: 'issue2',
- });
-
- expect(onIssueSelect).not.toHaveBeenCalled();
-
- await user.click(screen.getByRole('button', { name: 'Issue 4' }));
- expect(onIssueSelect).toHaveBeenCalledTimes(1);
- expect(onIssueSelect).toHaveBeenLastCalledWith('issue4');
- });
-
- it('flow selection should correctly be handled', async () => {
- const { user } = getPageObject();
- const onFlowSelect = jest.fn();
- renderConciseIssues(
- [
- ...issues,
- mockIssue(false, {
- key: 'custom',
- message: 'Custom Issue',
- secondaryLocations: [],
- flows: [[loc], [loc, loc, loc], [loc]],
- }),
- ],
- {
- onFlowSelect,
- selected: 'custom',
- },
- );
-
- expect(onFlowSelect).not.toHaveBeenCalled();
-
- await user.click(screen.getByText('issue.flow.x_steps.3'));
- expect(onFlowSelect).toHaveBeenCalledTimes(1);
- expect(onFlowSelect).toHaveBeenLastCalledWith(1);
- });
- });
-
- function getPageObject() {
- const selectors = {
- headerBackButton: byRole('link', { name: 'issues.return_to_list' }),
- expandBadgesButton: byRole('button', { name: /issues.show_x_more_locations.\d/ }),
- };
- const user = userEvent.setup();
- const ui = {
- ...selectors,
- async clickBackButton() {
- await user.click(ui.headerBackButton.get());
- },
- async clickExpandBadgesButton() {
- await user.click(ui.expandBadgesButton.get());
- },
- };
- return { ui, user };
- }
-
- function renderConciseIssues(
- issues: Issue[],
- listProps: Partial<ComponentPropsType<typeof SubnavigationIssuesList>> = {},
- ) {
- const wrapper = renderComponent(
- <SubnavigationIssuesList
- fetchMoreIssues={jest.fn()}
- loading={false}
- loadingMore={false}
- paging={mockPaging({ total: 10 })}
- issues={issues}
- onFlowSelect={jest.fn()}
- onIssueSelect={jest.fn()}
- onLocationSelect={jest.fn()}
- selected={undefined}
- selectedFlowIndex={undefined}
- selectedLocationIndex={undefined}
- {...listProps}
- />,
- );
-
- function override(
- issues: Issue[],
- listProps: Partial<ComponentPropsType<typeof SubnavigationIssuesList>> = {},
- ) {
- wrapper.rerender(
- <SubnavigationIssuesList
- fetchMoreIssues={jest.fn()}
- issues={issues}
- loading={false}
- loadingMore={false}
- paging={mockPaging({ total: 10 })}
- onFlowSelect={jest.fn()}
- onIssueSelect={jest.fn()}
- onLocationSelect={jest.fn()}
- selected={undefined}
- selectedFlowIndex={undefined}
- selectedLocationIndex={undefined}
- {...listProps}
- />,
- );
- }
-
- return { ...wrapper, override };
- }
|