Commit c3af89cc authored by Scott Hampton's avatar Scott Hampton

Get test suite details on click

Instead of fetching the full report when clicking
on a test suite, let's use the new test suite endpoint
to get just the individual suite data.
parent 3625ab69
......@@ -29,7 +29,7 @@ export default {
},
methods: {
...mapActions([
'fetchFullReport',
'fetchTestSuite',
'fetchSummary',
'setSelectedSuiteIndex',
'removeSelectedSuiteIndex',
......@@ -40,9 +40,9 @@ 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
if (!this.hasFullReport && !this.getSelectedSuite(index).hasFullSuite) {
this.fetchTestSuite(index);
}
},
beforeEnterTransition() {
......@@ -71,7 +71,7 @@ export default {
@after-leave="afterLeaveTransition"
>
<div v-if="showSuite" key="detail" class="w-100 position-absolute slide-enter-to-element">
<test-summary :report="getSelectedSuite" show-back @on-back-click="summaryBackClick" />
<test-summary :report="getSelectedSuite()" show-back @on-back-click="summaryBackClick" />
<test-suite-table />
</div>
......
......@@ -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,25 @@ export const fetchSummary = ({ state, commit, dispatch }) => {
});
};
export const fetchTestSuite = ({ state, commit, dispatch }, index) => {
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');
......
......@@ -9,10 +9,13 @@ export const getTestSuites = state => {
}));
};
export const getSelectedSuite = state =>
state.testReports?.test_suites?.[state.selectedSuiteIndex] || {};
// We want to use this to get the selected suite based on state
// but we also want to use this when selecting a state
// so we can make it return a method to call
export const getSelectedSuite = state => (index = state.selectedSuiteIndex) =>
state.testReports?.test_suites?.[index] || {};
export const getSuiteTests = state => {
const { test_cases: testCases = [] } = getSelectedSuite(state);
const { test_cases: testCases = [] } = getSelectedSuite(state)();
return testCases.sort(sortTestCases).map(addIconStatus);
};
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
......@@ -23356,6 +23356,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,47 @@ 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('fetch full report', () => {
beforeEach(() => {
mock.onGet(fullReportEndpoint).replyOnce(200, testReports, {});
......
......@@ -51,11 +51,20 @@ describe('Getters TestReports Store', () => {
it('should return the selected suite', () => {
setupState();
const selectedSuite = getters.getSelectedSuite(state);
const selectedSuite = getters.getSelectedSuite(state)();
const expected = testReports.test_suites[state.selectedSuiteIndex];
expect(selectedSuite).toEqual(expected);
});
it('should return the suite at the given index', () => {
setupState();
const selectedSuite = getters.getSelectedSuite(state)(0);
const expected = testReports.test_suites[0];
expect(selectedSuite).toEqual(expected);
});
});
describe('getSuiteTests', () => {
......
......@@ -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(),
......@@ -99,19 +99,37 @@ describe('Test reports app', () => {
it('should only call setSelectedSuiteIndex', () => {
expect(actionSpies.setSelectedSuiteIndex).toHaveBeenCalled();
expect(actionSpies.fetchFullReport).not.toHaveBeenCalled();
expect(actionSpies.fetchTestSuite).not.toHaveBeenCalled();
});
});
describe('when the full test report has not been received', () => {
beforeEach(() => {
createComponent({ hasFullReport: false });
testSummaryTable().vm.$emit('row-click', 0);
describe('when the full suite has already been received', () => {
beforeEach(() => {
const mockState = { hasFullReport: false, testReports };
mockState.testReports.test_suites[0].hasFullSuite = true;
createComponent(mockState);
testSummaryTable().vm.$emit('row-click', 0);
});
it('should only call setSelectedSuiteIndex', () => {
expect(actionSpies.setSelectedSuiteIndex).toHaveBeenCalled();
expect(actionSpies.fetchTestSuite).not.toHaveBeenCalled();
});
});
it('should call setSelectedSuiteIndex and fetchFullReport', () => {
expect(actionSpies.setSelectedSuiteIndex).toHaveBeenCalled();
expect(actionSpies.fetchFullReport).toHaveBeenCalled();
describe('when the full suite has not been received', () => {
beforeEach(() => {
const mockState = { hasFullReport: false, testReports };
mockState.testReports.test_suites[0].hasFullSuite = false;
createComponent(mockState);
testSummaryTable().vm.$emit('row-click', 0);
});
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