Commit cfaf663b authored by Scott Hampton's avatar Scott Hampton

Use the test report summary

The test report summary endpoint is behind a
feature flag `build_report_summary`. We can
use this endpoint to load the summary of the
test report first, and then load the rest of the
report when the user clicks on a test suite.

This `test_report_summary` in the pipeline
also allows us to render the total count in the tab
without us having to update the badge in JS.
parent 6c97d4ed
......@@ -122,13 +122,17 @@ const createTestDetails = () => {
}
const el = document.querySelector('#js-pipeline-tests-detail');
const { fullReportEndpoint, countEndpoint } = el?.dataset || {};
const { fullReportEndpoint, summaryEndpoint, countEndpoint } = el?.dataset || {};
const testReportsStore = createTestReportsStore({
fullReportEndpoint,
summaryEndpoint: countEndpoint,
summaryEndpoint: summaryEndpoint || countEndpoint,
useBuildSummaryReport: window.gon?.features?.buildReportSummary,
});
createPipelinesTabs(testReportsStore);
if (!window.gon?.features?.buildReportSummary) {
createPipelinesTabs(testReportsStore);
}
// eslint-disable-next-line no-new
new Vue({
......
......@@ -3,21 +3,35 @@ import * as types from './mutation_types';
import createFlash from '~/flash';
import { s__ } from '~/locale';
export const fetchSummary = ({ state, commit }) => {
export const fetchSummary = ({ state, commit, dispatch }) => {
// If we do this without the build_report_summary feature flag enabled
// it causes a race condition for toggleLoading and ruins the loading
// state in the application
if (state.useBuildSummaryReport) {
dispatch('toggleLoading');
}
return axios
.get(state.summaryEndpoint)
.then(({ data }) => {
commit(types.SET_SUMMARY, data);
// Set the tab counter badge to total_count
// This is temporary until we can server-side render that count number
// (see https://gitlab.com/gitlab-org/gitlab/-/issues/223134)
if (data.total_count !== undefined) {
document.querySelector('.js-test-report-badge-counter').innerHTML = data.total_count;
if (!state.useBuildSummaryReport) {
// Set the tab counter badge to total_count
// This is temporary until we can server-side render that count number
// (see https://gitlab.com/gitlab-org/gitlab/-/issues/223134)
if (data.total_count !== undefined) {
document.querySelector('.js-test-report-badge-counter').innerHTML = data.total_count;
}
}
})
.catch(() => {
createFlash(s__('TestReports|There was an error fetching the summary.'));
})
.finally(() => {
if (state.useBuildSummaryReport) {
dispatch('toggleLoading');
}
});
};
......@@ -35,8 +49,15 @@ export const fetchFullReport = ({ state, commit, dispatch }) => {
});
};
export const setSelectedSuiteIndex = ({ commit }, data) =>
export const setSelectedSuiteIndex = ({ state, commit, dispatch }, data) => {
commit(types.SET_SELECTED_SUITE_INDEX, data);
// Fetch the full report when the user clicks to see more details
if (!state.hasFullReport) {
dispatch('fetchFullReport');
}
};
export const removeSelectedSuiteIndex = ({ commit }) =>
commit(types.SET_SELECTED_SUITE_INDEX, null);
export const toggleLoading = ({ commit }) => commit(types.TOGGLE_LOADING);
......
......@@ -2,7 +2,7 @@ import * as types from './mutation_types';
export default {
[types.SET_REPORTS](state, testReports) {
Object.assign(state, { testReports });
Object.assign(state, { testReports, hasFullReport: true });
},
[types.SET_SELECTED_SUITE_INDEX](state, selectedSuiteIndex) {
......@@ -10,7 +10,7 @@ export default {
},
[types.SET_SUMMARY](state, summary) {
Object.assign(state, { summary });
Object.assign(state, { testReports: { ...state.testReports, ...summary } });
},
[types.TOGGLE_LOADING](state) {
......
export default ({ fullReportEndpoint = '', summaryEndpoint = '' }) => ({
export default ({
fullReportEndpoint = '',
summaryEndpoint = '',
useBuildSummaryReport = false,
}) => ({
summaryEndpoint,
fullReportEndpoint,
testReports: {},
selectedSuiteIndex: null,
summary: {},
hasFullReport: false,
isLoading: false,
useBuildSummaryReport,
});
......@@ -24,7 +24,7 @@
%li.js-tests-tab-link
= link_to test_report_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-tests', action: 'test_report', toggle: 'tab' }, class: 'test-tab' do
= s_('TestReports|Tests')
%span.badge.badge-pill.js-test-report-badge-counter
%span.badge.badge-pill.js-test-report-badge-counter= Feature.enabled?(:build_report_summary, @project) ? @pipeline.test_report_summary.total_count : ''
= render_if_exists "projects/pipelines/tabs_holder", pipeline: @pipeline, project: @project
.tab-content
......@@ -86,5 +86,7 @@
#js-pipeline-dag-vue{ data: { pipeline_data_path: dag_project_pipeline_path(@project, @pipeline), empty_svg_path: image_path('illustrations/empty-state/empty-dag-md.svg'), dag_doc_path: help_page_path('ci/yaml/README.md', anchor: 'needs')} }
#js-tab-tests.tab-pane
#js-pipeline-tests-detail{ data: { full_report_endpoint: test_report_project_pipeline_path(@project, @pipeline, format: :json), count_endpoint: test_reports_count_project_pipeline_path(@project, @pipeline, format: :json) } }
#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_tests_path(@project, @pipeline, 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
......@@ -23,12 +23,12 @@ describe('Actions TestReports Store', () => {
summaryEndpoint,
testReports: {},
selectedSuite: null,
summary: {},
useBuildSummaryReport: false,
};
beforeEach(() => {
mock = new MockAdapter(axios);
state = defaultState;
state = { ...defaultState };
});
afterEach(() => {
......@@ -40,31 +40,63 @@ describe('Actions TestReports Store', () => {
mock.onGet(summaryEndpoint).replyOnce(200, summary, {});
});
it('sets testReports and shows tests', done => {
testAction(
actions.fetchSummary,
null,
state,
[{ type: types.SET_SUMMARY, payload: summary }],
[],
done,
);
describe('when useBuildSummaryReport in state is true', () => {
it('sets testReports and shows tests', done => {
testAction(
actions.fetchSummary,
null,
{ ...state, useBuildSummaryReport: true },
[{ type: types.SET_SUMMARY, payload: summary }],
[{ type: 'toggleLoading' }, { type: 'toggleLoading' }],
done,
);
});
it('should create flash on API error', done => {
testAction(
actions.fetchSummary,
null,
{
summaryEndpoint: null,
useBuildSummaryReport: true,
},
[],
[{ type: 'toggleLoading' }, { type: 'toggleLoading' }],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
});
});
it('should create flash on API error', done => {
testAction(
actions.fetchSummary,
null,
{
summaryEndpoint: null,
},
[],
[],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
describe('when useBuildSummaryReport in state is false', () => {
it('sets testReports and shows tests', done => {
testAction(
actions.fetchSummary,
null,
state,
[{ type: types.SET_SUMMARY, payload: summary }],
[],
done,
);
});
it('should create flash on API error', done => {
testAction(
actions.fetchSummary,
null,
{
summaryEndpoint: null,
},
[],
[],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
});
});
});
......@@ -104,15 +136,30 @@ describe('Actions TestReports Store', () => {
describe('set selected suite index', () => {
const selectedSuiteIndex = 0;
it('sets selectedSuiteIndex', done => {
testAction(
actions.setSelectedSuiteIndex,
selectedSuiteIndex,
state,
[{ type: types.SET_SELECTED_SUITE_INDEX, payload: selectedSuiteIndex }],
[],
done,
);
describe('when state does not have full report', () => {
it('sets selectedSuiteIndex', done => {
testAction(
actions.setSelectedSuiteIndex,
selectedSuiteIndex,
state,
[{ type: types.SET_SELECTED_SUITE_INDEX, payload: selectedSuiteIndex }],
[{ type: 'fetchFullReport' }],
done,
);
});
});
describe('when state has full report', () => {
it('sets selectedSuiteIndex', done => {
testAction(
actions.setSelectedSuiteIndex,
selectedSuiteIndex,
{ ...state, hasFullReport: true },
[{ type: types.SET_SELECTED_SUITE_INDEX, payload: selectedSuiteIndex }],
[],
done,
);
});
});
});
......
......@@ -12,10 +12,11 @@ describe('Mutations TestReports Store', () => {
testReports: {},
selectedSuite: null,
isLoading: false,
hasFullReport: false,
};
beforeEach(() => {
mockState = defaultState;
mockState = { ...defaultState };
});
describe('set reports', () => {
......@@ -24,6 +25,7 @@ describe('Mutations TestReports Store', () => {
mutations[types.SET_REPORTS](mockState, testReports);
expect(mockState.testReports).toEqual(expectedState.testReports);
expect(mockState.hasFullReport).toBe(true);
});
});
......@@ -41,7 +43,7 @@ describe('Mutations TestReports Store', () => {
const summary = { total_count: 1 };
mutations[types.SET_SUMMARY](mockState, summary);
expect(mockState.summary).toEqual(summary);
expect(mockState.testReports).toEqual(summary);
});
});
......
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