Commit 803166d3 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch 'mrincon-improve-error-messages-missing-file' into 'master'

Improve error messages when CI blob is not loaded

See merge request gitlab-org/gitlab!51584
parents 20156a13 6f7c8654
...@@ -3,6 +3,7 @@ import { GlAlert, GlLoadingIcon, GlTabs, GlTab } from '@gitlab/ui'; ...@@ -3,6 +3,7 @@ import { GlAlert, GlLoadingIcon, GlTabs, GlTab } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
import { mergeUrlParams, redirectTo } from '~/lib/utils/url_utility'; import { mergeUrlParams, redirectTo } from '~/lib/utils/url_utility';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import httpStatusCodes from '~/lib/utils/http_status';
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue'; import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import CiLint from './components/lint/ci_lint.vue'; import CiLint from './components/lint/ci_lint.vue';
...@@ -23,7 +24,6 @@ const COMMIT_FAILURE = 'COMMIT_FAILURE'; ...@@ -23,7 +24,6 @@ const COMMIT_FAILURE = 'COMMIT_FAILURE';
const COMMIT_SUCCESS = 'COMMIT_SUCCESS'; const COMMIT_SUCCESS = 'COMMIT_SUCCESS';
const DEFAULT_FAILURE = 'DEFAULT_FAILURE'; const DEFAULT_FAILURE = 'DEFAULT_FAILURE';
const LOAD_FAILURE_NO_FILE = 'LOAD_FAILURE_NO_FILE'; const LOAD_FAILURE_NO_FILE = 'LOAD_FAILURE_NO_FILE';
const LOAD_FAILURE_NO_REF = 'LOAD_FAILURE_NO_REF';
const LOAD_FAILURE_UNKNOWN = 'LOAD_FAILURE_UNKNOWN'; const LOAD_FAILURE_UNKNOWN = 'LOAD_FAILURE_UNKNOWN';
export default { export default {
...@@ -125,6 +125,9 @@ export default { ...@@ -125,6 +125,9 @@ export default {
isBlobContentLoading() { isBlobContentLoading() {
return this.$apollo.queries.content.loading; return this.$apollo.queries.content.loading;
}, },
isBlobContentError() {
return this.failureType === LOAD_FAILURE_NO_FILE || this.failureType === LOAD_FAILURE_UNKNOWN;
},
isCiConfigDataLoading() { isCiConfigDataLoading() {
return this.$apollo.queries.ciConfigData.loading; return this.$apollo.queries.ciConfigData.loading;
}, },
...@@ -144,14 +147,11 @@ export default { ...@@ -144,14 +147,11 @@ export default {
}, },
failure() { failure() {
switch (this.failureType) { switch (this.failureType) {
case LOAD_FAILURE_NO_REF:
return {
text: this.$options.alertTexts[LOAD_FAILURE_NO_REF],
variant: 'danger',
};
case LOAD_FAILURE_NO_FILE: case LOAD_FAILURE_NO_FILE:
return { return {
text: this.$options.alertTexts[LOAD_FAILURE_NO_FILE], text: sprintf(this.$options.alertTexts[LOAD_FAILURE_NO_FILE], {
filePath: this.ciConfigPath,
}),
variant: 'danger', variant: 'danger',
}; };
case LOAD_FAILURE_UNKNOWN: case LOAD_FAILURE_UNKNOWN:
...@@ -182,9 +182,8 @@ export default { ...@@ -182,9 +182,8 @@ export default {
[COMMIT_FAILURE]: s__('Pipelines|The GitLab CI configuration could not be updated.'), [COMMIT_FAILURE]: s__('Pipelines|The GitLab CI configuration could not be updated.'),
[COMMIT_SUCCESS]: __('Your changes have been successfully committed.'), [COMMIT_SUCCESS]: __('Your changes have been successfully committed.'),
[DEFAULT_FAILURE]: __('Something went wrong on our end.'), [DEFAULT_FAILURE]: __('Something went wrong on our end.'),
[LOAD_FAILURE_NO_FILE]: s__('Pipelines|No CI file found in this repository, please add one.'), [LOAD_FAILURE_NO_FILE]: s__(
[LOAD_FAILURE_NO_REF]: s__( 'Pipelines|There is no %{filePath} file in this repository, please add one and visit the Pipeline Editor again.',
'Pipelines|Repository does not have a default branch, please set one.',
), ),
[LOAD_FAILURE_UNKNOWN]: s__('Pipelines|The CI configuration was not loaded, please try again.'), [LOAD_FAILURE_UNKNOWN]: s__('Pipelines|The CI configuration was not loaded, please try again.'),
}, },
...@@ -193,12 +192,13 @@ export default { ...@@ -193,12 +192,13 @@ export default {
const { networkError } = error; const { networkError } = error;
const { response } = networkError; const { response } = networkError;
if (response?.status === 404) {
// 404 for missing CI file // 404 for missing CI file
// 400 for blank projects with no repository
if (
response?.status === httpStatusCodes.NOT_FOUND ||
response?.status === httpStatusCodes.BAD_REQUEST
) {
this.reportFailure(LOAD_FAILURE_NO_FILE); this.reportFailure(LOAD_FAILURE_NO_FILE);
} else if (response?.status === 400) {
// 400 for a missing ref when no default branch is set
this.reportFailure(LOAD_FAILURE_NO_REF);
} else { } else {
this.reportFailure(LOAD_FAILURE_UNKNOWN); this.reportFailure(LOAD_FAILURE_UNKNOWN);
} }
...@@ -299,9 +299,9 @@ export default { ...@@ -299,9 +299,9 @@ export default {
<li v-for="reason in failureReasons" :key="reason">{{ reason }}</li> <li v-for="reason in failureReasons" :key="reason">{{ reason }}</li>
</ul> </ul>
</gl-alert> </gl-alert>
<div class="gl-mt-4">
<gl-loading-icon v-if="isBlobContentLoading" size="lg" class="gl-m-3" /> <gl-loading-icon v-if="isBlobContentLoading" size="lg" class="gl-m-3" />
<div v-else class="file-editor gl-mb-3"> <div v-else-if="!isBlobContentError" class="gl-mt-4">
<div class="file-editor gl-mb-3">
<div class="info-well gl-display-none gl-display-sm-block"> <div class="info-well gl-display-none gl-display-sm-block">
<validation-segment <validation-segment
class="well-segment" class="well-segment"
......
...@@ -20790,9 +20790,6 @@ msgstr "" ...@@ -20790,9 +20790,6 @@ msgstr ""
msgid "Pipelines|More Information" msgid "Pipelines|More Information"
msgstr "" msgstr ""
msgid "Pipelines|No CI file found in this repository, please add one."
msgstr ""
msgid "Pipelines|No triggers have been created yet. Add one using the form above." msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr "" msgstr ""
...@@ -20805,9 +20802,6 @@ msgstr "" ...@@ -20805,9 +20802,6 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset." msgid "Pipelines|Project cache successfully reset."
msgstr "" msgstr ""
msgid "Pipelines|Repository does not have a default branch, please set one."
msgstr ""
msgid "Pipelines|Revoke" msgid "Pipelines|Revoke"
msgstr "" msgstr ""
...@@ -20829,6 +20823,9 @@ msgstr "" ...@@ -20829,6 +20823,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines." msgid "Pipelines|There are currently no pipelines."
msgstr "" msgstr ""
msgid "Pipelines|There is no %{filePath} file in this repository, please add one and visit the Pipeline Editor again."
msgstr ""
msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team." msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
msgstr "" msgstr ""
......
...@@ -5,6 +5,7 @@ import waitForPromises from 'helpers/wait_for_promises'; ...@@ -5,6 +5,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createMockApollo from 'jest/helpers/mock_apollo_helper'; import createMockApollo from 'jest/helpers/mock_apollo_helper';
import httpStatusCodes from '~/lib/utils/http_status';
import { objectToQuery, redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility'; import { objectToQuery, redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility';
import { import {
mockCiConfigPath, mockCiConfigPath,
...@@ -414,11 +415,19 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { ...@@ -414,11 +415,19 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
mockCiConfigData.mockResolvedValue(mockCiConfigQueryResponse); mockCiConfigData.mockResolvedValue(mockCiConfigQueryResponse);
}); });
it('no error is shown when data is set', async () => { describe('when file exists', () => {
beforeEach(async () => {
createComponentWithApollo(); createComponentWithApollo();
await waitForPromises(); await waitForPromises();
});
it('shows editor and commit form', () => {
expect(findEditorLite().exists()).toBe(true);
expect(findTextEditor().exists()).toBe(true);
});
it('no error is shown when data is set', async () => {
expect(findAlert().exists()).toBe(false); expect(findAlert().exists()).toBe(false);
expect(findEditorLite().attributes('value')).toBe(mockCiYml); expect(findEditorLite().attributes('value')).toBe(mockCiYml);
}); });
...@@ -433,31 +442,45 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { ...@@ -433,31 +442,45 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
projectPath: mockProjectFullPath, projectPath: mockProjectFullPath,
}); });
}); });
});
describe('when no file exists', () => {
const expectedAlertMsg =
'There is no .gitlab-ci.yml file in this repository, please add one and visit the Pipeline Editor again.';
it('does not show editor or commit form', async () => {
mockBlobContentData.mockRejectedValueOnce(new Error('My error!'));
createComponentWithApollo();
await waitForPromises();
expect(findEditorLite().exists()).toBe(false);
expect(findTextEditor().exists()).toBe(false);
});
it('shows a 404 error message', async () => { it('shows a 404 error message', async () => {
mockBlobContentData.mockRejectedValueOnce({ mockBlobContentData.mockRejectedValueOnce({
response: { response: {
status: 404, status: httpStatusCodes.NOT_FOUND,
}, },
}); });
createComponentWithApollo(); createComponentWithApollo();
await waitForPromises(); await waitForPromises();
expect(findAlert().text()).toBe('No CI file found in this repository, please add one.'); expect(findAlert().text()).toBe(expectedAlertMsg);
}); });
it('shows a 400 error message', async () => { it('shows a 400 error message', async () => {
mockBlobContentData.mockRejectedValueOnce({ mockBlobContentData.mockRejectedValueOnce({
response: { response: {
status: 400, status: httpStatusCodes.BAD_REQUEST,
}, },
}); });
createComponentWithApollo(); createComponentWithApollo();
await waitForPromises(); await waitForPromises();
expect(findAlert().text()).toBe('Repository does not have a default branch, please set one.'); expect(findAlert().text()).toBe(expectedAlertMsg);
}); });
it('shows a unkown error message', async () => { it('shows a unkown error message', async () => {
...@@ -468,4 +491,5 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { ...@@ -468,4 +491,5 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
expect(findAlert().text()).toBe('The CI configuration was not loaded, please try again.'); expect(findAlert().text()).toBe('The CI configuration was not loaded, please try again.');
}); });
}); });
});
}); });
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