Commit 9a5e0ae6 authored by Scott Hampton's avatar Scott Hampton

Link test report file name to file

The file name in the test report is now a link to
the actual file in the commit.
parent 51cf348f
......@@ -5,6 +5,7 @@ import {
GlTooltipDirective,
GlFriendlyWrap,
GlIcon,
GlLink,
GlButton,
GlPagination,
} from '@gitlab/ui';
......@@ -16,6 +17,7 @@ export default {
components: {
GlIcon,
GlFriendlyWrap,
GlLink,
GlButton,
GlPagination,
TestCaseDetails,
......@@ -97,11 +99,9 @@ export default {
<div class="table-section section-10 section-wrap">
<div role="rowheader" class="table-mobile-header">{{ __('Filename') }}</div>
<div class="table-mobile-content gl-md-pr-2 gl-overflow-wrap-break">
<gl-friendly-wrap
v-if="testCase.file"
:symbols="$options.wrapSymbols"
:text="testCase.file"
/>
<gl-link v-if="testCase.file" :href="testCase.filePath" target="_blank">
<gl-friendly-wrap :symbols="$options.wrapSymbols" :text="testCase.file" />
</gl-link>
<gl-button
v-if="testCase.file"
v-gl-tooltip
......
......@@ -58,8 +58,9 @@ const createLegacyPipelinesDetailApp = (mediator) => {
const createTestDetails = () => {
const el = document.querySelector(SELECTORS.PIPELINE_TESTS);
const { summaryEndpoint, suiteEndpoint } = el?.dataset || {};
const { blobPath, summaryEndpoint, suiteEndpoint } = el?.dataset || {};
const testReportsStore = createTestReportsStore({
blobPath,
summaryEndpoint,
suiteEndpoint,
});
......
......@@ -17,7 +17,19 @@ export const getSuiteTests = (state) => {
const { page, perPage } = state.pageInfo;
const start = (page - 1) * perPage;
return testCases.map(addIconStatus).slice(start, start + perPage);
return testCases
.map((testCase) => ({
...testCase,
/**
* filePath is the file string appended onto the blob path.
* We need to make sure the file string doesn't start with `./` when appending.
* Even though we could leave the `/` at the beginning, we can't guarantee that the
* file string will have `/` at the beginning so we should just remove it and add it manually
*/
filePath: testCase.file ? `${state.blobPath}/${testCase.file.replace(/^\.?\//, '')}` : null,
}))
.map(addIconStatus)
.slice(start, start + perPage);
};
export const getSuiteTestCount = (state) => getSelectedSuite(state)?.test_cases?.length || 0;
export default ({ summaryEndpoint = '', suiteEndpoint = '' }) => ({
export default ({ blobPath = '', summaryEndpoint = '', suiteEndpoint = '' }) => ({
blobPath,
summaryEndpoint,
suiteEndpoint,
testReports: {},
......
......@@ -82,5 +82,6 @@
#js-tab-tests.tab-pane
#js-pipeline-tests-detail{ data: { summary_endpoint: summary_project_pipeline_tests_path(@project, @pipeline, format: :json),
suite_endpoint: project_pipeline_test_path(@project, @pipeline, suite_name: 'suite', format: :json) } }
suite_endpoint: project_pipeline_test_path(@project, @pipeline, suite_name: 'suite', format: :json),
blob_path: project_blob_path(@project, @pipeline.commit) } }
= render_if_exists "projects/pipelines/tabs_content", pipeline: @pipeline, project: @project
---
title: Add link to test case file in pipeline test report
merge_request:
author:
type: added
......@@ -8,6 +8,7 @@ describe('Getters TestReports Store', () => {
const testReports = getJSONFixture('pipelines/test_report.json');
const defaultState = {
blobPath: '/test/blob/path',
testReports,
selectedSuiteIndex: 0,
pageInfo: {
......@@ -17,6 +18,7 @@ describe('Getters TestReports Store', () => {
};
const emptyState = {
blobPath: '',
testReports: {},
selectedSuite: null,
pageInfo: {
......@@ -74,6 +76,7 @@ describe('Getters TestReports Store', () => {
const expected = testReports.test_suites[0].test_cases
.map((x) => ({
...x,
filePath: `${state.blobPath}/${x.file.replace(/^\.?\//, '')}`,
formattedTime: formattedTime(x.execution_time),
icon: iconForTestStatus(x.status),
}))
......
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlButton, GlFriendlyWrap, GlPagination } from '@gitlab/ui';
import { GlButton, GlFriendlyWrap, GlLink, GlPagination } from '@gitlab/ui';
import { getJSONFixture } from 'helpers/fixtures';
import SuiteTable from '~/pipelines/components/test_reports/test_suite_table.vue';
import * as getters from '~/pipelines/stores/test_reports/getters';
......@@ -17,18 +17,22 @@ describe('Test reports suite table', () => {
const {
test_suites: [testSuite],
} = getJSONFixture('pipelines/test_report.json');
console.log(JSON.stringify(testSuite.test_cases));
testSuite.test_cases = [...testSuite.test_cases, ...skippedTestCases];
const testCases = testSuite.test_cases;
const blobPath = '/test/blob/path';
const noCasesMessage = () => wrapper.find('.js-no-test-cases');
const allCaseRows = () => wrapper.findAll('.js-case-row');
const findCaseRowAtIndex = (index) => wrapper.findAll('.js-case-row').at(index);
const findLinkForRow = (row) => row.find(GlLink);
const findIconForRow = (row, status) => row.find(`.ci-status-icon-${status}`);
const createComponent = (suite = testSuite, perPage = 20) => {
store = new Vuex.Store({
state: {
blobPath,
testReports: {
test_suites: [suite],
},
......@@ -82,9 +86,16 @@ describe('Test reports suite table', () => {
it('renders the file name for the test with a copy button', () => {
const { file } = testCases[0];
// remove `./` from the beginning of the file path
const relativeFile = file.replace(/^\.?\//, '');
const filePath = `${blobPath}/${relativeFile}`;
const row = findCaseRowAtIndex(0);
const fileLink = findLinkForRow(row);
const button = row.find(GlButton);
console.log(fileLink.attributes('href'), file);
expect(fileLink.attributes('href')).toBe(filePath);
expect(row.text()).toContain(file);
expect(button.exists()).toBe(true);
expect(button.attributes('data-clipboard-text')).toBe(file);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment