Commit d4d4c9cc authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch 'experiment/pipeline-editor-walkthrough' into 'master'

Experiment: Pipeline Editor Walkthrough

See merge request gitlab-org/gitlab!73050
parents e147d7c8 1ec67ecd
...@@ -36,6 +36,11 @@ export default { ...@@ -36,6 +36,11 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
scrollToCommitForm: {
type: Boolean,
required: false,
default: false,
},
}, },
data() { data() {
return { return {
...@@ -52,6 +57,13 @@ export default { ...@@ -52,6 +57,13 @@ export default {
return !(this.message && this.targetBranch); return !(this.message && this.targetBranch);
}, },
}, },
watch: {
scrollToCommitForm(flag) {
if (flag) {
this.scrollIntoView();
}
},
},
methods: { methods: {
onSubmit() { onSubmit() {
this.$emit('submit', { this.$emit('submit', {
...@@ -63,6 +75,10 @@ export default { ...@@ -63,6 +75,10 @@ export default {
onReset() { onReset() {
this.$emit('cancel'); this.$emit('cancel');
}, },
scrollIntoView() {
this.$el.scrollIntoView({ behavior: 'smooth' });
this.$emit('scrolled-to-commit-form');
},
}, },
i18n: { i18n: {
commitMessage: __('Commit message'), commitMessage: __('Commit message'),
......
...@@ -45,6 +45,11 @@ export default { ...@@ -45,6 +45,11 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
scrollToCommitForm: {
type: Boolean,
required: false,
default: false,
},
}, },
data() { data() {
return { return {
...@@ -146,6 +151,8 @@ export default { ...@@ -146,6 +151,8 @@ export default {
:current-branch="currentBranch" :current-branch="currentBranch"
:default-message="defaultCommitMessage" :default-message="defaultCommitMessage"
:is-saving="isSaving" :is-saving="isSaving"
:scroll-to-commit-form="scrollToCommitForm"
v-on="$listeners"
@cancel="onCommitCancel" @cancel="onCommitCancel"
@submit="onCommitSubmit" @submit="onCommitSubmit"
/> />
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { GlButton, GlIcon } from '@gitlab/ui'; import { GlButton, GlIcon } from '@gitlab/ui';
import { __ } from '~/locale'; import { __ } from '~/locale';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { experiment } from '~/experimentation/utils';
import { DRAWER_EXPANDED_KEY } from '../../constants'; import { DRAWER_EXPANDED_KEY } from '../../constants';
import FirstPipelineCard from './cards/first_pipeline_card.vue'; import FirstPipelineCard from './cards/first_pipeline_card.vue';
import GettingStartedCard from './cards/getting_started_card.vue'; import GettingStartedCard from './cards/getting_started_card.vue';
...@@ -53,12 +54,23 @@ export default { ...@@ -53,12 +54,23 @@ export default {
}, },
methods: { methods: {
setInitialExpandState() { setInitialExpandState() {
let isExpanded;
experiment('pipeline_editor_walkthrough', {
control: () => {
isExpanded = true;
},
candidate: () => {
isExpanded = false;
},
});
// We check in the local storage and if no value is defined, we want the default // We check in the local storage and if no value is defined, we want the default
// to be true. We want to explicitly set it to true here so that the drawer // to be true. We want to explicitly set it to true here so that the drawer
// animates to open on load. // animates to open on load.
const localValue = localStorage.getItem(this.$options.localDrawerKey); const localValue = localStorage.getItem(this.$options.localDrawerKey);
if (localValue === null) { if (localValue === null) {
this.isExpanded = true; this.isExpanded = isExpanded;
} }
}, },
setTopPosition() { setTopPosition() {
......
...@@ -112,7 +112,7 @@ export default { ...@@ -112,7 +112,7 @@ export default {
isBranchesLoading() { isBranchesLoading() {
return this.$apollo.queries.availableBranches.loading || this.isSearchingBranches; return this.$apollo.queries.availableBranches.loading || this.isSearchingBranches;
}, },
showBranchSwitcher() { enableBranchSwitcher() {
return this.branches.length > 0 || this.searchTerm.length > 0; return this.branches.length > 0 || this.searchTerm.length > 0;
}, },
}, },
...@@ -230,11 +230,11 @@ export default { ...@@ -230,11 +230,11 @@ export default {
<template> <template>
<gl-dropdown <gl-dropdown
v-if="showBranchSwitcher"
v-gl-tooltip.hover v-gl-tooltip.hover
:title="$options.i18n.dropdownHeader" :title="$options.i18n.dropdownHeader"
:header-text="$options.i18n.dropdownHeader" :header-text="$options.i18n.dropdownHeader"
:text="currentBranch" :text="currentBranch"
:disabled="!enableBranchSwitcher"
icon="branch" icon="branch"
data-qa-selector="branch_selector_button" data-qa-selector="branch_selector_button"
data-testid="branch-selector" data-testid="branch-selector"
......
...@@ -4,6 +4,7 @@ import { s__ } from '~/locale'; ...@@ -4,6 +4,7 @@ import { s__ } from '~/locale';
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue'; import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { getParameterValues, setUrlParams, updateHistory } from '~/lib/utils/url_utility'; import { getParameterValues, setUrlParams, updateHistory } from '~/lib/utils/url_utility';
import GitlabExperiment from '~/experimentation/components/gitlab_experiment.vue';
import { import {
CREATE_TAB, CREATE_TAB,
EDITOR_APP_STATUS_EMPTY, EDITOR_APP_STATUS_EMPTY,
...@@ -22,6 +23,7 @@ import CiEditorHeader from './editor/ci_editor_header.vue'; ...@@ -22,6 +23,7 @@ import CiEditorHeader from './editor/ci_editor_header.vue';
import TextEditor from './editor/text_editor.vue'; import TextEditor from './editor/text_editor.vue';
import CiLint from './lint/ci_lint.vue'; import CiLint from './lint/ci_lint.vue';
import EditorTab from './ui/editor_tab.vue'; import EditorTab from './ui/editor_tab.vue';
import WalkthroughPopover from './walkthrough_popover.vue';
export default { export default {
i18n: { i18n: {
...@@ -63,6 +65,8 @@ export default { ...@@ -63,6 +65,8 @@ export default {
GlTabs, GlTabs,
PipelineGraph, PipelineGraph,
TextEditor, TextEditor,
GitlabExperiment,
WalkthroughPopover,
}, },
mixins: [glFeatureFlagsMixin()], mixins: [glFeatureFlagsMixin()],
props: { props: {
...@@ -79,6 +83,10 @@ export default { ...@@ -79,6 +83,10 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
isNewCiConfigFile: {
type: Boolean,
required: true,
},
}, },
apollo: { apollo: {
appStatus: { appStatus: {
...@@ -136,11 +144,17 @@ export default { ...@@ -136,11 +144,17 @@ export default {
> >
<editor-tab <editor-tab
class="gl-mb-3" class="gl-mb-3"
title-link-class="js-walkthrough-popover-target"
:title="$options.i18n.tabEdit" :title="$options.i18n.tabEdit"
lazy lazy
data-testid="editor-tab" data-testid="editor-tab"
@click="setCurrentTab($options.tabConstants.CREATE_TAB)" @click="setCurrentTab($options.tabConstants.CREATE_TAB)"
> >
<gitlab-experiment name="pipeline_editor_walkthrough">
<template #candidate>
<walkthrough-popover v-if="isNewCiConfigFile" v-on="$listeners" />
</template>
</gitlab-experiment>
<ci-editor-header /> <ci-editor-header />
<text-editor :commit-sha="commitSha" :value="ciFileContent" v-on="$listeners" /> <text-editor :commit-sha="commitSha" :value="ciFileContent" v-on="$listeners" />
</editor-tab> </editor-tab>
......
<script>
import { GlButton, GlPopover, GlSprintf, GlOutsideDirective as Outside } from '@gitlab/ui';
import { s__ } from '~/locale';
export default {
directives: { Outside },
i18n: {
title: s__('pipelineEditorWalkthrough|See how GitLab pipelines work'),
description: s__(
'pipelineEditorWalkthrough|This %{codeStart}.gitlab-ci.yml%{codeEnd} file creates a simple test pipeline.',
),
instruction: s__(
'pipelineEditorWalkthrough|Use the %{boldStart}commit changes%{boldEnd} button at the bottom of the page to run the pipeline.',
),
ctaText: s__("pipelineEditorWalkthrough|Let's do this!"),
},
components: {
GlButton,
GlPopover,
GlSprintf,
},
data() {
return {
show: true,
};
},
computed: {
targetElement() {
return document.querySelector('.js-walkthrough-popover-target');
},
},
methods: {
close() {
this.show = false;
},
handleClickCta() {
this.close();
this.$emit('walkthrough-popover-cta-clicked');
},
},
};
</script>
<template>
<gl-popover
:show.sync="show"
:title="$options.i18n.title"
:target="targetElement"
placement="right"
triggers="focus"
>
<div v-outside="close" class="gl-display-flex gl-flex-direction-column">
<p>
<gl-sprintf :message="$options.i18n.description">
<template #code="{ content }">
<code>{{ content }}</code>
</template>
</gl-sprintf>
</p>
<p>
<gl-sprintf :message="$options.i18n.instruction">
<template #bold="{ content }">
<strong>
{{ content }}
</strong>
</template>
</gl-sprintf>
</p>
<gl-button
class="gl-align-self-end"
category="tertiary"
data-testid="ctaBtn"
variant="confirm"
@click="handleClickCta"
>
<gl-emoji data-name="rocket" />
{{ this.$options.i18n.ctaText }}
</gl-button>
</div>
</gl-popover>
</template>
...@@ -58,6 +58,7 @@ export default { ...@@ -58,6 +58,7 @@ export default {
data() { data() {
return { return {
currentTab: CREATE_TAB, currentTab: CREATE_TAB,
scrollToCommitForm: false,
shouldLoadNewBranch: false, shouldLoadNewBranch: false,
showSwitchBranchModal: false, showSwitchBranchModal: false,
}; };
...@@ -81,6 +82,9 @@ export default { ...@@ -81,6 +82,9 @@ export default {
setCurrentTab(tabName) { setCurrentTab(tabName) {
this.currentTab = tabName; this.currentTab = tabName;
}, },
setScrollToCommitForm(newValue = true) {
this.scrollToCommitForm = newValue;
},
}, },
}; };
</script> </script>
...@@ -117,8 +121,10 @@ export default { ...@@ -117,8 +121,10 @@ export default {
:ci-config-data="ciConfigData" :ci-config-data="ciConfigData"
:ci-file-content="ciFileContent" :ci-file-content="ciFileContent"
:commit-sha="commitSha" :commit-sha="commitSha"
:is-new-ci-config-file="isNewCiConfigFile"
v-on="$listeners" v-on="$listeners"
@set-current-tab="setCurrentTab" @set-current-tab="setCurrentTab"
@walkthrough-popover-cta-clicked="setScrollToCommitForm"
/> />
<commit-section <commit-section
v-if="showCommitForm" v-if="showCommitForm"
...@@ -126,6 +132,8 @@ export default { ...@@ -126,6 +132,8 @@ export default {
:ci-file-content="ciFileContent" :ci-file-content="ciFileContent"
:commit-sha="commitSha" :commit-sha="commitSha"
:is-new-ci-config-file="isNewCiConfigFile" :is-new-ci-config-file="isNewCiConfigFile"
:scroll-to-commit-form="scrollToCommitForm"
@scrolled-to-commit-form="setScrollToCommitForm(false)"
v-on="$listeners" v-on="$listeners"
/> />
<pipeline-editor-drawer /> <pipeline-editor-drawer />
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
class Projects::Ci::PipelineEditorController < Projects::ApplicationController class Projects::Ci::PipelineEditorController < Projects::ApplicationController
before_action :check_can_collaborate! before_action :check_can_collaborate!
before_action :setup_walkthrough_experiment, only: :show
before_action do before_action do
push_frontend_feature_flag(:schema_linting, @project, default_enabled: :yaml) push_frontend_feature_flag(:schema_linting, @project, default_enabled: :yaml)
end end
...@@ -16,4 +17,10 @@ class Projects::Ci::PipelineEditorController < Projects::ApplicationController ...@@ -16,4 +17,10 @@ class Projects::Ci::PipelineEditorController < Projects::ApplicationController
def check_can_collaborate! def check_can_collaborate!
render_404 unless can_collaborate_with_project?(@project) render_404 unless can_collaborate_with_project?(@project)
end end
def setup_walkthrough_experiment
experiment(:pipeline_editor_walkthrough, actor: current_user) do |e|
e.candidate {}
end
end
end end
---
name: pipeline_editor_walkthrough
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73050
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345558
milestone: '14.5'
type: experiment
group: group::activation
default_enabled: false
...@@ -41698,6 +41698,18 @@ msgstr "" ...@@ -41698,6 +41698,18 @@ msgstr ""
msgid "pipeline schedules documentation" msgid "pipeline schedules documentation"
msgstr "" msgstr ""
msgid "pipelineEditorWalkthrough|Let's do this!"
msgstr ""
msgid "pipelineEditorWalkthrough|See how GitLab pipelines work"
msgstr ""
msgid "pipelineEditorWalkthrough|This %{codeStart}.gitlab-ci.yml%{codeEnd} file creates a simple test pipeline."
msgstr ""
msgid "pipelineEditorWalkthrough|Use the %{boldStart}commit changes%{boldEnd} button at the bottom of the page to run the pipeline."
msgstr ""
msgid "pod_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character" msgid "pod_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr "" msgstr ""
......
...@@ -6,6 +6,8 @@ RSpec.describe Projects::Ci::PipelineEditorController do ...@@ -6,6 +6,8 @@ RSpec.describe Projects::Ci::PipelineEditorController do
let_it_be(:project) { create(:project, :repository) } let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
subject(:show_request) { get :show, params: { namespace_id: project.namespace, project_id: project } }
before do before do
sign_in(user) sign_in(user)
end end
...@@ -14,8 +16,7 @@ RSpec.describe Projects::Ci::PipelineEditorController do ...@@ -14,8 +16,7 @@ RSpec.describe Projects::Ci::PipelineEditorController do
context 'with enough privileges' do context 'with enough privileges' do
before do before do
project.add_developer(user) project.add_developer(user)
show_request
get :show, params: { namespace_id: project.namespace, project_id: project }
end end
it { expect(response).to have_gitlab_http_status(:ok) } it { expect(response).to have_gitlab_http_status(:ok) }
...@@ -28,13 +29,27 @@ RSpec.describe Projects::Ci::PipelineEditorController do ...@@ -28,13 +29,27 @@ RSpec.describe Projects::Ci::PipelineEditorController do
context 'without enough privileges' do context 'without enough privileges' do
before do before do
project.add_reporter(user) project.add_reporter(user)
show_request
get :show, params: { namespace_id: project.namespace, project_id: project }
end end
it 'responds with 404' do it 'responds with 404' do
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
end end
describe 'pipeline_editor_walkthrough experiment' do
before do
project.add_developer(user)
end
it 'tracks the assignment', :experiment do
expect(experiment(:pipeline_editor_walkthrough))
.to track(:assignment)
.with_context(actor: user)
.on_next_instance
show_request
end
end
end end
end end
...@@ -5,6 +5,9 @@ import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue'; ...@@ -5,6 +5,9 @@ import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue';
import { mockCommitMessage, mockDefaultBranch } from '../../mock_data'; import { mockCommitMessage, mockDefaultBranch } from '../../mock_data';
const scrollIntoViewMock = jest.fn();
HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
describe('Pipeline Editor | Commit Form', () => { describe('Pipeline Editor | Commit Form', () => {
let wrapper; let wrapper;
...@@ -113,4 +116,20 @@ describe('Pipeline Editor | Commit Form', () => { ...@@ -113,4 +116,20 @@ describe('Pipeline Editor | Commit Form', () => {
expect(findSubmitBtn().attributes('disabled')).toBe('disabled'); expect(findSubmitBtn().attributes('disabled')).toBe('disabled');
}); });
}); });
describe('when scrollToCommitForm becomes true', () => {
beforeEach(async () => {
createComponent();
wrapper.setProps({ scrollToCommitForm: true });
await wrapper.vm.$nextTick();
});
it('scrolls into view', () => {
expect(scrollIntoViewMock).toHaveBeenCalledWith({ behavior: 'smooth' });
});
it('emits "scrolled-to-commit-form"', () => {
expect(wrapper.emitted()['scrolled-to-commit-form']).toBeTruthy();
});
});
}); });
...@@ -277,4 +277,16 @@ describe('Pipeline Editor | Commit section', () => { ...@@ -277,4 +277,16 @@ describe('Pipeline Editor | Commit section', () => {
expect(wrapper.emitted('resetContent')).toHaveLength(1); expect(wrapper.emitted('resetContent')).toHaveLength(1);
}); });
}); });
it('sets listeners on commit form', () => {
const handler = jest.fn();
createComponent({ options: { listeners: { event: handler } } });
findCommitForm().vm.$emit('event');
expect(handler).toHaveBeenCalled();
});
it('passes down scroll-to-commit-form prop to commit form', () => {
createComponent({ props: { 'scroll-to-commit-form': true } });
expect(findCommitForm().props('scrollToCommitForm')).toBe(true);
});
}); });
import { GlButton } from '@gitlab/ui'; import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import { stubExperiments } from 'helpers/experimentation_helper';
import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import FirstPipelineCard from '~/pipeline_editor/components/drawer/cards/first_pipeline_card.vue'; import FirstPipelineCard from '~/pipeline_editor/components/drawer/cards/first_pipeline_card.vue';
import GettingStartedCard from '~/pipeline_editor/components/drawer/cards/getting_started_card.vue'; import GettingStartedCard from '~/pipeline_editor/components/drawer/cards/getting_started_card.vue';
...@@ -33,19 +34,41 @@ describe('Pipeline editor drawer', () => { ...@@ -33,19 +34,41 @@ describe('Pipeline editor drawer', () => {
const clickToggleBtn = async () => findToggleBtn().vm.$emit('click'); const clickToggleBtn = async () => findToggleBtn().vm.$emit('click');
const originalObjects = [];
beforeEach(() => {
originalObjects.push(window.gon, window.gl);
stubExperiments({ pipeline_editor_walkthrough: 'control' });
});
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
localStorage.clear(); localStorage.clear();
[window.gon, window.gl] = originalObjects;
}); });
it('it sets the drawer to be opened by default', async () => { describe('default expanded state', () => {
createComponent(); describe('when experiment control', () => {
it('sets the drawer to be opened by default', async () => {
expect(findDrawerContent().exists()).toBe(false); createComponent();
expect(findDrawerContent().exists()).toBe(false);
await nextTick(); await nextTick();
expect(findDrawerContent().exists()).toBe(true);
});
});
expect(findDrawerContent().exists()).toBe(true); describe('when experiment candidate', () => {
beforeEach(() => {
stubExperiments({ pipeline_editor_walkthrough: 'candidate' });
});
it('sets the drawer to be closed by default', async () => {
createComponent();
expect(findDrawerContent().exists()).toBe(false);
await nextTick();
expect(findDrawerContent().exists()).toBe(false);
});
});
}); });
describe('when the drawer is collapsed', () => { describe('when the drawer is collapsed', () => {
......
...@@ -141,8 +141,8 @@ describe('Pipeline editor branch switcher', () => { ...@@ -141,8 +141,8 @@ describe('Pipeline editor branch switcher', () => {
createComponentWithApollo(); createComponentWithApollo();
}); });
it('does not render dropdown', () => { it('disables the dropdown', () => {
expect(findDropdown().exists()).toBe(false); expect(findDropdown().props('disabled')).toBe(true);
}); });
}); });
...@@ -189,7 +189,7 @@ describe('Pipeline editor branch switcher', () => { ...@@ -189,7 +189,7 @@ describe('Pipeline editor branch switcher', () => {
}); });
it('does not render dropdown', () => { it('does not render dropdown', () => {
expect(findDropdown().exists()).toBe(false); expect(findDropdown().props('disabled')).toBe(true);
}); });
it('shows an error message', () => { it('shows an error message', () => {
......
import { GlAlert, GlLoadingIcon, GlTabs } from '@gitlab/ui'; import { GlAlert, GlLoadingIcon, GlTabs } from '@gitlab/ui';
import { shallowMount, mount } from '@vue/test-utils'; import { shallowMount, mount } from '@vue/test-utils';
import { nextTick } from 'vue'; import Vue, { nextTick } from 'vue';
import setWindowLocation from 'helpers/set_window_location_helper'; import setWindowLocation from 'helpers/set_window_location_helper';
import CiConfigMergedPreview from '~/pipeline_editor/components/editor/ci_config_merged_preview.vue'; import CiConfigMergedPreview from '~/pipeline_editor/components/editor/ci_config_merged_preview.vue';
import WalkthroughPopover from '~/pipeline_editor/components/walkthrough_popover.vue';
import CiLint from '~/pipeline_editor/components/lint/ci_lint.vue'; import CiLint from '~/pipeline_editor/components/lint/ci_lint.vue';
import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue'; import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue';
import EditorTab from '~/pipeline_editor/components/ui/editor_tab.vue'; import EditorTab from '~/pipeline_editor/components/ui/editor_tab.vue';
import { stubExperiments } from 'helpers/experimentation_helper';
import { import {
CREATE_TAB, CREATE_TAB,
EDITOR_APP_STATUS_EMPTY, EDITOR_APP_STATUS_EMPTY,
...@@ -19,6 +21,8 @@ import { ...@@ -19,6 +21,8 @@ import {
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue'; import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import { mockLintResponse, mockLintResponseWithoutMerged, mockCiYml } from '../mock_data'; import { mockLintResponse, mockLintResponseWithoutMerged, mockCiYml } from '../mock_data';
Vue.config.ignoredElements = ['gl-emoji'];
describe('Pipeline editor tabs component', () => { describe('Pipeline editor tabs component', () => {
let wrapper; let wrapper;
const MockTextEditor = { const MockTextEditor = {
...@@ -26,6 +30,7 @@ describe('Pipeline editor tabs component', () => { ...@@ -26,6 +30,7 @@ describe('Pipeline editor tabs component', () => {
}; };
const createComponent = ({ const createComponent = ({
listeners = {},
props = {}, props = {},
provide = {}, provide = {},
appStatus = EDITOR_APP_STATUS_VALID, appStatus = EDITOR_APP_STATUS_VALID,
...@@ -35,6 +40,7 @@ describe('Pipeline editor tabs component', () => { ...@@ -35,6 +40,7 @@ describe('Pipeline editor tabs component', () => {
propsData: { propsData: {
ciConfigData: mockLintResponse, ciConfigData: mockLintResponse,
ciFileContent: mockCiYml, ciFileContent: mockCiYml,
isNewCiConfigFile: true,
...props, ...props,
}, },
data() { data() {
...@@ -47,6 +53,7 @@ describe('Pipeline editor tabs component', () => { ...@@ -47,6 +53,7 @@ describe('Pipeline editor tabs component', () => {
TextEditor: MockTextEditor, TextEditor: MockTextEditor,
EditorTab, EditorTab,
}, },
listeners,
}); });
}; };
...@@ -62,6 +69,7 @@ describe('Pipeline editor tabs component', () => { ...@@ -62,6 +69,7 @@ describe('Pipeline editor tabs component', () => {
const findPipelineGraph = () => wrapper.findComponent(PipelineGraph); const findPipelineGraph = () => wrapper.findComponent(PipelineGraph);
const findTextEditor = () => wrapper.findComponent(MockTextEditor); const findTextEditor = () => wrapper.findComponent(MockTextEditor);
const findMergedPreview = () => wrapper.findComponent(CiConfigMergedPreview); const findMergedPreview = () => wrapper.findComponent(CiConfigMergedPreview);
const findWalkthroughPopover = () => wrapper.findComponent(WalkthroughPopover);
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
...@@ -236,4 +244,63 @@ describe('Pipeline editor tabs component', () => { ...@@ -236,4 +244,63 @@ describe('Pipeline editor tabs component', () => {
expect(findGlTabs().props('syncActiveTabWithQueryParams')).toBe(true); expect(findGlTabs().props('syncActiveTabWithQueryParams')).toBe(true);
}); });
}); });
describe('pipeline_editor_walkthrough experiment', () => {
describe('when in control path', () => {
beforeEach(() => {
stubExperiments({ pipeline_editor_walkthrough: 'control' });
});
it('does not show walkthrough popover', async () => {
createComponent({ mountFn: mount });
await nextTick();
expect(findWalkthroughPopover().exists()).toBe(false);
});
});
describe('when in candidate path', () => {
beforeEach(() => {
stubExperiments({ pipeline_editor_walkthrough: 'candidate' });
});
describe('when isNewCiConfigFile prop is true (default)', () => {
beforeEach(async () => {
createComponent({
mountFn: mount,
});
await nextTick();
});
it('shows walkthrough popover', async () => {
expect(findWalkthroughPopover().exists()).toBe(true);
});
});
describe('when isNewCiConfigFile prop is false', () => {
it('does not show walkthrough popover', async () => {
createComponent({ props: { isNewCiConfigFile: false }, mountFn: mount });
await nextTick();
expect(findWalkthroughPopover().exists()).toBe(false);
});
});
});
});
it('sets listeners on walkthrough popover', async () => {
stubExperiments({ pipeline_editor_walkthrough: 'candidate' });
const handler = jest.fn();
createComponent({
mountFn: mount,
listeners: {
event: handler,
},
});
await nextTick();
findWalkthroughPopover().vm.$emit('event');
expect(handler).toHaveBeenCalled();
});
}); });
import { mount, shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import WalkthroughPopover from '~/pipeline_editor/components/walkthrough_popover.vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
Vue.config.ignoredElements = ['gl-emoji'];
describe('WalkthroughPopover component', () => {
let wrapper;
const createComponent = (mountFn = shallowMount) => {
return extendedWrapper(mountFn(WalkthroughPopover));
};
afterEach(() => {
wrapper.destroy();
});
describe('CTA button clicked', () => {
beforeEach(async () => {
wrapper = createComponent(mount);
await wrapper.findByTestId('ctaBtn').trigger('click');
});
it('emits "walkthrough-popover-cta-clicked" event', async () => {
expect(wrapper.emitted()['walkthrough-popover-cta-clicked']).toBeTruthy();
});
});
});
...@@ -152,4 +152,27 @@ describe('Pipeline editor home wrapper', () => { ...@@ -152,4 +152,27 @@ describe('Pipeline editor home wrapper', () => {
expect(findCommitSection().exists()).toBe(true); expect(findCommitSection().exists()).toBe(true);
}); });
}); });
describe('WalkthroughPopover events', () => {
beforeEach(() => {
createComponent();
});
describe('when "walkthrough-popover-cta-clicked" is emitted from pipeline editor tabs', () => {
it('passes down `scrollToCommitForm=true` to commit section', async () => {
expect(findCommitSection().props('scrollToCommitForm')).toBe(false);
await findPipelineEditorTabs().vm.$emit('walkthrough-popover-cta-clicked');
expect(findCommitSection().props('scrollToCommitForm')).toBe(true);
});
});
describe('when "scrolled-to-commit-form" is emitted from commit section', () => {
it('passes down `scrollToCommitForm=false` to commit section', async () => {
await findPipelineEditorTabs().vm.$emit('walkthrough-popover-cta-clicked');
expect(findCommitSection().props('scrollToCommitForm')).toBe(true);
await findCommitSection().vm.$emit('scrolled-to-commit-form');
expect(findCommitSection().props('scrollToCommitForm')).toBe(false);
});
});
});
}); });
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