123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- /*
- * SonarQube
- * Copyright (C) 2009-2022 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 { queryHelpers, screen, within } from '@testing-library/react';
- import userEvent from '@testing-library/user-event';
- import * as React from 'react';
- import { SourceViewerServiceMock } from '../../../api/mocks/SourceViewerServiceMock';
- import { HttpStatus } from '../../../helpers/request';
- import { mockIssue } from '../../../helpers/testMocks';
- import { renderComponent } from '../../../helpers/testReactTestingUtils';
- import SourceViewer from '../SourceViewer';
-
- jest.mock('../../../api/components');
- jest.mock('../../../api/issues');
- jest.mock('../helpers/lines', () => {
- const lines = jest.requireActual('../helpers/lines');
- return {
- ...lines,
- LINES_TO_LOAD: 20
- };
- });
-
- const handler = new SourceViewerServiceMock();
-
- beforeEach(() => {
- handler.reset();
- });
-
- it('should show a permalink on line number', async () => {
- const user = userEvent.setup();
- renderSourceViewer();
- let row = await screen.findByRole('row', { name: /\/\*$/ });
- expect(row).toBeInTheDocument();
- const rowScreen = within(row);
- await user.click(
- rowScreen.getByRole('button', {
- name: 'source_viewer.line_X.1'
- })
- );
- await user.click(
- rowScreen.getByRole('link', {
- name: 'component_viewer.copy_permalink'
- })
- );
-
- expect(
- /* eslint-disable-next-line testing-library/prefer-presence-queries */
- queryHelpers.queryByAttribute(
- 'data-clipboard-text',
- row,
- 'http://localhost/code?id=project&selected=project%3Atest.js&line=1'
- )
- ).toBeInTheDocument();
-
- await user.keyboard('[Escape]');
-
- expect(
- /* eslint-disable-next-line testing-library/prefer-presence-queries */
- queryHelpers.queryByAttribute(
- 'data-clipboard-text',
- row,
- 'http://localhost/code?id=project&selected=project%3Atest.js&line=1'
- )
- ).not.toBeInTheDocument();
-
- row = await screen.findByRole('row', { name: / \* 6$/ });
- expect(row).toBeInTheDocument();
- const lowerRowScreen = within(row);
- await user.click(
- lowerRowScreen.getByRole('button', {
- name: 'source_viewer.line_X.6'
- })
- );
-
- expect(
- lowerRowScreen.getByRole('link', {
- name: 'component_viewer.copy_permalink'
- })
- ).toBeInTheDocument();
-
- await user.keyboard('[Escape]');
- });
-
- it('should show issue on empty file', async () => {
- renderSourceViewer({
- component: handler.getEmptyFile(),
- loadIssues: jest.fn().mockResolvedValue([
- mockIssue(false, {
- key: 'first-issue',
- message: 'First Issue',
- line: undefined,
- textRange: undefined
- })
- ])
- });
- expect(await screen.findByRole('table')).toBeInTheDocument();
- expect(await screen.findByRole('row', { name: 'First Issue' })).toBeInTheDocument();
- });
-
- it('should be able to interact with issue action', async () => {
- const user = userEvent.setup();
- renderSourceViewer({
- loadIssues: jest.fn().mockResolvedValue([
- mockIssue(false, {
- actions: ['set_type', 'set_tags', 'comment', 'set_severity', 'assign'],
- key: 'first-issue',
- message: 'First Issue',
- line: 1,
- textRange: { startLine: 1, endLine: 1, startOffset: 0, endOffset: 1 }
- })
- ])
- });
-
- //Open Issue type
- await user.click(
- await screen.findByRole('button', { name: 'issue.type.type_x_click_to_change.issue.type.BUG' })
- );
- expect(screen.getByRole('link', { name: 'issue.type.CODE_SMELL' })).toBeInTheDocument();
-
- // Open severity
- await user.click(
- await screen.findByRole('button', {
- name: 'issue.severity.severity_x_click_to_change.severity.MAJOR'
- })
- );
- expect(screen.getByRole('link', { name: 'severity.MINOR' })).toBeInTheDocument();
-
- // Close
- await user.keyboard('{Escape}');
- expect(screen.queryByRole('link', { name: 'severity.MINOR' })).not.toBeInTheDocument();
-
- // Change the severity
- await user.click(
- await screen.findByRole('button', {
- name: 'issue.severity.severity_x_click_to_change.severity.MAJOR'
- })
- );
- expect(screen.getByRole('link', { name: 'severity.MINOR' })).toBeInTheDocument();
- await user.click(screen.getByRole('link', { name: 'severity.MINOR' }));
- expect(
- screen.getByRole('button', {
- name: 'issue.severity.severity_x_click_to_change.severity.MINOR'
- })
- ).toBeInTheDocument();
- });
-
- it('should load line when looking arround unloaded line', async () => {
- const { rerender } = renderSourceViewer({
- aroundLine: 50,
- component: handler.getHugeFile()
- });
- expect(await screen.findByRole('row', { name: /Line 50$/ })).toBeInTheDocument();
- rerender(getSourceViewerUi({ aroundLine: 100, component: handler.getHugeFile() }));
-
- expect(await screen.findByRole('row', { name: /Line 100$/ })).toBeInTheDocument();
- });
-
- it('should show SCM information', async () => {
- const user = userEvent.setup();
- renderSourceViewer();
- let row = await screen.findByRole('row', { name: /\/\*$/ });
- expect(row).toBeInTheDocument();
- const firstRowScreen = within(row);
- expect(
- firstRowScreen.getByRole('cell', { name: 'stas.vilchik@sonarsource.com' })
- ).toBeInTheDocument();
- await user.click(
- firstRowScreen.getByRole('button', {
- name: 'source_viewer.author_X.stas.vilchik@sonarsource.com, source_viewer.click_for_scm_info'
- })
- );
-
- expect(
- await firstRowScreen.findByRole('heading', { level: 4, name: 'author' })
- ).toBeInTheDocument();
- expect(
- firstRowScreen.getByRole('heading', { level: 4, name: 'source_viewer.tooltip.scm.commited_on' })
- ).toBeInTheDocument();
- expect(
- firstRowScreen.getByRole('heading', { level: 4, name: 'source_viewer.tooltip.scm.revision' })
- ).toBeInTheDocument();
-
- row = screen.getByRole('row', { name: /\* SonarQube$/ });
- expect(row).toBeInTheDocument();
- const secondRowScreen = within(row);
- expect(
- secondRowScreen.queryByRole('cell', { name: 'stas.vilchik@sonarsource.com' })
- ).not.toBeInTheDocument();
-
- // SCM with no date
- row = await screen.findByRole('row', { name: /\* mailto:info AT sonarsource DOT com$/ });
- expect(row).toBeInTheDocument();
- const thirdRowScreen = within(row);
- await user.click(
- thirdRowScreen.getByRole('button', {
- name: 'source_viewer.author_X.stas.vilchik@sonarsource.com, source_viewer.click_for_scm_info'
- })
- );
-
- expect(
- await thirdRowScreen.findByRole('heading', { level: 4, name: 'author' })
- ).toBeInTheDocument();
- expect(
- thirdRowScreen.queryByRole('heading', {
- level: 4,
- name: 'source_viewer.tooltip.scm.commited_on'
- })
- ).not.toBeInTheDocument();
- expect(
- thirdRowScreen.getByRole('heading', { level: 4, name: 'source_viewer.tooltip.scm.revision' })
- ).toBeInTheDocument();
-
- // SCM with no date no author
- row = await screen.findByRole('row', { name: /\* 5$/ });
- expect(row).toBeInTheDocument();
- const fourthRowScreen = within(row);
- expect(fourthRowScreen.getByText('…')).toBeInTheDocument();
- await user.click(
- fourthRowScreen.getByRole('button', {
- name: 'source_viewer.click_for_scm_info'
- })
- );
-
- expect(
- fourthRowScreen.queryByRole('heading', { level: 4, name: 'author' })
- ).not.toBeInTheDocument();
- expect(
- fourthRowScreen.queryByRole('heading', {
- level: 4,
- name: 'source_viewer.tooltip.scm.commited_on'
- })
- ).not.toBeInTheDocument();
- expect(
- fourthRowScreen.getByRole('heading', { level: 4, name: 'source_viewer.tooltip.scm.revision' })
- ).toBeInTheDocument();
-
- // No SCM Popup
- row = await screen.findByRole('row', {
- name: /\* This program is free software; you can redistribute it and\/or$/
- });
- expect(row).toBeInTheDocument();
- expect(within(row).queryByRole('button')).not.toBeInTheDocument();
- });
-
- it('should show issue indicator', async () => {
- const user = userEvent.setup();
- const onIssueSelect = jest.fn();
- renderSourceViewer({
- onIssueSelect,
- displayAllIssues: false,
- loadIssues: jest.fn().mockResolvedValue([
- mockIssue(false, {
- key: 'first-issue',
- message: 'First Issue',
- line: 1,
- textRange: { startLine: 1, endLine: 1, startOffset: 0, endOffset: 1 }
- }),
- mockIssue(false, {
- key: 'second-issue',
- message: 'Second Issue',
- line: 1,
- textRange: { startLine: 1, endLine: 1, startOffset: 1, endOffset: 2 }
- })
- ])
- });
- const row = await screen.findByRole('row', { name: /.*\/ \*$/ });
- const issueRow = within(row);
- expect(issueRow.getByText('2')).toBeInTheDocument();
- await user.click(issueRow.getByRole('button', { name: 'source_viewer.issues_on_line.show' }));
- const firstIssueBox = issueRow.getByRole('region', { name: 'First Issue' });
- const secondIssueBox = issueRow.getByRole('region', { name: 'Second Issue' });
- expect(firstIssueBox).toBeInTheDocument();
- expect(secondIssueBox).toBeInTheDocument();
-
- await user.click(firstIssueBox);
- expect(onIssueSelect).toBeCalledWith('first-issue');
-
- await user.click(secondIssueBox);
- expect(onIssueSelect).toBeCalledWith('second-issue');
- });
-
- it('should show coverage information', async () => {
- renderSourceViewer();
- const coverdLine = within(
- await screen.findByRole('row', { name: /\* mailto:info AT sonarsource DOT com$/ })
- );
- expect(
- coverdLine.getByLabelText('source_viewer.tooltip.covered.conditions.1')
- ).toBeInTheDocument();
-
- const partialyCoveredWithConditionLine = within(
- await screen.findByRole('row', { name: / \* 5$/ })
- );
- expect(
- partialyCoveredWithConditionLine.getByLabelText(
- 'source_viewer.tooltip.partially-covered.conditions.1.2'
- )
- ).toBeInTheDocument();
-
- const partialyCoveredLine = within(await screen.findByRole('row', { name: /\/\*$/ }));
- expect(
- partialyCoveredLine.getByLabelText('source_viewer.tooltip.partially-covered')
- ).toBeInTheDocument();
-
- const uncoveredLine = within(await screen.findByRole('row', { name: / \* 6$/ }));
- expect(uncoveredLine.getByLabelText('source_viewer.tooltip.uncovered')).toBeInTheDocument();
-
- const uncoveredWithConditionLine = within(
- await screen.findByRole('row', { name: / \* SonarQube$/ })
- );
- expect(
- uncoveredWithConditionLine.getByLabelText('source_viewer.tooltip.uncovered.conditions.1')
- ).toBeInTheDocument();
-
- const coveredWithNoCondition = within(await screen.findByRole('row', { name: /\* Copyright$/ }));
- expect(
- coveredWithNoCondition.getByLabelText('source_viewer.tooltip.covered')
- ).toBeInTheDocument();
- });
-
- it('should show duplication block', async () => {
- const user = userEvent.setup();
- renderSourceViewer();
- const duplicateLine = within(await screen.findByRole('row', { name: /\* 7$/ }));
- expect(
- duplicateLine.getByLabelText('source_viewer.tooltip.duplicated_block')
- ).toBeInTheDocument();
-
- await user.click(
- duplicateLine.getByRole('button', { name: 'source_viewer.tooltip.duplicated_block' })
- );
-
- expect(duplicateLine.getAllByRole('link', { name: 'test2.js' })[0]).toBeInTheDocument();
- await user.keyboard('[Escape]');
- expect(duplicateLine.queryByRole('link', { name: 'test2.js' })).not.toBeInTheDocument();
- });
-
- it('should highlight symbol', async () => {
- const user = userEvent.setup();
- renderSourceViewer({ component: 'project:testSymb.tsx' });
- const symbols = await screen.findAllByText('symbole');
- await user.click(symbols[0]);
-
- // For now just check the class. Maybe found a better accessible way of showing higlighted symbole
- symbols.forEach(element => {
- expect(element).toHaveClass('highlighted');
- });
- });
-
- it('should show correct message when component is not asscessible', async () => {
- handler.setFailLoadingComponentStatus(HttpStatus.Forbidden);
- renderSourceViewer();
- expect(
- await screen.findByText('code_viewer.no_source_code_displayed_due_to_security')
- ).toBeInTheDocument();
- });
-
- it('should show correct message when component does not exist', async () => {
- handler.setFailLoadingComponentStatus(HttpStatus.NotFound);
- renderSourceViewer();
- expect(await screen.findByText('component_viewer.no_component')).toBeInTheDocument();
- });
-
- function renderSourceViewer(override?: Partial<SourceViewer['props']>) {
- return renderComponent(getSourceViewerUi(override));
- }
-
- function getSourceViewerUi(override?: Partial<SourceViewer['props']>) {
- return (
- <SourceViewer
- aroundLine={1}
- branchLike={undefined}
- component={handler.getFileWithSource()}
- displayAllIssues={true}
- displayIssueLocationsCount={true}
- displayIssueLocationsLink={false}
- displayLocationMarkers={true}
- loadIssues={jest.fn().mockResolvedValue([])}
- onIssueChange={jest.fn()}
- onIssueSelect={jest.fn()}
- onLoaded={jest.fn()}
- onLocationSelect={jest.fn()}
- scroll={jest.fn()}
- slimHeader={true}
- {...override}
- />
- );
- }
|