Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 18:44:42 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 18:44:42 +0300
commit4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch)
tree5423a1c7516cffe36384133ade12572cf709398d /spec/frontend/pipeline_editor
parente570267f2f6b326480d284e0164a6464ba4081bc (diff)
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'spec/frontend/pipeline_editor')
-rw-r--r--spec/frontend/pipeline_editor/components/drawer/cards/first_pipeline_card_spec.js47
-rw-r--r--spec/frontend/pipeline_editor/components/drawer/cards/getting_started_card_spec.js26
-rw-r--r--spec/frontend/pipeline_editor/components/drawer/cards/pipeline_config_reference_card_spec.js51
-rw-r--r--spec/frontend/pipeline_editor/components/drawer/cards/visualize_and_lint_card_spec.js26
-rw-r--r--spec/frontend/pipeline_editor/components/drawer/pipeline_editor_drawer_spec.js142
-rw-r--r--spec/frontend/pipeline_editor/components/drawer/ui/demo_job_pill_spec.js27
-rw-r--r--spec/frontend/pipeline_editor/components/drawer/ui/pipeline_visual_reference_spec.js31
-rw-r--r--spec/frontend/pipeline_editor/components/editor/text_editor_spec.js5
-rw-r--r--spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js293
-rw-r--r--spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js20
-rw-r--r--spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js107
-rw-r--r--spec/frontend/pipeline_editor/components/ui/pipeline_editor_empty_state_spec.js19
-rw-r--r--spec/frontend/pipeline_editor/components/ui/pipeline_editor_messages_spec.js137
-rw-r--r--spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap4
-rw-r--r--spec/frontend/pipeline_editor/graphql/resolvers_spec.js18
-rw-r--r--spec/frontend/pipeline_editor/mock_data.js63
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_app_spec.js99
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_home_spec.js26
18 files changed, 926 insertions, 215 deletions
diff --git a/spec/frontend/pipeline_editor/components/drawer/cards/first_pipeline_card_spec.js b/spec/frontend/pipeline_editor/components/drawer/cards/first_pipeline_card_spec.js
new file mode 100644
index 00000000000..8a4f07c4d88
--- /dev/null
+++ b/spec/frontend/pipeline_editor/components/drawer/cards/first_pipeline_card_spec.js
@@ -0,0 +1,47 @@
+import { getByRole } from '@testing-library/dom';
+import { mount } from '@vue/test-utils';
+import FirstPipelineCard from '~/pipeline_editor/components/drawer/cards/first_pipeline_card.vue';
+import PipelineVisualReference from '~/pipeline_editor/components/drawer/ui/pipeline_visual_reference.vue';
+
+describe('First pipeline card', () => {
+ let wrapper;
+
+ const defaultProvide = {
+ ciExamplesHelpPagePath: '/pipelines/examples',
+ runnerHelpPagePath: '/help/runners',
+ };
+
+ const createComponent = () => {
+ wrapper = mount(FirstPipelineCard, {
+ provide: {
+ ...defaultProvide,
+ },
+ });
+ };
+
+ const getLinkByName = (name) => getByRole(wrapper.element, 'link', { name }).href;
+ const findPipelinesLink = () => getLinkByName(/examples and templates/i);
+ const findRunnersLink = () => getLinkByName(/make sure your instance has runners available/i);
+ const findVisualReference = () => wrapper.findComponent(PipelineVisualReference);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the title', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.title);
+ });
+
+ it('renders the content', () => {
+ expect(findVisualReference().exists()).toBe(true);
+ });
+
+ it('renders the links', () => {
+ expect(findRunnersLink()).toContain(defaultProvide.runnerHelpPagePath);
+ expect(findPipelinesLink()).toContain(defaultProvide.ciExamplesHelpPagePath);
+ });
+});
diff --git a/spec/frontend/pipeline_editor/components/drawer/cards/getting_started_card_spec.js b/spec/frontend/pipeline_editor/components/drawer/cards/getting_started_card_spec.js
new file mode 100644
index 00000000000..c592e959068
--- /dev/null
+++ b/spec/frontend/pipeline_editor/components/drawer/cards/getting_started_card_spec.js
@@ -0,0 +1,26 @@
+import { shallowMount } from '@vue/test-utils';
+import GettingStartedCard from '~/pipeline_editor/components/drawer/cards/getting_started_card.vue';
+
+describe('Getting started card', () => {
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = shallowMount(GettingStartedCard);
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the title', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.title);
+ });
+
+ it('renders the content', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.firstParagraph);
+ });
+});
diff --git a/spec/frontend/pipeline_editor/components/drawer/cards/pipeline_config_reference_card_spec.js b/spec/frontend/pipeline_editor/components/drawer/cards/pipeline_config_reference_card_spec.js
new file mode 100644
index 00000000000..3c8821d05a7
--- /dev/null
+++ b/spec/frontend/pipeline_editor/components/drawer/cards/pipeline_config_reference_card_spec.js
@@ -0,0 +1,51 @@
+import { getByRole } from '@testing-library/dom';
+import { mount } from '@vue/test-utils';
+import PipelineConfigReferenceCard from '~/pipeline_editor/components/drawer/cards/pipeline_config_reference_card.vue';
+
+describe('Pipeline config reference card', () => {
+ let wrapper;
+
+ const defaultProvide = {
+ ciExamplesHelpPagePath: 'help/ci/examples/',
+ ciHelpPagePath: 'help/ci/introduction',
+ needsHelpPagePath: 'help/ci/yaml#needs',
+ ymlHelpPagePath: 'help/ci/yaml',
+ };
+
+ const createComponent = () => {
+ wrapper = mount(PipelineConfigReferenceCard, {
+ provide: {
+ ...defaultProvide,
+ },
+ });
+ };
+
+ const getLinkByName = (name) => getByRole(wrapper.element, 'link', { name }).href;
+ const findCiExamplesLink = () => getLinkByName(/CI\/CD examples and templates/i);
+ const findCiIntroLink = () => getLinkByName(/GitLab CI\/CD concepts/i);
+ const findNeedsLink = () => getLinkByName(/Needs keyword/i);
+ const findYmlSyntaxLink = () => getLinkByName(/.gitlab-ci.yml syntax reference/i);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the title', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.title);
+ });
+
+ it('renders the content', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.firstParagraph);
+ });
+
+ it('renders the links', () => {
+ expect(findCiExamplesLink()).toContain(defaultProvide.ciExamplesHelpPagePath);
+ expect(findCiIntroLink()).toContain(defaultProvide.ciHelpPagePath);
+ expect(findNeedsLink()).toContain(defaultProvide.needsHelpPagePath);
+ expect(findYmlSyntaxLink()).toContain(defaultProvide.ymlHelpPagePath);
+ });
+});
diff --git a/spec/frontend/pipeline_editor/components/drawer/cards/visualize_and_lint_card_spec.js b/spec/frontend/pipeline_editor/components/drawer/cards/visualize_and_lint_card_spec.js
new file mode 100644
index 00000000000..bebd2484c1d
--- /dev/null
+++ b/spec/frontend/pipeline_editor/components/drawer/cards/visualize_and_lint_card_spec.js
@@ -0,0 +1,26 @@
+import { shallowMount } from '@vue/test-utils';
+import VisualizeAndLintCard from '~/pipeline_editor/components/drawer/cards/getting_started_card.vue';
+
+describe('Visual and Lint card', () => {
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = shallowMount(VisualizeAndLintCard);
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the title', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.title);
+ });
+
+ it('renders the content', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.firstParagraph);
+ });
+});
diff --git a/spec/frontend/pipeline_editor/components/drawer/pipeline_editor_drawer_spec.js b/spec/frontend/pipeline_editor/components/drawer/pipeline_editor_drawer_spec.js
new file mode 100644
index 00000000000..1b68cd3dc43
--- /dev/null
+++ b/spec/frontend/pipeline_editor/components/drawer/pipeline_editor_drawer_spec.js
@@ -0,0 +1,142 @@
+import { GlButton } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import { useLocalStorageSpy } from 'helpers/local_storage_helper';
+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 PipelineConfigReferenceCard from '~/pipeline_editor/components/drawer/cards/pipeline_config_reference_card.vue';
+import VisualizeAndLintCard from '~/pipeline_editor/components/drawer/cards/visualize_and_lint_card.vue';
+import PipelineEditorDrawer from '~/pipeline_editor/components/drawer/pipeline_editor_drawer.vue';
+import { DRAWER_EXPANDED_KEY } from '~/pipeline_editor/constants';
+import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
+
+describe('Pipeline editor drawer', () => {
+ useLocalStorageSpy();
+
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = shallowMount(PipelineEditorDrawer, {
+ stubs: { LocalStorageSync },
+ });
+ };
+
+ const findFirstPipelineCard = () => wrapper.findComponent(FirstPipelineCard);
+ const findGettingStartedCard = () => wrapper.findComponent(GettingStartedCard);
+ const findPipelineConfigReferenceCard = () => wrapper.findComponent(PipelineConfigReferenceCard);
+ const findToggleBtn = () => wrapper.findComponent(GlButton);
+ const findVisualizeAndLintCard = () => wrapper.findComponent(VisualizeAndLintCard);
+
+ const findArrowIcon = () => wrapper.find('[data-testid="toggle-icon"]');
+ const findCollapseText = () => wrapper.find('[data-testid="collapse-text"]');
+ const findDrawerContent = () => wrapper.find('[data-testid="drawer-content"]');
+
+ const clickToggleBtn = async () => findToggleBtn().vm.$emit('click');
+
+ afterEach(() => {
+ wrapper.destroy();
+ localStorage.clear();
+ });
+
+ it('it sets the drawer to be opened by default', async () => {
+ createComponent();
+
+ expect(findDrawerContent().exists()).toBe(false);
+
+ await nextTick();
+
+ expect(findDrawerContent().exists()).toBe(true);
+ });
+
+ describe('when the drawer is collapsed', () => {
+ beforeEach(async () => {
+ createComponent();
+ await clickToggleBtn();
+ });
+
+ it('shows the left facing arrow icon', () => {
+ expect(findArrowIcon().props('name')).toBe('chevron-double-lg-left');
+ });
+
+ it('does not show the collapse text', () => {
+ expect(findCollapseText().exists()).toBe(false);
+ });
+
+ it('does not show the drawer content', () => {
+ expect(findDrawerContent().exists()).toBe(false);
+ });
+
+ it('can open the drawer by clicking on the toggle button', async () => {
+ expect(findDrawerContent().exists()).toBe(false);
+
+ await clickToggleBtn();
+
+ expect(findDrawerContent().exists()).toBe(true);
+ });
+ });
+
+ describe('when the drawer is expanded', () => {
+ beforeEach(async () => {
+ createComponent();
+ });
+
+ it('shows the right facing arrow icon', () => {
+ expect(findArrowIcon().props('name')).toBe('chevron-double-lg-right');
+ });
+
+ it('shows the collapse text', () => {
+ expect(findCollapseText().exists()).toBe(true);
+ });
+
+ it('shows the drawer content', () => {
+ expect(findDrawerContent().exists()).toBe(true);
+ });
+
+ it('shows all the introduction cards', () => {
+ expect(findFirstPipelineCard().exists()).toBe(true);
+ expect(findGettingStartedCard().exists()).toBe(true);
+ expect(findPipelineConfigReferenceCard().exists()).toBe(true);
+ expect(findVisualizeAndLintCard().exists()).toBe(true);
+ });
+
+ it('can close the drawer by clicking on the toggle button', async () => {
+ expect(findDrawerContent().exists()).toBe(true);
+
+ await clickToggleBtn();
+
+ expect(findDrawerContent().exists()).toBe(false);
+ });
+ });
+
+ describe('local storage', () => {
+ it('saves the drawer expanded value to local storage', async () => {
+ localStorage.setItem(DRAWER_EXPANDED_KEY, 'false');
+
+ createComponent();
+ await clickToggleBtn();
+
+ expect(localStorage.setItem.mock.calls).toEqual([
+ [DRAWER_EXPANDED_KEY, 'false'],
+ [DRAWER_EXPANDED_KEY, 'true'],
+ ]);
+ });
+
+ it('loads the drawer collapsed when local storage is set to `false`, ', async () => {
+ localStorage.setItem(DRAWER_EXPANDED_KEY, false);
+ createComponent();
+
+ await nextTick();
+
+ expect(findDrawerContent().exists()).toBe(false);
+ });
+
+ it('loads the drawer expanded when local storage is set to `true`, ', async () => {
+ localStorage.setItem(DRAWER_EXPANDED_KEY, true);
+ createComponent();
+
+ await nextTick();
+
+ expect(findDrawerContent().exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/pipeline_editor/components/drawer/ui/demo_job_pill_spec.js b/spec/frontend/pipeline_editor/components/drawer/ui/demo_job_pill_spec.js
new file mode 100644
index 00000000000..edd2b45569a
--- /dev/null
+++ b/spec/frontend/pipeline_editor/components/drawer/ui/demo_job_pill_spec.js
@@ -0,0 +1,27 @@
+import { shallowMount } from '@vue/test-utils';
+import DemoJobPill from '~/pipeline_editor/components/drawer/ui/demo_job_pill.vue';
+
+describe('Demo job pill', () => {
+ let wrapper;
+ const jobName = 'my-build-job';
+
+ const createComponent = () => {
+ wrapper = shallowMount(DemoJobPill, {
+ propsData: {
+ jobName,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the jobName', () => {
+ expect(wrapper.text()).toContain(jobName);
+ });
+});
diff --git a/spec/frontend/pipeline_editor/components/drawer/ui/pipeline_visual_reference_spec.js b/spec/frontend/pipeline_editor/components/drawer/ui/pipeline_visual_reference_spec.js
new file mode 100644
index 00000000000..e4834544484
--- /dev/null
+++ b/spec/frontend/pipeline_editor/components/drawer/ui/pipeline_visual_reference_spec.js
@@ -0,0 +1,31 @@
+import { shallowMount } from '@vue/test-utils';
+import DemoJobPill from '~/pipeline_editor/components/drawer/ui/demo_job_pill.vue';
+import PipelineVisualReference from '~/pipeline_editor/components/drawer/ui/pipeline_visual_reference.vue';
+
+describe('Demo job pill', () => {
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = shallowMount(PipelineVisualReference);
+ };
+
+ const findAllDemoJobPills = () => wrapper.findAllComponents(DemoJobPill);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders all stage names', () => {
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.stageNames.build);
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.stageNames.test);
+ expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.stageNames.deploy);
+ });
+
+ it('renders all job pills', () => {
+ expect(findAllDemoJobPills()).toHaveLength(4);
+ });
+});
diff --git a/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js b/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js
index 3bf5a291c69..7a5b01fb04a 100644
--- a/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js
+++ b/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js
@@ -1,6 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import { EDITOR_READY_EVENT } from '~/editor/constants';
+import { EditorLiteExtension } from '~/editor/extensions/editor_lite_extension_base';
import TextEditor from '~/pipeline_editor/components/editor/text_editor.vue';
import {
mockCiConfigPath,
@@ -59,6 +60,10 @@ describe('Pipeline Editor | Text editor component', () => {
const findEditor = () => wrapper.findComponent(MockEditorLite);
+ beforeEach(() => {
+ EditorLiteExtension.deferRerender = jest.fn();
+ });
+
afterEach(() => {
wrapper.destroy();
wrapper = null;
diff --git a/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js b/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js
index fa937100982..d6763a7de41 100644
--- a/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js
+++ b/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js
@@ -1,11 +1,28 @@
-import { GlDropdown, GlDropdownItem, GlIcon } from '@gitlab/ui';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
+import {
+ GlDropdown,
+ GlDropdownItem,
+ GlInfiniteScroll,
+ GlLoadingIcon,
+ GlSearchBoxByType,
+} from '@gitlab/ui';
+import { createLocalVue, mount, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import BranchSwitcher from '~/pipeline_editor/components/file_nav/branch_switcher.vue';
import { DEFAULT_FAILURE } from '~/pipeline_editor/constants';
-import { mockDefaultBranch, mockProjectBranches, mockProjectFullPath } from '../../mock_data';
+import getAvailableBranches from '~/pipeline_editor/graphql/queries/available_branches.graphql';
+import {
+ mockBranchPaginationLimit,
+ mockDefaultBranch,
+ mockEmptySearchBranches,
+ mockProjectBranches,
+ mockProjectFullPath,
+ mockSearchBranches,
+ mockTotalBranches,
+ mockTotalBranchResults,
+ mockTotalSearchResults,
+} from '../../mock_data';
const localVue = createLocalVue();
localVue.use(VueApollo);
@@ -15,30 +32,64 @@ describe('Pipeline editor branch switcher', () => {
let mockApollo;
let mockAvailableBranchQuery;
- const createComponentWithApollo = () => {
- const resolvers = {
- Query: {
- project: mockAvailableBranchQuery,
+ const createComponent = (
+ { isQueryLoading, mountFn, options } = {
+ isQueryLoading: false,
+ mountFn: shallowMount,
+ options: {},
+ },
+ ) => {
+ wrapper = mountFn(BranchSwitcher, {
+ propsData: {
+ paginationLimit: mockBranchPaginationLimit,
},
- };
-
- mockApollo = createMockApollo([], resolvers);
- wrapper = shallowMount(BranchSwitcher, {
- localVue,
- apolloProvider: mockApollo,
provide: {
projectFullPath: mockProjectFullPath,
+ totalBranches: mockTotalBranches,
+ },
+ mocks: {
+ $apollo: {
+ queries: {
+ availableBranches: {
+ loading: isQueryLoading,
+ },
+ },
+ },
},
data() {
return {
+ branches: ['main'],
currentBranch: mockDefaultBranch,
};
},
+ ...options,
+ });
+ };
+
+ const createComponentWithApollo = (mountFn = shallowMount) => {
+ const handlers = [[getAvailableBranches, mockAvailableBranchQuery]];
+ mockApollo = createMockApollo(handlers);
+
+ createComponent({
+ mountFn,
+ options: {
+ localVue,
+ apolloProvider: mockApollo,
+ mocks: {},
+ data() {
+ return {
+ currentBranch: mockDefaultBranch,
+ };
+ },
+ },
});
};
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findDropdownItems = () => wrapper.findAll(GlDropdownItem);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
+ const findInfiniteScroll = () => wrapper.findComponent(GlInfiniteScroll);
beforeEach(() => {
mockAvailableBranchQuery = jest.fn();
@@ -48,7 +99,7 @@ describe('Pipeline editor branch switcher', () => {
wrapper.destroy();
});
- describe('while querying', () => {
+ describe('when querying for the first time', () => {
beforeEach(() => {
createComponentWithApollo();
});
@@ -61,41 +112,31 @@ describe('Pipeline editor branch switcher', () => {
describe('after querying', () => {
beforeEach(async () => {
mockAvailableBranchQuery.mockResolvedValue(mockProjectBranches);
- createComponentWithApollo();
+ createComponentWithApollo(mount);
await waitForPromises();
});
- it('query is called with correct variables', async () => {
- expect(mockAvailableBranchQuery).toHaveBeenCalledTimes(1);
- expect(mockAvailableBranchQuery).toHaveBeenCalledWith(
- expect.anything(),
- {
- fullPath: mockProjectFullPath,
- },
- expect.anything(),
- expect.anything(),
- );
+ it('renders search box', () => {
+ expect(findSearchBox().exists()).toBe(true);
});
it('renders list of branches', () => {
expect(findDropdown().exists()).toBe(true);
- expect(findDropdownItems()).toHaveLength(mockProjectBranches.repository.branches.length);
+ expect(findDropdownItems()).toHaveLength(mockTotalBranchResults);
});
- it('renders current branch at the top of the list with a check mark', () => {
- const firstDropdownItem = findDropdownItems().at(0);
- const icon = firstDropdownItem.findComponent(GlIcon);
+ it('renders current branch with a check mark', () => {
+ const defaultBranchInDropdown = findDropdownItems().at(0);
- expect(firstDropdownItem.text()).toBe(mockDefaultBranch);
- expect(icon.exists()).toBe(true);
- expect(icon.props('name')).toBe('check');
+ expect(defaultBranchInDropdown.text()).toBe(mockDefaultBranch);
+ expect(defaultBranchInDropdown.props('isChecked')).toBe(true);
});
it('does not render check mark for other branches', () => {
- const secondDropdownItem = findDropdownItems().at(1);
- const icon = secondDropdownItem.findComponent(GlIcon);
+ const nonDefaultBranch = findDropdownItems().at(1);
- expect(icon.classes()).toContain('gl-visibility-hidden');
+ expect(nonDefaultBranch.text()).not.toBe(mockDefaultBranch);
+ expect(nonDefaultBranch.props('isChecked')).toBe(false);
});
});
@@ -120,4 +161,186 @@ describe('Pipeline editor branch switcher', () => {
]);
});
});
+
+ describe('when switching branches', () => {
+ beforeEach(async () => {
+ jest.spyOn(window.history, 'pushState').mockImplementation(() => {});
+ mockAvailableBranchQuery.mockResolvedValue(mockProjectBranches);
+ createComponentWithApollo(mount);
+ await waitForPromises();
+ });
+
+ it('updates session history when selecting a different branch', async () => {
+ const branch = findDropdownItems().at(1);
+ await branch.vm.$emit('click');
+
+ expect(window.history.pushState).toHaveBeenCalled();
+ expect(window.history.pushState.mock.calls[0][2]).toContain(`?branch_name=${branch.text()}`);
+ });
+
+ it('does not update session history when selecting current branch', async () => {
+ const branch = findDropdownItems().at(0);
+ await branch.vm.$emit('click');
+
+ expect(branch.text()).toBe(mockDefaultBranch);
+ expect(window.history.pushState).not.toHaveBeenCalled();
+ });
+
+ it('emits the refetchContent event when selecting a different branch', async () => {
+ const branch = findDropdownItems().at(1);
+
+ expect(branch.text()).not.toBe(mockDefaultBranch);
+ expect(wrapper.emitted('refetchContent')).toBeUndefined();
+
+ await branch.vm.$emit('click');
+
+ expect(wrapper.emitted('refetchContent')).toBeDefined();
+ expect(wrapper.emitted('refetchContent')).toHaveLength(1);
+ });
+
+ it('does not emit the refetchContent event when selecting the current branch', async () => {
+ const branch = findDropdownItems().at(0);
+
+ expect(branch.text()).toBe(mockDefaultBranch);
+ expect(wrapper.emitted('refetchContent')).toBeUndefined();
+
+ await branch.vm.$emit('click');
+
+ expect(wrapper.emitted('refetchContent')).toBeUndefined();
+ });
+ });
+
+ describe('when searching', () => {
+ beforeEach(async () => {
+ mockAvailableBranchQuery.mockResolvedValue(mockProjectBranches);
+ createComponentWithApollo(mount);
+ await waitForPromises();
+
+ mockAvailableBranchQuery.mockResolvedValue(mockSearchBranches);
+ });
+
+ describe('with a search term', () => {
+ it('calls query with correct variables', async () => {
+ findSearchBox().vm.$emit('input', 'te');
+ await waitForPromises();
+
+ expect(mockAvailableBranchQuery).toHaveBeenCalledWith({
+ limit: mockTotalBranches, // fetch all branches
+ offset: 0,
+ projectFullPath: mockProjectFullPath,
+ searchPattern: '*te*',
+ });
+ });
+
+ it('fetches new list of branches', async () => {
+ expect(findDropdownItems()).toHaveLength(mockTotalBranchResults);
+
+ findSearchBox().vm.$emit('input', 'te');
+ await waitForPromises();
+
+ expect(findDropdownItems()).toHaveLength(mockTotalSearchResults);
+ });
+
+ it('does not hide dropdown when search result is empty', async () => {
+ mockAvailableBranchQuery.mockResolvedValue(mockEmptySearchBranches);
+ findSearchBox().vm.$emit('input', 'aaaaa');
+ await waitForPromises();
+
+ expect(findDropdown().exists()).toBe(true);
+ expect(findDropdownItems()).toHaveLength(0);
+ });
+ });
+
+ describe('without a search term', () => {
+ beforeEach(async () => {
+ findSearchBox().vm.$emit('input', 'te');
+ await waitForPromises();
+
+ mockAvailableBranchQuery.mockResolvedValue(mockProjectBranches);
+ });
+
+ it('calls query with correct variables', async () => {
+ findSearchBox().vm.$emit('input', '');
+ await waitForPromises();
+
+ expect(mockAvailableBranchQuery).toHaveBeenCalledWith({
+ limit: mockBranchPaginationLimit, // only fetch first n branches first
+ offset: 0,
+ projectFullPath: mockProjectFullPath,
+ searchPattern: '*',
+ });
+ });
+
+ it('fetches new list of branches', async () => {
+ expect(findDropdownItems()).toHaveLength(mockTotalSearchResults);
+
+ findSearchBox().vm.$emit('input', '');
+ await waitForPromises();
+
+ expect(findDropdownItems()).toHaveLength(mockTotalBranchResults);
+ });
+ });
+ });
+
+ describe('loading icon', () => {
+ test.each`
+ isQueryLoading | isRendered
+ ${true} | ${true}
+ ${false} | ${false}
+ `('checks if query is loading before rendering', ({ isQueryLoading, isRendered }) => {
+ createComponent({ isQueryLoading, mountFn: mount });
+
+ expect(findLoadingIcon().exists()).toBe(isRendered);
+ });
+ });
+
+ describe('when scrolling to the bottom of the list', () => {
+ beforeEach(async () => {
+ mockAvailableBranchQuery.mockResolvedValue(mockProjectBranches);
+ createComponentWithApollo();
+ await waitForPromises();
+ });
+
+ afterEach(() => {
+ mockAvailableBranchQuery.mockClear();
+ });
+
+ describe('when search term is empty', () => {
+ it('fetches more branches', async () => {
+ expect(mockAvailableBranchQuery).toHaveBeenCalledTimes(1);
+
+ findInfiniteScroll().vm.$emit('bottomReached');
+ await waitForPromises();
+
+ expect(mockAvailableBranchQuery).toHaveBeenCalledTimes(2);
+ });
+
+ it('calls the query with the correct variables', async () => {
+ findInfiniteScroll().vm.$emit('bottomReached');
+ await waitForPromises();
+
+ expect(mockAvailableBranchQuery).toHaveBeenCalledWith({
+ limit: mockBranchPaginationLimit,
+ offset: mockBranchPaginationLimit, // offset changed
+ projectFullPath: mockProjectFullPath,
+ searchPattern: '*',
+ });
+ });
+ });
+
+ describe('when search term exists', () => {
+ it('does not fetch more branches', async () => {
+ findSearchBox().vm.$emit('input', 'te');
+ await waitForPromises();
+
+ expect(mockAvailableBranchQuery).toHaveBeenCalledTimes(2);
+ mockAvailableBranchQuery.mockClear();
+
+ findInfiniteScroll().vm.$emit('bottomReached');
+ await waitForPromises();
+
+ expect(mockAvailableBranchQuery).not.toHaveBeenCalled();
+ });
+ });
+ });
});
diff --git a/spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js b/spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js
index 27652bb268b..e1dc08b637f 100644
--- a/spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js
+++ b/spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js
@@ -7,16 +7,10 @@ import { mockCiYml, mockLintResponse } from '../../mock_data';
describe('Pipeline editor header', () => {
let wrapper;
- const mockProvide = {
- glFeatures: {
- pipelineStatusForPipelineEditor: true,
- },
- };
const createComponent = ({ provide = {}, props = {} } = {}) => {
wrapper = shallowMount(PipelineEditorHeader, {
provide: {
- ...mockProvide,
...provide,
},
propsData: {
@@ -56,18 +50,4 @@ describe('Pipeline editor header', () => {
expect(findValidationSegment().exists()).toBe(true);
});
});
-
- describe('with pipeline status feature flag off', () => {
- beforeEach(() => {
- createComponent({
- provide: {
- glFeatures: { pipelineStatusForPipelineEditor: false },
- },
- });
- });
-
- it('does not render the pipeline status', () => {
- expect(findPipelineStatus().exists()).toBe(false);
- });
- });
});
diff --git a/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js b/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js
index eba853180cd..5cf8d47bc23 100644
--- a/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js
+++ b/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js
@@ -20,12 +20,6 @@ describe('Pipeline editor tabs component', () => {
const MockTextEditor = {
template: '<div />',
};
- const mockProvide = {
- glFeatures: {
- ciConfigVisualizationTab: true,
- ciConfigMergedTab: true,
- },
- };
const createComponent = ({
props = {},
@@ -44,7 +38,7 @@ describe('Pipeline editor tabs component', () => {
appStatus,
};
},
- provide: { ...mockProvide, ...provide },
+ provide: { ...provide },
stubs: {
TextEditor: MockTextEditor,
EditorTab,
@@ -82,41 +76,24 @@ describe('Pipeline editor tabs component', () => {
});
describe('visualization tab', () => {
- describe('with feature flag on', () => {
- describe('while loading', () => {
- beforeEach(() => {
- createComponent({ appStatus: EDITOR_APP_STATUS_LOADING });
- });
-
- it('displays a loading icon if the lint query is loading', () => {
- expect(findLoadingIcon().exists()).toBe(true);
- expect(findPipelineGraph().exists()).toBe(false);
- });
- });
- describe('after loading', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('display the tab and visualization', () => {
- expect(findVisualizationTab().exists()).toBe(true);
- expect(findPipelineGraph().exists()).toBe(true);
- });
+ describe('while loading', () => {
+ beforeEach(() => {
+ createComponent({ appStatus: EDITOR_APP_STATUS_LOADING });
});
- });
- describe('with feature flag off', () => {
+ it('displays a loading icon if the lint query is loading', () => {
+ expect(findLoadingIcon().exists()).toBe(true);
+ expect(findPipelineGraph().exists()).toBe(false);
+ });
+ });
+ describe('after loading', () => {
beforeEach(() => {
- createComponent({
- provide: {
- glFeatures: { ciConfigVisualizationTab: false },
- },
- });
+ createComponent();
});
- it('does not display the tab or component', () => {
- expect(findVisualizationTab().exists()).toBe(false);
- expect(findPipelineGraph().exists()).toBe(false);
+ it('display the tab and visualization', () => {
+ expect(findVisualizationTab().exists()).toBe(true);
+ expect(findPipelineGraph().exists()).toBe(true);
});
});
});
@@ -148,51 +125,39 @@ describe('Pipeline editor tabs component', () => {
});
describe('merged tab', () => {
- describe('with feature flag on', () => {
- describe('while loading', () => {
- beforeEach(() => {
- createComponent({ appStatus: EDITOR_APP_STATUS_LOADING });
- });
-
- it('displays a loading icon if the lint query is loading', () => {
- expect(findLoadingIcon().exists()).toBe(true);
- });
+ describe('while loading', () => {
+ beforeEach(() => {
+ createComponent({ appStatus: EDITOR_APP_STATUS_LOADING });
});
- describe('when there is a fetch error', () => {
- beforeEach(() => {
- createComponent({ appStatus: EDITOR_APP_STATUS_ERROR });
- });
-
- it('show an error message', () => {
- expect(findAlert().exists()).toBe(true);
- expect(findAlert().text()).toBe(wrapper.vm.$options.errorTexts.loadMergedYaml);
- });
+ it('displays a loading icon if the lint query is loading', () => {
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+ });
- it('does not render the `meged_preview` component', () => {
- expect(findMergedPreview().exists()).toBe(false);
- });
+ describe('when there is a fetch error', () => {
+ beforeEach(() => {
+ createComponent({ appStatus: EDITOR_APP_STATUS_ERROR });
});
- describe('after loading', () => {
- beforeEach(() => {
- createComponent();
- });
+ it('show an error message', () => {
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().text()).toBe(wrapper.vm.$options.errorTexts.loadMergedYaml);
+ });
- it('display the tab and the merged preview component', () => {
- expect(findMergedTab().exists()).toBe(true);
- expect(findMergedPreview().exists()).toBe(true);
- });
+ it('does not render the `merged_preview` component', () => {
+ expect(findMergedPreview().exists()).toBe(false);
});
});
- describe('with feature flag off', () => {
+
+ describe('after loading', () => {
beforeEach(() => {
- createComponent({ provide: { glFeatures: { ciConfigMergedTab: false } } });
+ createComponent();
});
- it('does not display the merged tab', () => {
- expect(findMergedTab().exists()).toBe(false);
- expect(findMergedPreview().exists()).toBe(false);
+ it('display the tab and the merged preview component', () => {
+ expect(findMergedTab().exists()).toBe(true);
+ expect(findMergedPreview().exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/pipeline_editor/components/ui/pipeline_editor_empty_state_spec.js b/spec/frontend/pipeline_editor/components/ui/pipeline_editor_empty_state_spec.js
index b444d9dcfea..76c68e21180 100644
--- a/spec/frontend/pipeline_editor/components/ui/pipeline_editor_empty_state_spec.js
+++ b/spec/frontend/pipeline_editor/components/ui/pipeline_editor_empty_state_spec.js
@@ -1,11 +1,13 @@
import { GlButton, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
+import PipelineEditorFileNav from '~/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue';
import PipelineEditorEmptyState from '~/pipeline_editor/components/ui/pipeline_editor_empty_state.vue';
describe('Pipeline editor empty state', () => {
let wrapper;
const defaultProvide = {
glFeatures: {
+ pipelineEditorBranchSwitcher: true,
pipelineEditorEmptyStateAction: false,
},
emptyStateIllustrationPath: 'my/svg/path',
@@ -17,6 +19,7 @@ describe('Pipeline editor empty state', () => {
});
};
+ const findFileNav = () => wrapper.findComponent(PipelineEditorFileNav);
const findSvgImage = () => wrapper.find('img');
const findTitle = () => wrapper.find('h1');
const findConfirmButton = () => wrapper.findComponent(GlButton);
@@ -45,6 +48,10 @@ describe('Pipeline editor empty state', () => {
expect(findDescription().html()).toContain(wrapper.vm.$options.i18n.body);
});
+ it('renders the file nav', () => {
+ expect(findFileNav().exists()).toBe(true);
+ });
+
describe('with feature flag off', () => {
it('does not renders a CTA button', () => {
expect(findConfirmButton().exists()).toBe(false);
@@ -75,5 +82,17 @@ describe('Pipeline editor empty state', () => {
await findConfirmButton().vm.$emit('click');
expect(wrapper.emitted(expectedEvent)).toHaveLength(1);
});
+
+ describe('with branch switcher feature flag OFF', () => {
+ it('does not render the file nav', () => {
+ createComponent({
+ provide: {
+ glFeatures: { pipelineEditorBranchSwitcher: false },
+ },
+ });
+
+ expect(findFileNav().exists()).toBe(false);
+ });
+ });
});
});
diff --git a/spec/frontend/pipeline_editor/components/ui/pipeline_editor_messages_spec.js b/spec/frontend/pipeline_editor/components/ui/pipeline_editor_messages_spec.js
new file mode 100644
index 00000000000..93ebbc648fe
--- /dev/null
+++ b/spec/frontend/pipeline_editor/components/ui/pipeline_editor_messages_spec.js
@@ -0,0 +1,137 @@
+import { GlAlert } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { TEST_HOST } from 'helpers/test_constants';
+import CodeSnippetAlert from '~/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue';
+import { CODE_SNIPPET_SOURCES } from '~/pipeline_editor/components/code_snippet_alert/constants';
+import PipelineEditorMessages from '~/pipeline_editor/components/ui/pipeline_editor_messages.vue';
+import {
+ COMMIT_FAILURE,
+ COMMIT_SUCCESS,
+ DEFAULT_FAILURE,
+ DEFAULT_SUCCESS,
+ LOAD_FAILURE_UNKNOWN,
+} from '~/pipeline_editor/constants';
+
+describe('Pipeline Editor messages', () => {
+ let wrapper;
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(PipelineEditorMessages, {
+ propsData: props,
+ });
+ };
+
+ const findCodeSnippetAlert = () => wrapper.findComponent(CodeSnippetAlert);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+
+ describe('success alert', () => {
+ it('shows a message for successful commit type', () => {
+ createComponent({ successType: COMMIT_SUCCESS, showSuccess: true });
+
+ expect(findAlert().text()).toBe(wrapper.vm.$options.successTexts[COMMIT_SUCCESS]);
+ });
+
+ it('does not show alert when there is a successType but visibility is off', () => {
+ createComponent({ successType: COMMIT_SUCCESS, showSuccess: false });
+
+ expect(findAlert().exists()).toBe(false);
+ });
+
+ it('shows a success alert with default copy if `showSuccess` is true and the `successType` is not valid,', () => {
+ createComponent({ successType: 'random', showSuccess: true });
+
+ expect(findAlert().text()).toBe(wrapper.vm.$options.successTexts[DEFAULT_SUCCESS]);
+ });
+
+ it('emit `hide-success` event when clicking on the dismiss button', async () => {
+ const expectedEvent = 'hide-success';
+
+ createComponent({ successType: COMMIT_SUCCESS, showSuccess: true });
+ expect(wrapper.emitted(expectedEvent)).not.toBeDefined();
+
+ await findAlert().vm.$emit('dismiss');
+
+ expect(wrapper.emitted(expectedEvent)).toBeDefined();
+ });
+ });
+
+ describe('failure alert', () => {
+ it.each`
+ failureType | message | expectedFailureType
+ ${COMMIT_FAILURE} | ${'failed commit'} | ${COMMIT_FAILURE}
+ ${LOAD_FAILURE_UNKNOWN} | ${'loading failure'} | ${LOAD_FAILURE_UNKNOWN}
+ ${'random'} | ${'error without a specified type'} | ${DEFAULT_FAILURE}
+ `('shows a message for $message', ({ failureType, expectedFailureType }) => {
+ createComponent({ failureType, showFailure: true });
+
+ expect(findAlert().text()).toBe(wrapper.vm.$options.errorTexts[expectedFailureType]);
+ });
+
+ it('show failure reasons when there are some', () => {
+ const failureReasons = ['There was a problem', 'ouppps'];
+ createComponent({ failureType: COMMIT_FAILURE, failureReasons, showFailure: true });
+
+ expect(wrapper.html()).toContain(failureReasons[0]);
+ expect(wrapper.html()).toContain(failureReasons[1]);
+ });
+
+ it('does not show a message for error with a disabled visibility', () => {
+ createComponent({ failureType: 'random', showFailure: false });
+
+ expect(findAlert().exists()).toBe(false);
+ });
+
+ it('emit `hide-failure` event when clicking on the dismiss button', async () => {
+ const expectedEvent = 'hide-failure';
+
+ createComponent({ failureType: COMMIT_FAILURE, showFailure: true });
+ expect(wrapper.emitted(expectedEvent)).not.toBeDefined();
+
+ await findAlert().vm.$emit('dismiss');
+
+ expect(wrapper.emitted(expectedEvent)).toBeDefined();
+ });
+ });
+
+ describe('code snippet alert', () => {
+ const setCodeSnippetUrlParam = (value) => {
+ global.jsdom.reconfigure({
+ url: `${TEST_HOST}/?code_snippet_copied_from=${value}`,
+ });
+ };
+
+ it('does not show by default', () => {
+ createComponent();
+
+ expect(findCodeSnippetAlert().exists()).toBe(false);
+ });
+
+ it.each(CODE_SNIPPET_SOURCES)('shows if URL param is %s, and cleans up URL', (source) => {
+ jest.spyOn(window.history, 'replaceState');
+ setCodeSnippetUrlParam(source);
+ createComponent();
+
+ expect(findCodeSnippetAlert().exists()).toBe(true);
+ expect(window.history.replaceState).toHaveBeenCalledWith({}, document.title, `${TEST_HOST}/`);
+ });
+
+ it('does not show if URL param is invalid', () => {
+ setCodeSnippetUrlParam('foo_bar');
+ createComponent();
+
+ expect(findCodeSnippetAlert().exists()).toBe(false);
+ });
+
+ it('disappears on dismiss', async () => {
+ setCodeSnippetUrlParam('api_fuzzing');
+ createComponent();
+ const alert = findCodeSnippetAlert();
+
+ expect(alert.exists()).toBe(true);
+
+ await alert.vm.$emit('dismiss');
+
+ expect(alert.exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap b/spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap
index 8670c44f6f6..ee5a3cb288f 100644
--- a/spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap
+++ b/spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap
@@ -17,7 +17,7 @@ Object {
"environment": "prd",
"except": Object {
"refs": Array [
- "master@gitlab-org/gitlab",
+ "main@gitlab-org/gitlab",
"/^release/.*$/@gitlab-org/gitlab",
],
},
@@ -44,7 +44,7 @@ Object {
"environment": "stg",
"except": Object {
"refs": Array [
- "master@gitlab-org/gitlab",
+ "main@gitlab-org/gitlab",
"/^release/.*$/@gitlab-org/gitlab",
],
},
diff --git a/spec/frontend/pipeline_editor/graphql/resolvers_spec.js b/spec/frontend/pipeline_editor/graphql/resolvers_spec.js
index f0932fc55d3..d39c0d80296 100644
--- a/spec/frontend/pipeline_editor/graphql/resolvers_spec.js
+++ b/spec/frontend/pipeline_editor/graphql/resolvers_spec.js
@@ -9,7 +9,6 @@ import {
mockDefaultBranch,
mockLintResponse,
mockProjectFullPath,
- mockProjectBranches,
} from '../mock_data';
jest.mock('~/api', () => {
@@ -47,23 +46,6 @@ describe('~/pipeline_editor/graphql/resolvers', () => {
await expect(result.rawData).resolves.toBe(mockCiYml);
});
});
-
- describe('project', () => {
- it('resolves project data with type names', async () => {
- const result = await resolvers.Query.project();
-
- // eslint-disable-next-line no-underscore-dangle
- expect(result.__typename).toBe('Project');
- });
-
- it('resolves project with available list of branches', async () => {
- const result = await resolvers.Query.project();
-
- expect(result.repository.branches).toHaveLength(
- mockProjectBranches.repository.branches.length,
- );
- });
- });
});
describe('Mutation', () => {
diff --git a/spec/frontend/pipeline_editor/mock_data.js b/spec/frontend/pipeline_editor/mock_data.js
index 7f651a42231..e08fce3ceb9 100644
--- a/spec/frontend/pipeline_editor/mock_data.js
+++ b/spec/frontend/pipeline_editor/mock_data.js
@@ -4,7 +4,7 @@ import { unwrapStagesWithNeeds } from '~/pipelines/components/unwrapping_utils';
export const mockProjectNamespace = 'user1';
export const mockProjectPath = 'project1';
export const mockProjectFullPath = `${mockProjectNamespace}/${mockProjectPath}`;
-export const mockDefaultBranch = 'master';
+export const mockDefaultBranch = 'main';
export const mockNewMergeRequestPath = '/-/merge_requests/new';
export const mockCommitSha = 'aabbccdd';
export const mockCommitNextSha = 'eeffgghh';
@@ -139,19 +139,54 @@ export const mergeUnwrappedCiConfig = (mergedConfig) => {
};
export const mockProjectBranches = {
- __typename: 'Project',
- repository: {
- __typename: 'Repository',
- branches: [
- { __typename: 'Branch', name: 'master' },
- { __typename: 'Branch', name: 'main' },
- { __typename: 'Branch', name: 'develop' },
- { __typename: 'Branch', name: 'production' },
- { __typename: 'Branch', name: 'test' },
- ],
+ data: {
+ project: {
+ repository: {
+ branchNames: [
+ 'main',
+ 'develop',
+ 'production',
+ 'test',
+ 'better-feature',
+ 'feature-abc',
+ 'update-ci',
+ 'mock-feature',
+ 'test-merge-request',
+ 'staging',
+ ],
+ },
+ },
},
};
+export const mockTotalBranchResults =
+ mockProjectBranches.data.project.repository.branchNames.length;
+
+export const mockSearchBranches = {
+ data: {
+ project: {
+ repository: {
+ branchNames: ['test', 'better-feature', 'update-ci', 'test-merge-request'],
+ },
+ },
+ },
+};
+
+export const mockTotalSearchResults = mockSearchBranches.data.project.repository.branchNames.length;
+
+export const mockEmptySearchBranches = {
+ data: {
+ project: {
+ repository: {
+ branchNames: [],
+ },
+ },
+ },
+};
+
+export const mockBranchPaginationLimit = 10;
+export const mockTotalBranches = 20; // must be greater than mockBranchPaginationLimit to test pagination
+
export const mockProjectPipeline = {
pipeline: {
commitPath: '/-/commit/aabbccdd',
@@ -186,7 +221,7 @@ export const mockLintResponse = {
when: 'on_success',
allow_failure: false,
only: null,
- except: { refs: ['master@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
+ except: { refs: ['main@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
},
{
name: 'job_2',
@@ -199,7 +234,7 @@ export const mockLintResponse = {
when: 'on_success',
allow_failure: true,
only: { refs: ['web', 'chat', 'pushes'] },
- except: { refs: ['master@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
+ except: { refs: ['main@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
},
],
};
@@ -242,7 +277,7 @@ export const mockJobs = [
when: 'on_success',
allowFailure: false,
only: { refs: ['branches@gitlab-org/gitlab'] },
- except: { refs: ['master@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
+ except: { refs: ['main@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
},
];
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
index d8e3436479c..c88fe159c0d 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
@@ -2,17 +2,15 @@ import { GlAlert, GlButton, GlLoadingIcon, GlTabs } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
-import { TEST_HOST } from 'helpers/test_constants';
import waitForPromises from 'helpers/wait_for_promises';
import httpStatusCodes from '~/lib/utils/http_status';
-import CodeSnippetAlert from '~/pipeline_editor/components/code_snippet_alert/code_snippet_alert.vue';
-import { CODE_SNIPPET_SOURCES } from '~/pipeline_editor/components/code_snippet_alert/constants';
import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue';
import TextEditor from '~/pipeline_editor/components/editor/text_editor.vue';
import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue';
import PipelineEditorEmptyState from '~/pipeline_editor/components/ui/pipeline_editor_empty_state.vue';
-import { COMMIT_SUCCESS, COMMIT_FAILURE, LOAD_FAILURE_UNKNOWN } from '~/pipeline_editor/constants';
+import PipelineEditorMessages from '~/pipeline_editor/components/ui/pipeline_editor_messages.vue';
+import { COMMIT_SUCCESS, COMMIT_FAILURE } from '~/pipeline_editor/constants';
import getCiConfigData from '~/pipeline_editor/graphql/queries/ci_config.graphql';
import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue';
import PipelineEditorHome from '~/pipeline_editor/pipeline_editor_home.vue';
@@ -56,6 +54,7 @@ describe('Pipeline editor app component', () => {
CommitForm,
PipelineEditorHome,
PipelineEditorTabs,
+ PipelineEditorMessages,
EditorLite: MockEditorLite,
PipelineEditorEmptyState,
},
@@ -92,6 +91,11 @@ describe('Pipeline editor app component', () => {
const options = {
localVue,
+ data() {
+ return {
+ currentBranch: mockDefaultBranch,
+ };
+ },
mocks: {},
apolloProvider: mockApollo,
};
@@ -108,7 +112,6 @@ describe('Pipeline editor app component', () => {
const findEmptyState = () => wrapper.findComponent(PipelineEditorEmptyState);
const findEmptyStateButton = () =>
wrapper.findComponent(PipelineEditorEmptyState).findComponent(GlButton);
- const findCodeSnippetAlert = () => wrapper.findComponent(CodeSnippetAlert);
beforeEach(() => {
mockBlobContentData = jest.fn();
@@ -116,9 +119,6 @@ describe('Pipeline editor app component', () => {
});
afterEach(() => {
- mockBlobContentData.mockReset();
- mockCiConfigData.mockReset();
-
wrapper.destroy();
});
@@ -131,48 +131,6 @@ describe('Pipeline editor app component', () => {
});
});
- describe('code snippet alert', () => {
- const setCodeSnippetUrlParam = (value) => {
- global.jsdom.reconfigure({
- url: `${TEST_HOST}/?code_snippet_copied_from=${value}`,
- });
- };
-
- it('does not show by default', () => {
- createComponent();
-
- expect(findCodeSnippetAlert().exists()).toBe(false);
- });
-
- it.each(CODE_SNIPPET_SOURCES)('shows if URL param is %s, and cleans up URL', (source) => {
- jest.spyOn(window.history, 'replaceState');
- setCodeSnippetUrlParam(source);
- createComponent();
-
- expect(findCodeSnippetAlert().exists()).toBe(true);
- expect(window.history.replaceState).toHaveBeenCalledWith({}, document.title, `${TEST_HOST}/`);
- });
-
- it('does not show if URL param is invalid', () => {
- setCodeSnippetUrlParam('foo_bar');
- createComponent();
-
- expect(findCodeSnippetAlert().exists()).toBe(false);
- });
-
- it('disappears on dismiss', async () => {
- setCodeSnippetUrlParam('api_fuzzing');
- createComponent();
- const alert = findCodeSnippetAlert();
-
- expect(alert.exists()).toBe(true);
-
- await alert.vm.$emit('dismiss');
-
- expect(alert.exists()).toBe(false);
- });
- });
-
describe('when queries are called', () => {
beforeEach(() => {
mockBlobContentData.mockResolvedValue(mockCiYml);
@@ -233,11 +191,14 @@ describe('Pipeline editor app component', () => {
describe('because of a fetching error', () => {
it('shows a unkown error message', async () => {
+ const loadUnknownFailureText = 'The CI configuration was not loaded, please try again.';
+
mockBlobContentData.mockRejectedValueOnce(new Error('My error!'));
await createComponentWithApollo();
expect(findEmptyState().exists()).toBe(false);
- expect(findAlert().text()).toBe(wrapper.vm.$options.errorTexts[LOAD_FAILURE_UNKNOWN]);
+
+ expect(findAlert().text()).toBe(loadUnknownFailureText);
expect(findEditorHome().exists()).toBe(true);
});
});
@@ -271,6 +232,7 @@ describe('Pipeline editor app component', () => {
describe('when the user commits', () => {
const updateFailureMessage = 'The GitLab CI configuration could not be updated.';
+ const updateSuccessMessage = 'Your changes have been successfully committed.';
describe('and the commit mutation succeeds', () => {
beforeEach(() => {
@@ -281,7 +243,7 @@ describe('Pipeline editor app component', () => {
});
it('shows a confirmation message', () => {
- expect(findAlert().text()).toBe(wrapper.vm.$options.successTexts[COMMIT_SUCCESS]);
+ expect(findAlert().text()).toBe(updateSuccessMessage);
});
it('scrolls to the top of the page to bring attention to the confirmation message', () => {
@@ -337,4 +299,37 @@ describe('Pipeline editor app component', () => {
});
});
});
+
+ describe('when refetching content', () => {
+ it('refetches blob content', async () => {
+ await createComponentWithApollo();
+ jest
+ .spyOn(wrapper.vm.$apollo.queries.initialCiFileContent, 'refetch')
+ .mockImplementation(jest.fn());
+
+ expect(wrapper.vm.$apollo.queries.initialCiFileContent.refetch).toHaveBeenCalledTimes(0);
+
+ await wrapper.vm.refetchContent();
+
+ expect(wrapper.vm.$apollo.queries.initialCiFileContent.refetch).toHaveBeenCalledTimes(1);
+ });
+
+ it('hides start screen when refetch fetches CI file', async () => {
+ mockBlobContentData.mockRejectedValue({
+ response: {
+ status: httpStatusCodes.NOT_FOUND,
+ },
+ });
+ await createComponentWithApollo();
+
+ expect(findEmptyState().exists()).toBe(true);
+ expect(findEditorHome().exists()).toBe(false);
+
+ mockBlobContentData.mockResolvedValue(mockCiYml);
+ await wrapper.vm.$apollo.queries.initialCiFileContent.refetch();
+
+ expect(findEmptyState().exists()).toBe(false);
+ expect(findEditorHome().exists()).toBe(true);
+ });
+ });
});
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js
index a1e3d24acfa..7aba336b8e8 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js
@@ -2,6 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import CommitSection from '~/pipeline_editor/components/commit/commit_section.vue';
+import PipelineEditorDrawer from '~/pipeline_editor/components/drawer/pipeline_editor_drawer.vue';
import PipelineEditorFileNav from '~/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue';
import PipelineEditorHeader from '~/pipeline_editor/components/header/pipeline_editor_header.vue';
import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue';
@@ -13,7 +14,7 @@ import { mockLintResponse, mockCiYml } from './mock_data';
describe('Pipeline editor home wrapper', () => {
let wrapper;
- const createComponent = ({ props = {} } = {}) => {
+ const createComponent = ({ props = {}, glFeatures = {} } = {}) => {
wrapper = shallowMount(PipelineEditorHome, {
propsData: {
ciConfigData: mockLintResponse,
@@ -22,13 +23,20 @@ describe('Pipeline editor home wrapper', () => {
isNewCiConfigFile: false,
...props,
},
+ provide: {
+ glFeatures: {
+ pipelineEditorDrawer: true,
+ ...glFeatures,
+ },
+ },
});
};
- const findPipelineEditorHeader = () => wrapper.findComponent(PipelineEditorHeader);
- const findPipelineEditorTabs = () => wrapper.findComponent(PipelineEditorTabs);
const findCommitSection = () => wrapper.findComponent(CommitSection);
const findFileNav = () => wrapper.findComponent(PipelineEditorFileNav);
+ const findPipelineEditorDrawer = () => wrapper.findComponent(PipelineEditorDrawer);
+ const findPipelineEditorHeader = () => wrapper.findComponent(PipelineEditorHeader);
+ const findPipelineEditorTabs = () => wrapper.findComponent(PipelineEditorTabs);
afterEach(() => {
wrapper.destroy();
@@ -55,6 +63,10 @@ describe('Pipeline editor home wrapper', () => {
it('shows the commit section by default', () => {
expect(findCommitSection().exists()).toBe(true);
});
+
+ it('show the pipeline drawer', () => {
+ expect(findPipelineEditorDrawer().exists()).toBe(true);
+ });
});
describe('commit form toggle', () => {
@@ -82,4 +94,12 @@ describe('Pipeline editor home wrapper', () => {
expect(findCommitSection().exists()).toBe(true);
});
});
+
+ describe('Pipeline drawer', () => {
+ it('hides the drawer when the feature flag is off', () => {
+ createComponent({ glFeatures: { pipelineEditorDrawer: false } });
+
+ expect(findPipelineEditorDrawer().exists()).toBe(false);
+ });
+ });
});