Commit 4e40a01b authored by Frédéric Caplette's avatar Frédéric Caplette Committed by Miguel Rincon

Add graphql query for linting in editor app

In the new PA home, we add the graphQl query
to fetch the linted data and we also update
the test to mock thei new returned value.
parent 296275d8
#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