Commit bcf59315 authored by Miguel Rincon's avatar Miguel Rincon

Merge branch 'fc-lint-querry' into 'master'

Add graphql query for linting in editor app

See merge request gitlab-org/gitlab!49379
parents 10b803c2 4e40a01b
#import "~/pipelines/graphql/queries/pipeline_stages.fragment.graphql"
query getCiConfigData($content: String!) {
ciConfig(content: $content) {
errors
status
stages {
...PipelineStagesData
}
}
}
<script> <script>
import { GlAlert, GlLoadingIcon, GlTab, GlTabs } from '@gitlab/ui'; import { GlAlert, GlLoadingIcon, GlTab, GlTabs } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
import { redirectTo, mergeUrlParams, refreshCurrentPage } from '~/lib/utils/url_utility'; import { mergeUrlParams, redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility';
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue'; import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import CommitForm from './components/commit/commit_form.vue'; import CommitForm from './components/commit/commit_form.vue';
...@@ -9,24 +9,25 @@ import TextEditor from './components/text_editor.vue'; ...@@ -9,24 +9,25 @@ import TextEditor from './components/text_editor.vue';
import commitCiFileMutation from './graphql/mutations/commit_ci_file.mutation.graphql'; import commitCiFileMutation from './graphql/mutations/commit_ci_file.mutation.graphql';
import getBlobContent from './graphql/queries/blob_content.graphql'; import getBlobContent from './graphql/queries/blob_content.graphql';
import getCiConfigData from './graphql/queries/ci_config.graphql';
const MR_SOURCE_BRANCH = 'merge_request[source_branch]'; const MR_SOURCE_BRANCH = 'merge_request[source_branch]';
const MR_TARGET_BRANCH = 'merge_request[target_branch]'; const MR_TARGET_BRANCH = 'merge_request[target_branch]';
const LOAD_FAILURE_NO_REF = 'LOAD_FAILURE_NO_REF';
const LOAD_FAILURE_NO_FILE = 'LOAD_FAILURE_NO_FILE';
const LOAD_FAILURE_UNKNOWN = 'LOAD_FAILURE_UNKNOWN';
const COMMIT_FAILURE = 'COMMIT_FAILURE'; const COMMIT_FAILURE = 'COMMIT_FAILURE';
const DEFAULT_FAILURE = 'DEFAULT_FAILURE'; const DEFAULT_FAILURE = 'DEFAULT_FAILURE';
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';
export default { export default {
components: { components: {
CommitForm,
GlAlert, GlAlert,
GlLoadingIcon, GlLoadingIcon,
GlTab, GlTab,
GlTabs, GlTabs,
PipelineGraph, PipelineGraph,
CommitForm,
TextEditor, TextEditor,
}, },
props: { props: {
...@@ -55,14 +56,15 @@ export default { ...@@ -55,14 +56,15 @@ export default {
}, },
data() { data() {
return { return {
showFailureAlert: false, ciConfigData: {},
content: '',
contentModel: '',
currentTabIndex: 0,
editorIsReady: false,
failureType: null, failureType: null,
failureReasons: [], failureReasons: [],
isSaving: false, isSaving: false,
editorIsReady: false, showFailureAlert: false,
content: '',
contentModel: '',
}; };
}, },
apollo: { apollo: {
...@@ -85,18 +87,35 @@ export default { ...@@ -85,18 +87,35 @@ export default {
this.handleBlobContentError(error); this.handleBlobContentError(error);
}, },
}, },
ciConfigData: {
query: getCiConfigData,
// If content is not loaded, we can't lint the data
skip: ({ contentModel }) => {
return !contentModel;
},
variables() {
return {
content: this.contentModel,
};
},
update(data) {
return data?.ciConfig ?? {};
},
error() {
this.reportFailure(LOAD_FAILURE_UNKNOWN);
},
},
}, },
computed: { computed: {
isLoading() { isBlobContentLoading() {
return this.$apollo.queries.content.loading; return this.$apollo.queries.content.loading;
}, },
isVisualizeTabActive() {
return this.currentTabIndex === 1;
},
defaultCommitMessage() { defaultCommitMessage() {
return sprintf(this.$options.i18n.defaultCommitMessage, { sourcePath: this.ciConfigPath }); return sprintf(this.$options.i18n.defaultCommitMessage, { sourcePath: this.ciConfigPath });
}, },
pipelineData() {
// Note data will loaded as part of https://gitlab.com/gitlab-org/gitlab/-/issues/263141
return {};
},
failure() { failure() {
switch (this.failureType) { switch (this.failureType) {
case LOAD_FAILURE_NO_REF: case LOAD_FAILURE_NO_REF:
...@@ -233,17 +252,17 @@ export default { ...@@ -233,17 +252,17 @@ export default {
</ul> </ul>
</gl-alert> </gl-alert>
<div class="gl-mt-4"> <div class="gl-mt-4">
<gl-loading-icon v-if="isLoading" 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 class="file-editor gl-mb-3">
<gl-tabs> <gl-tabs v-model="currentTabIndex">
<!-- editor should be mounted when its tab is visible, so the container has a size --> <!-- editor should be mounted when its tab is visible, so the container has a size -->
<gl-tab :title="$options.i18n.tabEdit" :lazy="!editorIsReady"> <gl-tab :title="$options.i18n.tabEdit" :lazy="!editorIsReady">
<!-- editor should be mounted only once, when the tab is displayed --> <!-- editor should be mounted only once, when the tab is displayed -->
<text-editor v-model="contentModel" @editor-ready="editorIsReady = true" /> <text-editor v-model="contentModel" @editor-ready="editorIsReady = true" />
</gl-tab> </gl-tab>
<gl-tab :title="$options.i18n.tabGraph"> <gl-tab :title="$options.i18n.tabGraph" :lazy="!isVisualizeTabActive">
<pipeline-graph :pipeline-data="pipelineData" /> <pipeline-graph :pipeline-data="ciConfigData" />
</gl-tab> </gl-tab>
</gl-tabs> </gl-tabs>
</div> </div>
......
fragment PipelineStagesData on CiConfigStage {
name
groups {
name
jobs {
name
needs {
name
}
}
}
}
...@@ -12,6 +12,16 @@ job1: ...@@ -12,6 +12,16 @@ job1:
- echo 'test' - echo 'test'
`; `;
export const mockCiConfigQueryResponse = {
data: {
ciConfig: {
errors: [],
stages: [],
status: '',
},
},
};
export const mockLintResponse = { export const mockLintResponse = {
valid: true, valid: true,
errors: [], errors: [],
......
...@@ -13,9 +13,10 @@ import waitForPromises from 'helpers/wait_for_promises'; ...@@ -13,9 +13,10 @@ 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 { redirectTo, refreshCurrentPage, objectToQuery } from '~/lib/utils/url_utility'; import { objectToQuery, redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility';
import { import {
mockCiConfigPath, mockCiConfigPath,
mockCiConfigQueryResponse,
mockCiYml, mockCiYml,
mockCommitId, mockCommitId,
mockCommitMessage, mockCommitMessage,
...@@ -24,10 +25,11 @@ import { ...@@ -24,10 +25,11 @@ import {
mockNewMergeRequestPath, mockNewMergeRequestPath,
} from './mock_data'; } from './mock_data';
import TextEditor from '~/pipeline_editor/components/text_editor.vue'; import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue';
import getCiConfig from '~/pipeline_editor/graphql/queries/ci_config.graphql';
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue'; import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue'; import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue';
import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue'; import TextEditor from '~/pipeline_editor/components/text_editor.vue';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(VueApollo); localVue.use(VueApollo);
...@@ -42,9 +44,10 @@ jest.mock('~/lib/utils/url_utility', () => ({ ...@@ -42,9 +44,10 @@ jest.mock('~/lib/utils/url_utility', () => ({
describe('~/pipeline_editor/pipeline_editor_app.vue', () => { describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
let wrapper; let wrapper;
let mockMutate;
let mockApollo; let mockApollo;
let mockBlobContentData; let mockBlobContentData;
let mockCiConfigData;
let mockMutate;
const createComponent = ({ const createComponent = ({
props = {}, props = {},
...@@ -96,7 +99,8 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { ...@@ -96,7 +99,8 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
}; };
const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => { const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
mockApollo = createMockApollo([], { const handlers = [[getCiConfig, mockCiConfigData]];
const resolvers = {
Query: { Query: {
blobContent() { blobContent() {
return { return {
...@@ -105,7 +109,9 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { ...@@ -105,7 +109,9 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
}; };
}, },
}, },
}); };
mockApollo = createMockApollo(handlers, resolvers);
const options = { const options = {
localVue, localVue,
...@@ -125,10 +131,12 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { ...@@ -125,10 +131,12 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
beforeEach(() => { beforeEach(() => {
mockBlobContentData = jest.fn(); mockBlobContentData = jest.fn();
mockCiConfigData = jest.fn().mockResolvedValue(mockCiConfigQueryResponse);
}); });
afterEach(() => { afterEach(() => {
mockBlobContentData.mockReset(); mockBlobContentData.mockReset();
mockCiConfigData.mockReset();
refreshCurrentPage.mockReset(); refreshCurrentPage.mockReset();
redirectTo.mockReset(); redirectTo.mockReset();
mockMutate.mockReset(); mockMutate.mockReset();
...@@ -177,12 +185,10 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { ...@@ -177,12 +185,10 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
beforeEach(async () => { beforeEach(async () => {
createComponent({ mountFn: mount }); createComponent({ mountFn: mount });
wrapper.setData({ await wrapper.setData({
content: mockCiYml, content: mockCiYml,
contentModel: mockCiYml, contentModel: mockCiYml,
}); });
await nextTick();
}); });
it('displays content after the query loads', () => { it('displays content after the query loads', () => {
...@@ -347,7 +353,7 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { ...@@ -347,7 +353,7 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
}); });
describe('displays fetch content errors', () => { describe('displays fetch content errors', () => {
it('no error is show when data is set', async () => { it('no error is shown when data is set', async () => {
mockBlobContentData.mockResolvedValue(mockCiYml); mockBlobContentData.mockResolvedValue(mockCiYml);
createComponentWithApollo(); createComponentWithApollo();
......
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