Commit 9689e8f0 authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch '211839-test-report-suite' into 'master'

Get test suite details on click

See merge request gitlab-org/gitlab!37232
parents 1285e22d 400f918c
......@@ -29,7 +29,7 @@ export default {
},
methods: {
...mapActions([
'fetchFullReport',
'fetchTestSuite',
'fetchSummary',
'setSelectedSuiteIndex',
'removeSelectedSuiteIndex',
......@@ -40,10 +40,8 @@ export default {
summaryTableRowClick(index) {
this.setSelectedSuiteIndex(index);
// Fetch the full report when the user clicks to see more details
if (!this.hasFullReport) {
this.fetchFullReport();
}
// Fetch the test suite when the user clicks to see more details
this.fetchTestSuite(index);
},
beforeEnterTransition() {
document.documentElement.style.overflowX = 'hidden';
......
......@@ -122,11 +122,12 @@ const createTestDetails = () => {
}
const el = document.querySelector('#js-pipeline-tests-detail');
const { fullReportEndpoint, summaryEndpoint, countEndpoint } = el?.dataset || {};
const { fullReportEndpoint, summaryEndpoint, suiteEndpoint, countEndpoint } = el?.dataset || {};
const testReportsStore = createTestReportsStore({
fullReportEndpoint,
summaryEndpoint: summaryEndpoint || countEndpoint,
suiteEndpoint,
useBuildSummaryReport: window.gon?.features?.buildReportSummary,
});
......
......@@ -33,6 +33,31 @@ export const fetchSummary = ({ state, commit, dispatch }) => {
});
};
export const fetchTestSuite = ({ state, commit, dispatch }, index) => {
const { hasFullSuite } = state.testReports?.test_suites?.[index] || {};
// We don't need to fetch the suite if we have the information already
if (state.hasFullReport || hasFullSuite) {
return Promise.resolve();
}
dispatch('toggleLoading');
const { name = '', build_ids = [] } = state.testReports?.test_suites?.[index] || {};
// Replacing `/:suite_name.json` with the name of the suite. Including the extra characters
// to ensure that we replace exactly the template part of the URL string
const endpoint = state.suiteEndpoint?.replace('/:suite_name.json', `/${name}.json`);
return axios
.get(endpoint, { params: { build_ids } })
.then(({ data }) => commit(types.SET_SUITE, { suite: data, index }))
.catch(() => {
createFlash(s__('TestReports|There was an error fetching the test suite.'));
})
.finally(() => {
dispatch('toggleLoading');
});
};
export const fetchFullReport = ({ state, commit, dispatch }) => {
dispatch('toggleLoading');
......
export const SET_REPORTS = 'SET_REPORTS';
export const SET_SELECTED_SUITE_INDEX = 'SET_SELECTED_SUITE_INDEX';
export const SET_SUMMARY = 'SET_SUMMARY';
export const SET_SUITE = 'SET_SUITE';
export const TOGGLE_LOADING = 'TOGGLE_LOADING';
......@@ -5,6 +5,10 @@ export default {
Object.assign(state, { testReports, hasFullReport: true });
},
[types.SET_SUITE](state, { suite = {}, index = null }) {
state.testReports.test_suites[index] = { ...suite, hasFullSuite: true };
},
[types.SET_SELECTED_SUITE_INDEX](state, selectedSuiteIndex) {
Object.assign(state, { selectedSuiteIndex });
},
......
export default ({
fullReportEndpoint = '',
summaryEndpoint = '',
suiteEndpoint = '',
useBuildSummaryReport = false,
}) => ({
summaryEndpoint,
suiteEndpoint,
fullReportEndpoint,
testReports: {},
selectedSuiteIndex: null,
......
......@@ -88,5 +88,6 @@
#js-tab-tests.tab-pane
#js-pipeline-tests-detail{ data: { full_report_endpoint: test_report_project_pipeline_path(@project, @pipeline, format: :json),
summary_endpoint: Feature.enabled?(:build_report_summary, @project) ? summary_project_pipeline_tests_path(@project, @pipeline, format: :json) : '',
suite_endpoint: Feature.enabled?(:build_report_summary, @project) ? project_pipeline_test_path(@project, @pipeline, suite_name: ':suite_name', format: :json) : '',
count_endpoint: test_reports_count_project_pipeline_path(@project, @pipeline, format: :json) } }
= render_if_exists "projects/pipelines/tabs_content", pipeline: @pipeline, project: @project
......@@ -23383,6 +23383,9 @@ msgstr ""
msgid "TestReports|There was an error fetching the test reports."
msgstr ""
msgid "TestReports|There was an error fetching the test suite."
msgstr ""
msgid "Tests"
msgstr ""
......
......@@ -17,9 +17,11 @@ describe('Actions TestReports Store', () => {
const summary = { total_count: 1 };
const fullReportEndpoint = `${TEST_HOST}/test_reports.json`;
const suiteEndpoint = `${TEST_HOST}/tests/:suite_name.json`;
const summaryEndpoint = `${TEST_HOST}/test_reports/summary.json`;
const defaultState = {
fullReportEndpoint,
suiteEndpoint,
summaryEndpoint,
testReports: {},
selectedSuite: null,
......@@ -100,6 +102,65 @@ describe('Actions TestReports Store', () => {
});
});
describe('fetch test suite', () => {
beforeEach(() => {
const buildIds = [1];
testReports.test_suites[0].build_ids = buildIds;
const endpoint = suiteEndpoint.replace(':suite_name', testReports.test_suites[0].name);
mock
.onGet(endpoint, { params: { build_ids: buildIds } })
.replyOnce(200, testReports.test_suites[0], {});
});
it('sets test suite and shows tests', done => {
const suite = testReports.test_suites[0];
const index = 0;
testAction(
actions.fetchTestSuite,
index,
{ ...state, testReports },
[{ type: types.SET_SUITE, payload: { suite, index } }],
[{ type: 'toggleLoading' }, { type: 'toggleLoading' }],
done,
);
});
it('should create flash on API error', done => {
const index = 0;
testAction(
actions.fetchTestSuite,
index,
{ ...state, testReports, suiteEndpoint: null },
[],
[{ type: 'toggleLoading' }, { type: 'toggleLoading' }],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
});
describe('when we already have the suite data', () => {
it('should not fetch suite', done => {
const index = 0;
testReports.test_suites[0].hasFullSuite = true;
testAction(actions.fetchTestSuite, index, { ...state, testReports }, [], [], done);
});
});
describe('when we already have the full report data', () => {
it('should not fetch suite', done => {
const index = 0;
testReports.hasFullReport = true;
testAction(actions.fetchTestSuite, index, { ...state, testReports }, [], [], done);
});
});
});
describe('fetch full report', () => {
beforeEach(() => {
mock.onGet(fullReportEndpoint).replyOnce(200, testReports, {});
......
......@@ -29,6 +29,21 @@ describe('Mutations TestReports Store', () => {
});
});
describe('set suite', () => {
it('should set the suite at the given index', () => {
mockState.testReports = testReports;
const suite = { name: 'test_suite' };
const index = 0;
const expectedState = { ...mockState };
expectedState.testReports.test_suites[index] = { suite, hasFullSuite: true };
mutations[types.SET_SUITE](mockState, { suite, index });
expect(mockState.testReports.test_suites[index]).toEqual(
expectedState.testReports.test_suites[index],
);
});
});
describe('set selected suite index', () => {
it('should set selectedSuiteIndex', () => {
const selectedSuiteIndex = 0;
......
......@@ -22,7 +22,7 @@ describe('Test reports app', () => {
const testSummaryTable = () => wrapper.find(TestSummaryTable);
const actionSpies = {
fetchFullReport: jest.fn(),
fetchTestSuite: jest.fn(),
fetchSummary: jest.fn(),
setSelectedSuiteIndex: jest.fn(),
removeSelectedSuiteIndex: jest.fn(),
......@@ -91,28 +91,14 @@ describe('Test reports app', () => {
});
describe('when a suite is clicked', () => {
describe('when the full test report has already been received', () => {
beforeEach(() => {
createComponent({ hasFullReport: true });
testSummaryTable().vm.$emit('row-click', 0);
});
it('should only call setSelectedSuiteIndex', () => {
expect(actionSpies.setSelectedSuiteIndex).toHaveBeenCalled();
expect(actionSpies.fetchFullReport).not.toHaveBeenCalled();
});
beforeEach(() => {
createComponent({ hasFullReport: true });
testSummaryTable().vm.$emit('row-click', 0);
});
describe('when the full test report has not been received', () => {
beforeEach(() => {
createComponent({ hasFullReport: false });
testSummaryTable().vm.$emit('row-click', 0);
});
it('should call setSelectedSuiteIndex and fetchFullReport', () => {
expect(actionSpies.setSelectedSuiteIndex).toHaveBeenCalled();
expect(actionSpies.fetchFullReport).toHaveBeenCalled();
});
it('should call setSelectedSuiteIndex and fetchTestSuite', () => {
expect(actionSpies.setSelectedSuiteIndex).toHaveBeenCalled();
expect(actionSpies.fetchTestSuite).toHaveBeenCalled();
});
});
......
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