Commit 7736ce72 authored by Scott Hampton's avatar Scott Hampton

Handle errors when fetching projects

Add error handling to the apollo queries. Add
specs for testing that the error handling works.
parent 331f903b
<script> <script>
import { import {
GlAlert,
GlButton, GlButton,
GlDropdown, GlDropdown,
GlDropdownSectionHeader, GlDropdownSectionHeader,
...@@ -19,6 +20,7 @@ import getGroupProjects from '../graphql/queries/get_group_projects.query.graphq ...@@ -19,6 +20,7 @@ import getGroupProjects from '../graphql/queries/get_group_projects.query.graphq
export default { export default {
name: 'GroupRepositoryAnalytics', name: 'GroupRepositoryAnalytics',
components: { components: {
GlAlert,
GlButton, GlButton,
GlDropdown, GlDropdown,
GlDropdownSectionHeader, GlDropdownSectionHeader,
...@@ -57,13 +59,17 @@ export default { ...@@ -57,13 +59,17 @@ export default {
})); }));
}, },
result({ data }) { result({ data }) {
this.projectsPageInfo = data?.group?.projects?.pageInfo; this.projectsPageInfo = data?.group?.projects?.pageInfo || {};
},
error() {
this.hasError = true;
}, },
}, },
}, },
data() { data() {
return { return {
groupProjects: [], groupProjects: [],
hasError: false,
projectsPageInfo: {}, projectsPageInfo: {},
projectSearchTerm: '', projectSearchTerm: '',
selectAllProjects: true, selectAllProjects: true,
...@@ -135,23 +141,30 @@ export default { ...@@ -135,23 +141,30 @@ export default {
clickDateRange(dateRange) { clickDateRange(dateRange) {
this.selectedDateRange = dateRange; this.selectedDateRange = dateRange;
}, },
dismissError() {
this.hasError = false;
},
loadMoreProjects() { loadMoreProjects() {
this.$apollo.queries.groupProjects.fetchMore({ this.$apollo.queries.groupProjects
variables: { .fetchMore({
groupFullPath: this.groupFullPath, variables: {
after: this.projectsPageInfo.endCursor, groupFullPath: this.groupFullPath,
}, after: this.projectsPageInfo.endCursor,
updateQuery(previousResult, { fetchMoreResult }) { },
const results = produce(fetchMoreResult, draftData => { updateQuery(previousResult, { fetchMoreResult }) {
// eslint-disable-next-line no-param-reassign const results = produce(fetchMoreResult, draftData => {
draftData.group.projects.nodes = [ // eslint-disable-next-line no-param-reassign
...previousResult.group.projects.nodes, draftData.group.projects.nodes = [
...draftData.group.projects.nodes, ...previousResult.group.projects.nodes,
]; ...draftData.group.projects.nodes,
}); ];
return results; });
}, return results;
}); },
})
.catch(() => {
this.hasError = true;
});
}, },
}, },
text: { text: {
...@@ -167,6 +180,7 @@ export default { ...@@ -167,6 +180,7 @@ export default {
projectDropdownHeader: __('Projects'), projectDropdownHeader: __('Projects'),
projectDropdownAllProjects: __('All projects'), projectDropdownAllProjects: __('All projects'),
projectSelectAll: __('Select all'), projectSelectAll: __('Select all'),
queryErrorMessage: s__('RepositoriesAnalytics|There was an error fetching the projects.'),
}, },
dateRangeOptions: [ dateRangeOptions: [
{ value: 7, text: __('Last week') }, { value: 7, text: __('Last week') },
...@@ -194,6 +208,13 @@ export default { ...@@ -194,6 +208,13 @@ export default {
:action-primary="downloadCSVModalButton" :action-primary="downloadCSVModalButton"
:action-cancel="cancelModalButton" :action-cancel="cancelModalButton"
> >
<gl-alert
v-if="hasError"
variant="danger"
data-testid="group-code-coverage-projects-error"
@dismiss="dismissError"
>{{ $options.text.queryErrorMessage }}</gl-alert
>
<div>{{ $options.text.downloadCSVModalDescription }}</div> <div>{{ $options.text.downloadCSVModalDescription }}</div>
<div class="gl-my-4"> <div class="gl-my-4">
<label class="gl-display-block col-form-label-sm col-form-label"> <label class="gl-display-block col-form-label-sm col-form-label">
......
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import { import {
GlAlert,
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
GlIntersectionObserver, GlIntersectionObserver,
...@@ -32,6 +33,7 @@ describe('Group repository analytics app', () => { ...@@ -32,6 +33,7 @@ describe('Group repository analytics app', () => {
.trigger('click'); .trigger('click');
const findIntersectionObserver = () => wrapper.find(GlIntersectionObserver); const findIntersectionObserver = () => wrapper.find(GlIntersectionObserver);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon); const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findAlert = () => wrapper.find(GlAlert);
const injectedProperties = { const injectedProperties = {
groupAnalyticsCoverageReportsPath: '/coverage.csv?ref_path=refs/heads/master', groupAnalyticsCoverageReportsPath: '/coverage.csv?ref_path=refs/heads/master',
...@@ -46,6 +48,7 @@ describe('Group repository analytics app', () => { ...@@ -46,6 +48,7 @@ describe('Group repository analytics app', () => {
return { return {
// Ensure that isSelected is set to false for each project so that every test is reset properly // Ensure that isSelected is set to false for each project so that every test is reset properly
groupProjects: groupProjectsData.map(project => ({ ...project, isSelected: false })), groupProjects: groupProjectsData.map(project => ({ ...project, isSelected: false })),
hasError: false,
projectsPageInfo: { projectsPageInfo: {
hasNextPage: false, hasNextPage: false,
endCursor: null, endCursor: null,
...@@ -88,6 +91,16 @@ describe('Group repository analytics app', () => { ...@@ -88,6 +91,16 @@ describe('Group repository analytics app', () => {
openCodeCoverageModal(); openCodeCoverageModal();
}); });
describe('when there is an error fetching the projects', () => {
beforeEach(() => {
createComponent({ data: { hasError: true } });
});
it('displays an alert for the failed query', () => {
expect(findAlert().exists()).toBe(true);
});
});
describe('when selecting a project', () => { describe('when selecting a project', () => {
// Due to the fake_date helper, we can always expect today's date to be 2020-07-06 // Due to the fake_date helper, we can always expect today's date to be 2020-07-06
// and the default date 30 days ago to be 2020-06-06 // and the default date 30 days ago to be 2020-06-06
...@@ -164,7 +177,7 @@ describe('Group repository analytics app', () => { ...@@ -164,7 +177,7 @@ describe('Group repository analytics app', () => {
beforeEach(() => { beforeEach(() => {
jest jest
.spyOn(wrapper.vm.$apollo.queries.groupProjects, 'fetchMore') .spyOn(wrapper.vm.$apollo.queries.groupProjects, 'fetchMore')
.mockImplementation(jest.fn()); .mockImplementation(jest.fn().mockResolvedValue());
findIntersectionObserver().vm.$emit('appear'); findIntersectionObserver().vm.$emit('appear');
return wrapper.vm.$nextTick(); return wrapper.vm.$nextTick();
...@@ -173,6 +186,21 @@ describe('Group repository analytics app', () => { ...@@ -173,6 +186,21 @@ describe('Group repository analytics app', () => {
it('makes a query to fetch more projects', () => { it('makes a query to fetch more projects', () => {
expect(wrapper.vm.$apollo.queries.groupProjects.fetchMore).toHaveBeenCalledTimes(1); expect(wrapper.vm.$apollo.queries.groupProjects.fetchMore).toHaveBeenCalledTimes(1);
}); });
describe('when the fetchMore query throws an error', () => {
beforeEach(() => {
jest
.spyOn(wrapper.vm.$apollo.queries.groupProjects, 'fetchMore')
.mockImplementation(jest.fn().mockRejectedValue());
findIntersectionObserver().vm.$emit('appear');
return wrapper.vm.$nextTick();
});
it('displays an alert for the failed query', () => {
expect(findAlert().exists()).toBe(true);
});
});
}); });
describe('when a query is loading a new page of projects', () => { describe('when a query is loading a new page of projects', () => {
......
...@@ -8,8 +8,6 @@ msgid "" ...@@ -8,8 +8,6 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: gitlab 1.0.0\n" "Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-09-18 11:00+1200\n"
"PO-Revision-Date: 2020-09-18 11:00+1200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n" "Language: \n"
...@@ -21508,6 +21506,9 @@ msgstr "" ...@@ -21508,6 +21506,9 @@ msgstr ""
msgid "RepositoriesAnalytics|Test Code Coverage" msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr "" msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
msgid "Repository" msgid "Repository"
msgstr "" msgstr ""
......
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