diff options
Diffstat (limited to 'spec/frontend/reports')
6 files changed, 251 insertions, 14 deletions
diff --git a/spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js b/spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js index 3e11af9c9df..f99dcbffdff 100644 --- a/spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js +++ b/spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js @@ -1,10 +1,15 @@ +import { GlIcon } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import component from '~/reports/codequality_report/components/codequality_issue_body.vue'; import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '~/reports/constants'; describe('code quality issue body issue body', () => { let wrapper; + const findSeverityIcon = () => wrapper.findByTestId('codequality-severity-icon'); + const findGlIcon = () => wrapper.find(GlIcon); + const codequalityIssue = { name: 'rubygem-rest-client: session fixation vulnerability via Set-Cookie headers in 30x redirection responses', @@ -14,13 +19,15 @@ describe('code quality issue body issue body', () => { urlPath: '/Gemfile.lock#L22', }; - const mountWithStatus = initialStatus => { - wrapper = shallowMount(component, { - propsData: { - issue: codequalityIssue, - status: initialStatus, - }, - }); + const createComponent = (initialStatus, issue = codequalityIssue) => { + wrapper = extendedWrapper( + shallowMount(component, { + propsData: { + issue, + status: initialStatus, + }, + }), + ); }; afterEach(() => { @@ -28,17 +35,43 @@ describe('code quality issue body issue body', () => { wrapper = null; }); + describe('severity rating', () => { + it.each` + severity | iconClass | iconName + ${'info'} | ${'text-primary-400'} | ${'severity-info'} + ${'minor'} | ${'text-warning-200'} | ${'severity-low'} + ${'major'} | ${'text-warning-400'} | ${'severity-medium'} + ${'critical'} | ${'text-danger-600'} | ${'severity-high'} + ${'blocker'} | ${'text-danger-800'} | ${'severity-critical'} + ${'unknown'} | ${'text-secondary-400'} | ${'severity-unknown'} + ${'invalid'} | ${'text-secondary-400'} | ${'severity-unknown'} + `( + 'renders correct icon for "$severity" severity rating', + ({ severity, iconClass, iconName }) => { + createComponent(STATUS_FAILED, { + ...codequalityIssue, + severity, + }); + const icon = findGlIcon(); + + expect(findSeverityIcon().classes()).toContain(iconClass); + expect(icon.exists()).toBe(true); + expect(icon.props('name')).toBe(iconName); + }, + ); + }); + describe('with success', () => { it('renders fixed label', () => { - mountWithStatus(STATUS_SUCCESS); + createComponent(STATUS_SUCCESS); expect(wrapper.text()).toContain('Fixed'); }); }); describe('without success', () => { - it('renders fixed label', () => { - mountWithStatus(STATUS_FAILED); + it('does not render fixed label', () => { + createComponent(STATUS_FAILED); expect(wrapper.text()).not.toContain('Fixed'); }); @@ -46,7 +79,7 @@ describe('code quality issue body issue body', () => { describe('name', () => { it('renders name', () => { - mountWithStatus(STATUS_NEUTRAL); + createComponent(STATUS_NEUTRAL); expect(wrapper.text()).toContain(codequalityIssue.name); }); @@ -54,7 +87,7 @@ describe('code quality issue body issue body', () => { describe('path', () => { it('renders the report-link path using the correct code quality issue', () => { - mountWithStatus(STATUS_NEUTRAL); + createComponent(STATUS_NEUTRAL); expect(wrapper.find('report-link-stub').props('issue')).toBe(codequalityIssue); }); diff --git a/spec/frontend/reports/components/grouped_test_reports_app_spec.js b/spec/frontend/reports/components/grouped_test_reports_app_spec.js index 556904b7da5..ae2718db17f 100644 --- a/spec/frontend/reports/components/grouped_test_reports_app_spec.js +++ b/spec/frontend/reports/components/grouped_test_reports_app_spec.js @@ -1,11 +1,13 @@ import { mount, createLocalVue } from '@vue/test-utils'; import Vuex from 'vuex'; +import { mockTracking } from 'helpers/tracking_helper'; import GroupedTestReportsApp from '~/reports/components/grouped_test_reports_app.vue'; import { getStoreConfig } from '~/reports/store'; import { failedReport } from '../mock_data/mock_data'; import successTestReports from '../mock_data/no_failures_report.json'; import newFailedTestReports from '../mock_data/new_failures_report.json'; +import recentFailuresTestReports from '../mock_data/recent_failures_report.json'; import newErrorsTestReports from '../mock_data/new_errors_report.json'; import mixedResultsTestReports from '../mock_data/new_and_fixed_failures_report.json'; import resolvedFailures from '../mock_data/resolved_failures.json'; @@ -20,7 +22,7 @@ describe('Grouped test reports app', () => { let wrapper; let mockStore; - const mountComponent = ({ props = { pipelinePath } } = {}) => { + const mountComponent = ({ props = { pipelinePath }, testFailureHistory = false } = {}) => { wrapper = mount(Component, { store: mockStore, localVue, @@ -29,6 +31,11 @@ describe('Grouped test reports app', () => { pipelinePath, ...props, }, + provide: { + glFeatures: { + testFailureHistory, + }, + }, }); }; @@ -39,6 +46,7 @@ describe('Grouped test reports app', () => { }; const findHeader = () => wrapper.find('[data-testid="report-section-code-text"]'); + const findExpandButton = () => wrapper.find('[data-testid="report-section-expand-button"]'); const findFullTestReportLink = () => wrapper.find('[data-testid="group-test-reports-full-link"]'); const findSummaryDescription = () => wrapper.find('[data-testid="test-summary-row-description"]'); const findIssueDescription = () => wrapper.find('[data-testid="test-issue-body-description"]'); @@ -96,6 +104,35 @@ describe('Grouped test reports app', () => { }); }); + describe('`Expand` button', () => { + let trackingSpy; + + beforeEach(() => { + setReports(newFailedTestReports); + mountComponent(); + document.body.dataset.page = 'projects:merge_requests:show'; + trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn); + }); + + it('tracks an event on click', () => { + findExpandButton().trigger('click'); + + expect(trackingSpy).toHaveBeenCalledWith(undefined, 'expand_test_report_widget', {}); + }); + + it('only tracks the first expansion', () => { + expect(trackingSpy).not.toHaveBeenCalled(); + + const button = findExpandButton(); + + button.trigger('click'); + button.trigger('click'); + button.trigger('click'); + + expect(trackingSpy).toHaveBeenCalledTimes(1); + }); + }); + describe('with new failed result', () => { beforeEach(() => { setReports(newFailedTestReports); @@ -203,6 +240,77 @@ describe('Grouped test reports app', () => { }); }); + describe('recent failures counts', () => { + describe('with recent failures counts', () => { + beforeEach(() => { + setReports(recentFailuresTestReports); + }); + + describe('with feature flag enabled', () => { + beforeEach(() => { + mountComponent({ testFailureHistory: true }); + }); + + it('renders the recently failed tests summary', () => { + expect(findHeader().text()).toContain( + '2 out of 3 failed tests have failed more than once in the last 14 days', + ); + }); + + it('renders the recently failed count on the test suite', () => { + expect(findSummaryDescription().text()).toContain( + '1 out of 2 failed tests has failed more than once in the last 14 days', + ); + }); + + it('renders the recent failures count on the test case', () => { + expect(findIssueDescription().text()).toContain('Failed 8 times in the last 14 days'); + }); + }); + + describe('with feature flag disabled', () => { + beforeEach(() => { + mountComponent({ testFailureHistory: false }); + }); + + it('does not render the recently failed tests summary', () => { + expect(findHeader().text()).not.toContain('failed more than once in the last 14 days'); + }); + + it('does not render the recently failed count on the test suite', () => { + expect(findSummaryDescription().text()).not.toContain( + 'failed more than once in the last 14 days', + ); + }); + + it('renders the recent failures count on the test case', () => { + expect(findIssueDescription().text()).not.toContain('in the last 14 days'); + }); + }); + }); + + describe('without recent failures counts', () => { + beforeEach(() => { + setReports(mixedResultsTestReports); + mountComponent(); + }); + + it('does not render the recently failed tests summary', () => { + expect(findHeader().text()).not.toContain('failed more than once in the last 14 days'); + }); + + it('does not render the recently failed count on the test suite', () => { + expect(findSummaryDescription().text()).not.toContain( + 'failed more than once in the last 14 days', + ); + }); + + it('does not render the recent failures count on the test case', () => { + expect(findIssueDescription().text()).not.toContain('in the last 14 days'); + }); + }); + }); + describe('with a report that failed to load', () => { beforeEach(() => { setReports(failedReport); diff --git a/spec/frontend/reports/components/report_section_spec.js b/spec/frontend/reports/components/report_section_spec.js index a620b5d9afc..2d228313a9b 100644 --- a/spec/frontend/reports/components/report_section_spec.js +++ b/spec/frontend/reports/components/report_section_spec.js @@ -244,7 +244,7 @@ describe('Report section', () => { hasIssues: true, }, slots: { - actionButtons: ['Action!'], + 'action-buttons': ['Action!'], }, }); }); diff --git a/spec/frontend/reports/mock_data/recent_failures_report.json b/spec/frontend/reports/mock_data/recent_failures_report.json new file mode 100644 index 00000000000..a47bc30a8e5 --- /dev/null +++ b/spec/frontend/reports/mock_data/recent_failures_report.json @@ -0,0 +1,46 @@ +{ + "summary": { "total": 11, "resolved": 0, "errored": 0, "failed": 3, "recentlyFailed": 2 }, + "suites": [ + { + "name": "rspec:pg", + "summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 2, "recentlyFailed": 1 }, + "new_failures": [ + { + "result": "failure", + "name": "Test#sum when a is 1 and b is 2 returns summary", + "execution_time": 0.009411, + "system_output": "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in <top (required)>'", + "recent_failures": 8 + }, + { + "result": "failure", + "name": "Test#sum when a is 100 and b is 200 returns summary", + "execution_time": 0.000162, + "system_output": "Failure/Error: is_expected.to eq(300)\n\n expected: 300\n got: -100\n\n (compared using ==)\n./spec/test_spec.rb:21:in `block (4 levels) in <top (required)>'" + } + ], + "resolved_failures": [], + "existing_failures": [], + "new_errors": [], + "resolved_errors": [], + "existing_errors": [] + }, + { + "name": "java ant", + "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 1, "recentlyFailed": 1 }, + "new_failures": [ + { + "result": "failure", + "name": "Test#sum when a is 100 and b is 200 returns summary", + "execution_time": 0.000562, + "recent_failures": 3 + } + ], + "resolved_failures": [], + "existing_failures": [], + "new_errors": [], + "resolved_errors": [], + "existing_errors": [] + } + ] +} diff --git a/spec/frontend/reports/store/mutations_spec.js b/spec/frontend/reports/store/mutations_spec.js index 9446cd454ab..82a399c876d 100644 --- a/spec/frontend/reports/store/mutations_spec.js +++ b/spec/frontend/reports/store/mutations_spec.js @@ -46,6 +46,7 @@ describe('Reports Store Mutations', () => { name: 'StringHelper#concatenate when a is git and b is lab returns summary', execution_time: 0.0092435, system_output: "Failure/Error: is_expected.to eq('gitlab')", + recent_failures: 4, }, ], resolved_failures: [ @@ -82,6 +83,7 @@ describe('Reports Store Mutations', () => { expect(stateCopy.summary.total).toEqual(mockedResponse.summary.total); expect(stateCopy.summary.resolved).toEqual(mockedResponse.summary.resolved); expect(stateCopy.summary.failed).toEqual(mockedResponse.summary.failed); + expect(stateCopy.summary.recentlyFailed).toEqual(1); }); it('should set reports', () => { diff --git a/spec/frontend/reports/store/utils_spec.js b/spec/frontend/reports/store/utils_spec.js index 9ae456658dc..8977268115e 100644 --- a/spec/frontend/reports/store/utils_spec.js +++ b/spec/frontend/reports/store/utils_spec.js @@ -168,6 +168,54 @@ describe('Reports store utils', () => { }); }); + describe('recentFailuresTextBuilder', () => { + it.each` + recentlyFailed | failed | expected + ${0} | ${1} | ${''} + ${1} | ${1} | ${'1 out of 1 failed test has failed more than once in the last 14 days'} + ${1} | ${2} | ${'1 out of 2 failed tests has failed more than once in the last 14 days'} + ${2} | ${3} | ${'2 out of 3 failed tests have failed more than once in the last 14 days'} + `( + 'should render summary for $recentlyFailed out of $failed failures', + ({ recentlyFailed, failed, expected }) => { + const result = utils.recentFailuresTextBuilder({ recentlyFailed, failed }); + + expect(result).toBe(expected); + }, + ); + }); + + describe('countRecentlyFailedTests', () => { + it('counts tests with more than one recent failure in a report', () => { + const report = { + new_failures: [{ recent_failures: 2 }], + existing_failures: [{ recent_failures: 1 }], + resolved_failures: [{ recent_failures: 20 }, { recent_failures: 5 }], + }; + const result = utils.countRecentlyFailedTests(report); + + expect(result).toBe(3); + }); + + it('counts tests with more than one recent failure in an array of reports', () => { + const reports = [ + { + new_failures: [{ recent_failures: 2 }], + existing_failures: [{ recent_failures: 20 }, { recent_failures: 5 }], + resolved_failures: [{ recent_failures: 2 }], + }, + { + new_failures: [{ recent_failures: 8 }, { recent_failures: 14 }], + existing_failures: [{ recent_failures: 1 }], + resolved_failures: [{ recent_failures: 7 }, { recent_failures: 5 }], + }, + ]; + const result = utils.countRecentlyFailedTests(reports); + + expect(result).toBe(8); + }); + }); + describe('statusIcon', () => { describe('with failed status', () => { it('returns ICON_WARNING', () => { |