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
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-04-27 21:09:41 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-04-27 21:09:41 +0300
commitf569792df8a25caa1bed9c448c8c4c3f837f5164 (patch)
tree8c2ed7dae5ba132a97c0321a7649174e5832d637 /spec
parentc2908ec6a0d7b62996cdb8da0350705bdad691bf (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/factories/identities.rb2
-rw-r--r--spec/frontend/alert_management/components/alert_management_list_spec.js11
-rw-r--r--spec/frontend/boards/boards_store_spec.js61
-rw-r--r--spec/frontend/boards/issue_spec.js22
-rw-r--r--spec/frontend/deploy_keys/components/action_btn_spec.js54
-rw-r--r--spec/frontend/deploy_keys/components/app_spec.js142
-rw-r--r--spec/frontend/deploy_keys/components/key_spec.js161
-rw-r--r--spec/frontend/deploy_keys/components/keys_panel_spec.js63
-rw-r--r--spec/frontend/dirty_submit/dirty_submit_collection_spec.js22
-rw-r--r--spec/frontend/dirty_submit/dirty_submit_factory_spec.js (renamed from spec/javascripts/dirty_submit/dirty_submit_factory_spec.js)0
-rw-r--r--spec/frontend/dirty_submit/dirty_submit_form_spec.js (renamed from spec/javascripts/dirty_submit/dirty_submit_form_spec.js)65
-rw-r--r--spec/frontend/dirty_submit/helper.js (renamed from spec/javascripts/dirty_submit/helper.js)5
-rw-r--r--spec/frontend/pipelines/mock_data.js423
-rw-r--r--spec/frontend/pipelines/pipelines_spec.js659
-rw-r--r--spec/frontend/users_select/utils_spec.js33
-rw-r--r--spec/graphql/types/jira_import_type_spec.rb2
-rw-r--r--spec/helpers/explore_helper_spec.rb19
-rw-r--r--spec/helpers/projects/alert_management_helper_spec.rb17
-rw-r--r--spec/javascripts/deploy_keys/components/action_btn_spec.js72
-rw-r--r--spec/javascripts/deploy_keys/components/app_spec.js155
-rw-r--r--spec/javascripts/deploy_keys/components/key_spec.js157
-rw-r--r--spec/javascripts/deploy_keys/components/keys_panel_spec.js63
-rw-r--r--spec/javascripts/dirty_submit/dirty_submit_collection_spec.js29
-rw-r--r--spec/javascripts/pipelines/pipelines_spec.js783
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb10
-rw-r--r--spec/lib/gitlab/kubernetes/helm/base_command_spec.rb80
-rw-r--r--spec/lib/gitlab/kubernetes/helm/delete_command_spec.rb35
-rw-r--r--spec/lib/gitlab/kubernetes/helm/init_command_spec.rb73
-rw-r--r--spec/lib/gitlab/kubernetes/helm/install_command_spec.rb84
-rw-r--r--spec/lib/gitlab/kubernetes/helm/patch_command_spec.rb68
-rw-r--r--spec/lib/gitlab/kubernetes/helm/reset_command_spec.rb33
-rw-r--r--spec/lib/gitlab/visibility_level_spec.rb24
-rw-r--r--spec/models/jira_import_state_spec.rb1
-rw-r--r--spec/requests/api/graphql/project/jira_import_spec.rb1
-rw-r--r--spec/requests/api/settings_spec.rb10
-rw-r--r--spec/rubocop/cop/api/grape_api_instance_spec.rb31
-rw-r--r--spec/rubocop/cop/api/grape_array_missing_coerce_spec.rb64
-rw-r--r--spec/rubocop/cop/code_reuse/worker_spec.rb2
-rw-r--r--spec/rubocop/cop/gitlab/json_spec.rb39
-rw-r--r--spec/support/shared_examples/helm_commands_shared_examples.rb131
-rw-r--r--spec/support/shared_examples/lib/gitlab/helm_generated_script_shared_examples.rb15
41 files changed, 2008 insertions, 1713 deletions
diff --git a/spec/factories/identities.rb b/spec/factories/identities.rb
index fda4bfa589b..a2615ce30c3 100644
--- a/spec/factories/identities.rb
+++ b/spec/factories/identities.rb
@@ -3,6 +3,6 @@
FactoryBot.define do
factory :identity do
provider { 'ldapmain' }
- sequence(:extern_uid) { |n| "my-ldap-id-#{n}" }
+ extern_uid { 'my-ldap-id' }
end
end
diff --git a/spec/frontend/alert_management/components/alert_management_list_spec.js b/spec/frontend/alert_management/components/alert_management_list_spec.js
index 9753300d035..c18c2ec0d53 100644
--- a/spec/frontend/alert_management/components/alert_management_list_spec.js
+++ b/spec/frontend/alert_management/components/alert_management_list_spec.js
@@ -12,7 +12,10 @@ describe('AlertManagementList', () => {
function mountComponent({
stubs = {},
- props = { alertManagementEnabled: false },
+ props = {
+ alertManagementEnabled: false,
+ userCanEnableAlertManagement: false,
+ },
data = {},
loading = false,
} = {}) {
@@ -62,7 +65,7 @@ describe('AlertManagementList', () => {
it('loading state', () => {
mountComponent({
stubs: { GlTable },
- props: { alertManagementEnabled: true },
+ props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: null },
loading: true,
});
@@ -73,7 +76,7 @@ describe('AlertManagementList', () => {
it('error state', () => {
mountComponent({
stubs: { GlTable },
- props: { alertManagementEnabled: true },
+ props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: null, errored: true },
loading: false,
});
@@ -86,7 +89,7 @@ describe('AlertManagementList', () => {
it('empty state', () => {
mountComponent({
stubs: { GlTable },
- props: { alertManagementEnabled: true },
+ props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: [], errored: false },
loading: false,
});
diff --git a/spec/frontend/boards/boards_store_spec.js b/spec/frontend/boards/boards_store_spec.js
index 5c5315fd465..05a44138275 100644
--- a/spec/frontend/boards/boards_store_spec.js
+++ b/spec/frontend/boards/boards_store_spec.js
@@ -1040,5 +1040,66 @@ describe('boardsStore', () => {
});
});
});
+
+ describe('updateIssue', () => {
+ let issue;
+ let patchSpy;
+
+ beforeEach(() => {
+ issue = new ListIssue({
+ title: 'Testing',
+ id: 1,
+ iid: 1,
+ confidential: false,
+ labels: [
+ {
+ id: 1,
+ title: 'test',
+ color: 'red',
+ description: 'testing',
+ },
+ ],
+ assignees: [
+ {
+ id: 1,
+ name: 'name',
+ username: 'username',
+ avatar_url: 'http://avatar_url',
+ },
+ ],
+ real_path: 'path/to/issue',
+ });
+
+ patchSpy = jest.fn().mockReturnValue([200, { labels: [] }]);
+ axiosMock.onPatch(`path/to/issue.json`).reply(({ data }) => patchSpy(JSON.parse(data)));
+ });
+
+ it('passes assignee ids when there are assignees', () => {
+ boardsStore.updateIssue(issue);
+ return boardsStore.updateIssue(issue).then(() => {
+ expect(patchSpy).toHaveBeenCalledWith({
+ issue: {
+ milestone_id: null,
+ assignee_ids: [1],
+ label_ids: [1],
+ },
+ });
+ });
+ });
+
+ it('passes assignee ids of [0] when there are no assignees', () => {
+ issue.removeAllAssignees();
+
+ return boardsStore.updateIssue(issue).then(() => {
+ expect(patchSpy).toHaveBeenCalledWith({
+ issue: {
+ milestone_id: null,
+ assignee_ids: [0],
+ label_ids: [1],
+ },
+ });
+ });
+ });
+ });
});
});
diff --git a/spec/frontend/boards/issue_spec.js b/spec/frontend/boards/issue_spec.js
index ff72edaa695..412f20684f5 100644
--- a/spec/frontend/boards/issue_spec.js
+++ b/spec/frontend/boards/issue_spec.js
@@ -1,6 +1,5 @@
/* global ListIssue */
-import axios from '~/lib/utils/axios_utils';
import '~/boards/models/label';
import '~/boards/models/assignee';
import '~/boards/models/issue';
@@ -173,25 +172,12 @@ describe('Issue model', () => {
});
describe('update', () => {
- it('passes assignee ids when there are assignees', done => {
- jest.spyOn(axios, 'patch').mockImplementation((url, data) => {
- expect(data.issue.assignee_ids).toEqual([1]);
- done();
- return Promise.resolve();
- });
-
- issue.update('url');
- });
+ it('passes update to boardsStore', () => {
+ jest.spyOn(boardsStore, 'updateIssue').mockImplementation();
- it('passes assignee ids of [0] when there are no assignees', done => {
- jest.spyOn(axios, 'patch').mockImplementation((url, data) => {
- expect(data.issue.assignee_ids).toEqual([0]);
- done();
- return Promise.resolve();
- });
+ issue.update();
- issue.removeAllAssignees();
- issue.update('url');
+ expect(boardsStore.updateIssue).toHaveBeenCalledWith(issue);
});
});
});
diff --git a/spec/frontend/deploy_keys/components/action_btn_spec.js b/spec/frontend/deploy_keys/components/action_btn_spec.js
new file mode 100644
index 00000000000..b8211b02464
--- /dev/null
+++ b/spec/frontend/deploy_keys/components/action_btn_spec.js
@@ -0,0 +1,54 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlLoadingIcon } from '@gitlab/ui';
+import eventHub from '~/deploy_keys/eventhub';
+import actionBtn from '~/deploy_keys/components/action_btn.vue';
+
+describe('Deploy keys action btn', () => {
+ const data = getJSONFixture('deploy_keys/keys.json');
+ const deployKey = data.enabled_keys[0];
+ let wrapper;
+
+ const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+
+ beforeEach(() => {
+ wrapper = shallowMount(actionBtn, {
+ propsData: {
+ deployKey,
+ type: 'enable',
+ },
+ slots: {
+ default: 'Enable',
+ },
+ });
+ });
+
+ it('renders the default slot', () => {
+ expect(wrapper.text()).toBe('Enable');
+ });
+
+ it('sends eventHub event with btn type', () => {
+ jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
+
+ wrapper.trigger('click');
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(eventHub.$emit).toHaveBeenCalledWith('enable.key', deployKey, expect.anything());
+ });
+ });
+
+ it('shows loading spinner after click', () => {
+ wrapper.trigger('click');
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+ });
+
+ it('disables button after click', () => {
+ wrapper.trigger('click');
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.attributes('disabled')).toBe('disabled');
+ });
+ });
+});
diff --git a/spec/frontend/deploy_keys/components/app_spec.js b/spec/frontend/deploy_keys/components/app_spec.js
new file mode 100644
index 00000000000..291502c9ed7
--- /dev/null
+++ b/spec/frontend/deploy_keys/components/app_spec.js
@@ -0,0 +1,142 @@
+import { mount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import { TEST_HOST } from 'spec/test_constants';
+import waitForPromises from 'helpers/wait_for_promises';
+import axios from '~/lib/utils/axios_utils';
+import eventHub from '~/deploy_keys/eventhub';
+import deployKeysApp from '~/deploy_keys/components/app.vue';
+
+const TEST_ENDPOINT = `${TEST_HOST}/dummy/`;
+
+describe('Deploy keys app component', () => {
+ const data = getJSONFixture('deploy_keys/keys.json');
+ let wrapper;
+ let mock;
+
+ const mountComponent = () => {
+ wrapper = mount(deployKeysApp, {
+ propsData: {
+ endpoint: TEST_ENDPOINT,
+ projectId: '8',
+ },
+ });
+
+ return waitForPromises();
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ mock.onGet(TEST_ENDPOINT).reply(200, data);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ mock.restore();
+ });
+
+ const findLoadingIcon = () => wrapper.find('.gl-spinner');
+ const findKeyPanels = () => wrapper.findAll('.deploy-keys .nav-links li');
+
+ it('renders loading icon while waiting for request', () => {
+ mock.onGet(TEST_ENDPOINT).reply(() => new Promise());
+
+ mountComponent();
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+ });
+
+ it('renders keys panels', () => {
+ return mountComponent().then(() => {
+ expect(findKeyPanels().length).toBe(3);
+ });
+ });
+
+ it.each`
+ selector | label | count
+ ${'.js-deployKeys-tab-enabled_keys'} | ${'Enabled deploy keys'} | ${1}
+ ${'.js-deployKeys-tab-available_project_keys'} | ${'Privately accessible deploy keys'} | ${0}
+ ${'.js-deployKeys-tab-public_keys'} | ${'Publicly accessible deploy keys'} | ${1}
+ `('$selector title is $label with keys count equal to $count', ({ selector, label, count }) => {
+ return mountComponent().then(() => {
+ const element = wrapper.find(selector);
+ expect(element.exists()).toBe(true);
+ expect(element.text().trim()).toContain(label);
+
+ expect(
+ element
+ .find('.badge')
+ .text()
+ .trim(),
+ ).toBe(count.toString());
+ });
+ });
+
+ it('does not render key panels when keys object is empty', () => {
+ mock.onGet(TEST_ENDPOINT).reply(200, []);
+
+ return mountComponent().then(() => {
+ expect(findKeyPanels().length).toBe(0);
+ });
+ });
+
+ it('re-fetches deploy keys when enabling a key', () => {
+ const key = data.public_keys[0];
+ return mountComponent()
+ .then(() => {
+ jest.spyOn(wrapper.vm.service, 'getKeys').mockImplementation(() => {});
+ jest.spyOn(wrapper.vm.service, 'enableKey').mockImplementation(() => Promise.resolve());
+
+ eventHub.$emit('enable.key', key);
+
+ return wrapper.vm.$nextTick();
+ })
+ .then(() => {
+ expect(wrapper.vm.service.enableKey).toHaveBeenCalledWith(key.id);
+ expect(wrapper.vm.service.getKeys).toHaveBeenCalled();
+ });
+ });
+
+ it('re-fetches deploy keys when disabling a key', () => {
+ const key = data.public_keys[0];
+ return mountComponent()
+ .then(() => {
+ jest.spyOn(window, 'confirm').mockReturnValue(true);
+ jest.spyOn(wrapper.vm.service, 'getKeys').mockImplementation(() => {});
+ jest.spyOn(wrapper.vm.service, 'disableKey').mockImplementation(() => Promise.resolve());
+
+ eventHub.$emit('disable.key', key);
+
+ return wrapper.vm.$nextTick();
+ })
+ .then(() => {
+ expect(wrapper.vm.service.disableKey).toHaveBeenCalledWith(key.id);
+ expect(wrapper.vm.service.getKeys).toHaveBeenCalled();
+ });
+ });
+
+ it('calls disableKey when removing a key', () => {
+ const key = data.public_keys[0];
+ return mountComponent()
+ .then(() => {
+ jest.spyOn(window, 'confirm').mockReturnValue(true);
+ jest.spyOn(wrapper.vm.service, 'getKeys').mockImplementation(() => {});
+ jest.spyOn(wrapper.vm.service, 'disableKey').mockImplementation(() => Promise.resolve());
+
+ eventHub.$emit('remove.key', key);
+
+ return wrapper.vm.$nextTick();
+ })
+ .then(() => {
+ expect(wrapper.vm.service.disableKey).toHaveBeenCalledWith(key.id);
+ expect(wrapper.vm.service.getKeys).toHaveBeenCalled();
+ });
+ });
+
+ it('hasKeys returns true when there are keys', () => {
+ return mountComponent().then(() => {
+ expect(wrapper.vm.hasKeys).toEqual(3);
+ });
+ });
+});
diff --git a/spec/frontend/deploy_keys/components/key_spec.js b/spec/frontend/deploy_keys/components/key_spec.js
new file mode 100644
index 00000000000..7d942d969bb
--- /dev/null
+++ b/spec/frontend/deploy_keys/components/key_spec.js
@@ -0,0 +1,161 @@
+import { mount } from '@vue/test-utils';
+import DeployKeysStore from '~/deploy_keys/store';
+import key from '~/deploy_keys/components/key.vue';
+import { getTimeago } from '~/lib/utils/datetime_utility';
+
+describe('Deploy keys key', () => {
+ let wrapper;
+ let store;
+
+ const data = getJSONFixture('deploy_keys/keys.json');
+
+ const findTextAndTrim = selector =>
+ wrapper
+ .find(selector)
+ .text()
+ .trim();
+
+ const createComponent = propsData => {
+ wrapper = mount(key, {
+ propsData: {
+ store,
+ endpoint: 'https://test.host/dummy/endpoint',
+ ...propsData,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ store = new DeployKeysStore();
+ store.keys = data;
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('enabled key', () => {
+ const deployKey = data.enabled_keys[0];
+
+ it('renders the keys title', () => {
+ createComponent({ deployKey });
+
+ expect(findTextAndTrim('.title')).toContain('My title');
+ });
+
+ it('renders human friendly formatted created date', () => {
+ createComponent({ deployKey });
+
+ expect(findTextAndTrim('.key-created-at')).toBe(
+ `${getTimeago().format(deployKey.created_at)}`,
+ );
+ });
+
+ it('shows pencil button for editing', () => {
+ createComponent({ deployKey });
+
+ expect(wrapper.find('.btn .ic-pencil')).toExist();
+ });
+
+ it('shows disable button when the project is not deletable', () => {
+ createComponent({ deployKey });
+
+ expect(wrapper.find('.btn .ic-cancel')).toExist();
+ });
+
+ it('shows remove button when the project is deletable', () => {
+ createComponent({
+ deployKey: { ...deployKey, destroyed_when_orphaned: true, almost_orphaned: true },
+ });
+ expect(wrapper.find('.btn .ic-remove')).toExist();
+ });
+ });
+
+ describe('deploy key labels', () => {
+ const deployKey = data.enabled_keys[0];
+ const deployKeysProjects = [...deployKey.deploy_keys_projects];
+ it('shows write access title when key has write access', () => {
+ deployKeysProjects[0] = { ...deployKeysProjects[0], can_push: true };
+ createComponent({ deployKey: { ...deployKey, deploy_keys_projects: deployKeysProjects } });
+
+ expect(wrapper.find('.deploy-project-label').attributes('data-original-title')).toBe(
+ 'Write access allowed',
+ );
+ });
+
+ it('does not show write access title when key has write access', () => {
+ deployKeysProjects[0] = { ...deployKeysProjects[0], can_push: false };
+ createComponent({ deployKey: { ...deployKey, deploy_keys_projects: deployKeysProjects } });
+
+ expect(wrapper.find('.deploy-project-label').attributes('data-original-title')).toBe(
+ 'Read access only',
+ );
+ });
+
+ it('shows expandable button if more than two projects', () => {
+ createComponent({ deployKey });
+ const labels = wrapper.findAll('.deploy-project-label');
+
+ expect(labels.length).toBe(2);
+ expect(labels.at(1).text()).toContain('others');
+ expect(labels.at(1).attributes('data-original-title')).toContain('Expand');
+ });
+
+ it('expands all project labels after click', () => {
+ createComponent({ deployKey });
+ const { length } = deployKey.deploy_keys_projects;
+ wrapper
+ .findAll('.deploy-project-label')
+ .at(1)
+ .trigger('click');
+
+ return wrapper.vm.$nextTick().then(() => {
+ const labels = wrapper.findAll('.deploy-project-label');
+
+ expect(labels.length).toBe(length);
+ expect(labels.at(1).text()).not.toContain(`+${length} others`);
+ expect(labels.at(1).attributes('data-original-title')).not.toContain('Expand');
+ });
+ });
+
+ it('shows two projects', () => {
+ createComponent({
+ deployKey: { ...deployKey, deploy_keys_projects: [...deployKeysProjects].slice(0, 2) },
+ });
+
+ const labels = wrapper.findAll('.deploy-project-label');
+
+ expect(labels.length).toBe(2);
+ expect(labels.at(1).text()).toContain(deployKey.deploy_keys_projects[1].project.full_name);
+ });
+ });
+
+ describe('public keys', () => {
+ const deployKey = data.public_keys[0];
+
+ it('renders deploy keys without any enabled projects', () => {
+ createComponent({ deployKey: { ...deployKey, deploy_keys_projects: [] } });
+
+ expect(findTextAndTrim('.deploy-project-list')).toBe('None');
+ });
+
+ it('shows enable button', () => {
+ createComponent({ deployKey });
+ expect(findTextAndTrim('.btn')).toBe('Enable');
+ });
+
+ it('shows pencil button for editing', () => {
+ createComponent({ deployKey });
+ expect(wrapper.find('.btn .ic-pencil')).toExist();
+ });
+
+ it('shows disable button when key is enabled', () => {
+ store.keys.enabled_keys.push(deployKey);
+
+ createComponent({ deployKey });
+
+ expect(wrapper.find('.btn .ic-cancel')).toExist();
+ });
+ });
+});
diff --git a/spec/frontend/deploy_keys/components/keys_panel_spec.js b/spec/frontend/deploy_keys/components/keys_panel_spec.js
new file mode 100644
index 00000000000..53c8ba073bc
--- /dev/null
+++ b/spec/frontend/deploy_keys/components/keys_panel_spec.js
@@ -0,0 +1,63 @@
+import { mount } from '@vue/test-utils';
+import DeployKeysStore from '~/deploy_keys/store';
+import deployKeysPanel from '~/deploy_keys/components/keys_panel.vue';
+
+describe('Deploy keys panel', () => {
+ const data = getJSONFixture('deploy_keys/keys.json');
+ let wrapper;
+
+ const findTableRowHeader = () => wrapper.find('.table-row-header');
+
+ const mountComponent = props => {
+ const store = new DeployKeysStore();
+ store.keys = data;
+ wrapper = mount(deployKeysPanel, {
+ propsData: {
+ title: 'test',
+ keys: data.enabled_keys,
+ showHelpBox: true,
+ store,
+ endpoint: 'https://test.host/dummy/endpoint',
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('renders list of keys', () => {
+ mountComponent();
+ expect(wrapper.findAll('.deploy-key').length).toBe(wrapper.vm.keys.length);
+ });
+
+ it('renders table header', () => {
+ mountComponent();
+ const tableHeader = findTableRowHeader();
+
+ expect(tableHeader).toExist();
+ expect(tableHeader.text()).toContain('Deploy key');
+ expect(tableHeader.text()).toContain('Project usage');
+ expect(tableHeader.text()).toContain('Created');
+ });
+
+ it('renders help box if keys are empty', () => {
+ mountComponent({ keys: [] });
+
+ expect(wrapper.find('.settings-message').exists()).toBe(true);
+
+ expect(
+ wrapper
+ .find('.settings-message')
+ .text()
+ .trim(),
+ ).toBe('No deploy keys found. Create one with the form above.');
+ });
+
+ it('renders no table header if keys are empty', () => {
+ mountComponent({ keys: [] });
+ expect(findTableRowHeader().exists()).toBe(false);
+ });
+});
diff --git a/spec/frontend/dirty_submit/dirty_submit_collection_spec.js b/spec/frontend/dirty_submit/dirty_submit_collection_spec.js
new file mode 100644
index 00000000000..170d581be23
--- /dev/null
+++ b/spec/frontend/dirty_submit/dirty_submit_collection_spec.js
@@ -0,0 +1,22 @@
+import DirtySubmitCollection from '~/dirty_submit/dirty_submit_collection';
+import { setInputValue, createForm } from './helper';
+
+jest.mock('lodash/throttle', () => jest.fn(fn => fn));
+
+describe('DirtySubmitCollection', () => {
+ const testElementsCollection = [createForm(), createForm()];
+ const forms = testElementsCollection.map(testElements => testElements.form);
+
+ new DirtySubmitCollection(forms); // eslint-disable-line no-new
+
+ it.each(testElementsCollection)('disables submits until there are changes', testElements => {
+ const { input, submit } = testElements;
+ const originalValue = input.value;
+
+ expect(submit.disabled).toBe(true);
+ setInputValue(input, `${originalValue} changes`);
+ expect(submit.disabled).toBe(false);
+ setInputValue(input, originalValue);
+ expect(submit.disabled).toBe(true);
+ });
+});
diff --git a/spec/javascripts/dirty_submit/dirty_submit_factory_spec.js b/spec/frontend/dirty_submit/dirty_submit_factory_spec.js
index 40843a68582..40843a68582 100644
--- a/spec/javascripts/dirty_submit/dirty_submit_factory_spec.js
+++ b/spec/frontend/dirty_submit/dirty_submit_factory_spec.js
diff --git a/spec/javascripts/dirty_submit/dirty_submit_form_spec.js b/spec/frontend/dirty_submit/dirty_submit_form_spec.js
index 42f806fa1bf..d7f690df1f3 100644
--- a/spec/javascripts/dirty_submit/dirty_submit_form_spec.js
+++ b/spec/frontend/dirty_submit/dirty_submit_form_spec.js
@@ -1,95 +1,78 @@
-import { range as rge } from 'lodash';
+import { range as rge, throttle } from 'lodash';
import DirtySubmitForm from '~/dirty_submit/dirty_submit_form';
import { getInputValue, setInputValue, createForm } from './helper';
+jest.mock('lodash/throttle', () => jest.fn(fn => fn));
+const lodash = jest.requireActual('lodash');
+
function expectToToggleDisableOnDirtyUpdate(submit, input) {
const originalValue = getInputValue(input);
expect(submit.disabled).toBe(true);
- return setInputValue(input, `${originalValue} changes`)
- .then(() => expect(submit.disabled).toBe(false))
- .then(() => setInputValue(input, originalValue))
- .then(() => expect(submit.disabled).toBe(true));
+ setInputValue(input, `${originalValue} changes`);
+ expect(submit.disabled).toBe(false);
+ setInputValue(input, originalValue);
+ expect(submit.disabled).toBe(true);
}
describe('DirtySubmitForm', () => {
- const originalThrottleDuration = DirtySubmitForm.THROTTLE_DURATION;
-
describe('submit button tests', () => {
- beforeEach(() => {
- DirtySubmitForm.THROTTLE_DURATION = 0;
- });
-
- afterEach(() => {
- DirtySubmitForm.THROTTLE_DURATION = originalThrottleDuration;
- });
-
- it('disables submit until there are changes', done => {
+ it('disables submit until there are changes', () => {
const { form, input, submit } = createForm();
new DirtySubmitForm(form); // eslint-disable-line no-new
- return expectToToggleDisableOnDirtyUpdate(submit, input)
- .then(done)
- .catch(done.fail);
+ expectToToggleDisableOnDirtyUpdate(submit, input);
});
- it('disables submit until there are changes when initializing with a falsy value', done => {
+ it('disables submit until there are changes when initializing with a falsy value', () => {
const { form, input, submit } = createForm();
input.value = '';
new DirtySubmitForm(form); // eslint-disable-line no-new
- return expectToToggleDisableOnDirtyUpdate(submit, input)
- .then(done)
- .catch(done.fail);
+ expectToToggleDisableOnDirtyUpdate(submit, input);
});
- it('disables submit until there are changes for radio inputs', done => {
+ it('disables submit until there are changes for radio inputs', () => {
const { form, input, submit } = createForm('radio');
new DirtySubmitForm(form); // eslint-disable-line no-new
- return expectToToggleDisableOnDirtyUpdate(submit, input)
- .then(done)
- .catch(done.fail);
+ expectToToggleDisableOnDirtyUpdate(submit, input);
});
- it('disables submit until there are changes for checkbox inputs', done => {
+ it('disables submit until there are changes for checkbox inputs', () => {
const { form, input, submit } = createForm('checkbox');
new DirtySubmitForm(form); // eslint-disable-line no-new
- return expectToToggleDisableOnDirtyUpdate(submit, input)
- .then(done)
- .catch(done.fail);
+ expectToToggleDisableOnDirtyUpdate(submit, input);
});
});
describe('throttling tests', () => {
beforeEach(() => {
- jasmine.clock().install();
- jasmine.clock().mockDate();
- DirtySubmitForm.THROTTLE_DURATION = 100;
+ throttle.mockImplementation(lodash.throttle);
+ jest.useFakeTimers();
});
afterEach(() => {
- jasmine.clock().uninstall();
- DirtySubmitForm.THROTTLE_DURATION = originalThrottleDuration;
+ throttle.mockReset();
});
it('throttles updates when rapid changes are made to a single form element', () => {
const { form, input } = createForm();
- const updateDirtyInputSpy = spyOn(new DirtySubmitForm(form), 'updateDirtyInput');
+ const updateDirtyInputSpy = jest.spyOn(new DirtySubmitForm(form), 'updateDirtyInput');
rge(10).forEach(i => {
setInputValue(input, `change ${i}`, false);
});
- jasmine.clock().tick(101);
+ jest.runOnlyPendingTimers();
- expect(updateDirtyInputSpy).toHaveBeenCalledTimes(2);
+ expect(updateDirtyInputSpy).toHaveBeenCalledTimes(1);
});
it('does not throttle updates when rapid changes are made to different form elements', () => {
@@ -99,14 +82,14 @@ describe('DirtySubmitForm', () => {
form.innerHTML += `<input type="text" name="input-${i}" class="js-input-${i}"/>`;
});
- const updateDirtyInputSpy = spyOn(new DirtySubmitForm(form), 'updateDirtyInput');
+ const updateDirtyInputSpy = jest.spyOn(new DirtySubmitForm(form), 'updateDirtyInput');
range.forEach(i => {
const input = form.querySelector(`.js-input-${i}`);
setInputValue(input, `change`, false);
});
- jasmine.clock().tick(101);
+ jest.runOnlyPendingTimers();
expect(updateDirtyInputSpy).toHaveBeenCalledTimes(range.length);
});
diff --git a/spec/javascripts/dirty_submit/helper.js b/spec/frontend/dirty_submit/helper.js
index b51783cb915..c02512b7671 100644
--- a/spec/javascripts/dirty_submit/helper.js
+++ b/spec/frontend/dirty_submit/helper.js
@@ -1,6 +1,3 @@
-import DirtySubmitForm from '~/dirty_submit/dirty_submit_form';
-import setTimeoutPromiseHelper from '../helpers/set_timeout_promise_helper';
-
function isCheckableType(type) {
return /^(radio|checkbox)$/.test(type);
}
@@ -22,8 +19,6 @@ export function setInputValue(element, value) {
bubbles: true,
}),
);
-
- return setTimeoutPromiseHelper(DirtySubmitForm.THROTTLE_DURATION);
}
export function getInputValue(input) {
diff --git a/spec/frontend/pipelines/mock_data.js b/spec/frontend/pipelines/mock_data.js
new file mode 100644
index 00000000000..f876987cd88
--- /dev/null
+++ b/spec/frontend/pipelines/mock_data.js
@@ -0,0 +1,423 @@
+export const pipelineWithStages = {
+ id: 20333396,
+ user: {
+ id: 128633,
+ name: 'Rémy Coutable',
+ username: 'rymai',
+ state: 'active',
+ avatar_url:
+ 'https://secure.gravatar.com/avatar/263da227929cc0035cb0eba512bcf81a?s=80\u0026d=identicon',
+ web_url: 'https://gitlab.com/rymai',
+ path: '/rymai',
+ },
+ active: true,
+ coverage: '58.24',
+ source: 'push',
+ created_at: '2018-04-11T14:04:53.881Z',
+ updated_at: '2018-04-11T14:05:00.792Z',
+ path: '/gitlab-org/gitlab/pipelines/20333396',
+ flags: {
+ latest: true,
+ stuck: false,
+ auto_devops: false,
+ yaml_errors: false,
+ retryable: false,
+ cancelable: true,
+ failure_reason: false,
+ },
+ details: {
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab/pipelines/20333396',
+ favicon:
+ 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_running-2eb56be2871937954b2ba6d6f4ee9fdf7e5e1c146ac45f7be98119ccaca1aca9.ico',
+ },
+ duration: null,
+ finished_at: null,
+ stages: [
+ {
+ name: 'build',
+ title: 'build: skipped',
+ status: {
+ icon: 'status_skipped',
+ text: 'skipped',
+ label: 'skipped',
+ group: 'skipped',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab/pipelines/20333396#build',
+ favicon:
+ 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_skipped-a2eee568a5bffdb494050c7b62dde241de9189280836288ac8923d369f16222d.ico',
+ },
+ path: '/gitlab-org/gitlab/pipelines/20333396#build',
+ dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=build',
+ },
+ {
+ name: 'prepare',
+ title: 'prepare: passed',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab/pipelines/20333396#prepare',
+ favicon:
+ 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_success-26f59841becbef8c6fe414e9e74471d8bfd6a91b5855c19fe7f5923a40a7da47.ico',
+ },
+ path: '/gitlab-org/gitlab/pipelines/20333396#prepare',
+ dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=prepare',
+ },
+ {
+ name: 'test',
+ title: 'test: running',
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab/pipelines/20333396#test',
+ favicon:
+ 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_running-2eb56be2871937954b2ba6d6f4ee9fdf7e5e1c146ac45f7be98119ccaca1aca9.ico',
+ },
+ path: '/gitlab-org/gitlab/pipelines/20333396#test',
+ dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=test',
+ },
+ {
+ name: 'post-test',
+ title: 'post-test: created',
+ status: {
+ icon: 'status_created',
+ text: 'created',
+ label: 'created',
+ group: 'created',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab/pipelines/20333396#post-test',
+ favicon:
+ 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_created-e997aa0b7db73165df8a9d6803932b18d7b7cc37d604d2d96e378fea2dba9c5f.ico',
+ },
+ path: '/gitlab-org/gitlab/pipelines/20333396#post-test',
+ dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=post-test',
+ },
+ {
+ name: 'pages',
+ title: 'pages: created',
+ status: {
+ icon: 'status_created',
+ text: 'created',
+ label: 'created',
+ group: 'created',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab/pipelines/20333396#pages',
+ favicon:
+ 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_created-e997aa0b7db73165df8a9d6803932b18d7b7cc37d604d2d96e378fea2dba9c5f.ico',
+ },
+ path: '/gitlab-org/gitlab/pipelines/20333396#pages',
+ dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=pages',
+ },
+ {
+ name: 'post-cleanup',
+ title: 'post-cleanup: created',
+ status: {
+ icon: 'status_created',
+ text: 'created',
+ label: 'created',
+ group: 'created',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab/pipelines/20333396#post-cleanup',
+ favicon:
+ 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_created-e997aa0b7db73165df8a9d6803932b18d7b7cc37d604d2d96e378fea2dba9c5f.ico',
+ },
+ path: '/gitlab-org/gitlab/pipelines/20333396#post-cleanup',
+ dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=post-cleanup',
+ },
+ ],
+ artifacts: [
+ {
+ name: 'gitlab:assets:compile',
+ expired: false,
+ expire_at: '2018-05-12T14:22:54.730Z',
+ path: '/gitlab-org/gitlab/-/jobs/62411438/artifacts/download',
+ keep_path: '/gitlab-org/gitlab/-/jobs/62411438/artifacts/keep',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411438/artifacts/browse',
+ },
+ {
+ name: 'rspec-mysql 12 28',
+ expired: false,
+ expire_at: '2018-05-12T14:22:45.136Z',
+ path: '/gitlab-org/gitlab/-/jobs/62411397/artifacts/download',
+ keep_path: '/gitlab-org/gitlab/-/jobs/62411397/artifacts/keep',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411397/artifacts/browse',
+ },
+ {
+ name: 'rspec-mysql 6 28',
+ expired: false,
+ expire_at: '2018-05-12T14:22:41.523Z',
+ path: '/gitlab-org/gitlab/-/jobs/62411391/artifacts/download',
+ keep_path: '/gitlab-org/gitlab/-/jobs/62411391/artifacts/keep',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411391/artifacts/browse',
+ },
+ {
+ name: 'rspec-pg geo 0 1',
+ expired: false,
+ expire_at: '2018-05-12T14:22:13.287Z',
+ path: '/gitlab-org/gitlab/-/jobs/62411353/artifacts/download',
+ keep_path: '/gitlab-org/gitlab/-/jobs/62411353/artifacts/keep',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411353/artifacts/browse',
+ },
+ {
+ name: 'rspec-mysql 0 28',
+ expired: false,
+ expire_at: '2018-05-12T14:22:06.834Z',
+ path: '/gitlab-org/gitlab/-/jobs/62411385/artifacts/download',
+ keep_path: '/gitlab-org/gitlab/-/jobs/62411385/artifacts/keep',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411385/artifacts/browse',
+ },
+ {
+ name: 'spinach-mysql 0 2',
+ expired: false,
+ expire_at: '2018-05-12T14:21:51.409Z',
+ path: '/gitlab-org/gitlab/-/jobs/62411423/artifacts/download',
+ keep_path: '/gitlab-org/gitlab/-/jobs/62411423/artifacts/keep',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411423/artifacts/browse',
+ },
+ {
+ name: 'karma',
+ expired: false,
+ expire_at: '2018-05-12T14:21:20.934Z',
+ path: '/gitlab-org/gitlab/-/jobs/62411440/artifacts/download',
+ keep_path: '/gitlab-org/gitlab/-/jobs/62411440/artifacts/keep',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411440/artifacts/browse',
+ },
+ {
+ name: 'spinach-pg 0 2',
+ expired: false,
+ expire_at: '2018-05-12T14:20:01.028Z',
+ path: '/gitlab-org/gitlab/-/jobs/62411419/artifacts/download',
+ keep_path: '/gitlab-org/gitlab/-/jobs/62411419/artifacts/keep',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411419/artifacts/browse',
+ },
+ {
+ name: 'spinach-pg 1 2',
+ expired: false,
+ expire_at: '2018-05-12T14:19:04.336Z',
+ path: '/gitlab-org/gitlab/-/jobs/62411421/artifacts/download',
+ keep_path: '/gitlab-org/gitlab/-/jobs/62411421/artifacts/keep',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411421/artifacts/browse',
+ },
+ {
+ name: 'sast',
+ expired: null,
+ expire_at: null,
+ path: '/gitlab-org/gitlab/-/jobs/62411442/artifacts/download',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411442/artifacts/browse',
+ },
+ {
+ name: 'code_quality',
+ expired: false,
+ expire_at: '2018-04-18T14:16:24.484Z',
+ path: '/gitlab-org/gitlab/-/jobs/62411441/artifacts/download',
+ keep_path: '/gitlab-org/gitlab/-/jobs/62411441/artifacts/keep',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411441/artifacts/browse',
+ },
+ {
+ name: 'cache gems',
+ expired: null,
+ expire_at: null,
+ path: '/gitlab-org/gitlab/-/jobs/62411447/artifacts/download',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411447/artifacts/browse',
+ },
+ {
+ name: 'dependency_scanning',
+ expired: null,
+ expire_at: null,
+ path: '/gitlab-org/gitlab/-/jobs/62411443/artifacts/download',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411443/artifacts/browse',
+ },
+ {
+ name: 'compile-assets',
+ expired: false,
+ expire_at: '2018-04-18T14:12:07.638Z',
+ path: '/gitlab-org/gitlab/-/jobs/62411334/artifacts/download',
+ keep_path: '/gitlab-org/gitlab/-/jobs/62411334/artifacts/keep',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411334/artifacts/browse',
+ },
+ {
+ name: 'setup-test-env',
+ expired: false,
+ expire_at: '2018-04-18T14:10:27.024Z',
+ path: '/gitlab-org/gitlab/-/jobs/62411336/artifacts/download',
+ keep_path: '/gitlab-org/gitlab/-/jobs/62411336/artifacts/keep',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411336/artifacts/browse',
+ },
+ {
+ name: 'retrieve-tests-metadata',
+ expired: false,
+ expire_at: '2018-05-12T14:06:35.926Z',
+ path: '/gitlab-org/gitlab/-/jobs/62411333/artifacts/download',
+ keep_path: '/gitlab-org/gitlab/-/jobs/62411333/artifacts/keep',
+ browse_path: '/gitlab-org/gitlab/-/jobs/62411333/artifacts/browse',
+ },
+ ],
+ manual_actions: [
+ {
+ name: 'package-and-qa',
+ path: '/gitlab-org/gitlab/-/jobs/62411330/play',
+ playable: true,
+ },
+ {
+ name: 'review-docs-deploy',
+ path: '/gitlab-org/gitlab/-/jobs/62411332/play',
+ playable: true,
+ },
+ ],
+ },
+ ref: {
+ name: 'master',
+ path: '/gitlab-org/gitlab/commits/master',
+ tag: false,
+ branch: true,
+ },
+ commit: {
+ id: 'e6a2885c503825792cb8a84a8731295e361bd059',
+ short_id: 'e6a2885c',
+ title: "Merge branch 'ce-to-ee-2018-04-11' into 'master'",
+ created_at: '2018-04-11T14:04:39.000Z',
+ parent_ids: [
+ '5d9b5118f6055f72cff1a82b88133609912f2c1d',
+ '6fdc6ee76a8062fe41b1a33f7c503334a6ebdc02',
+ ],
+ message:
+ "Merge branch 'ce-to-ee-2018-04-11' into 'master'\n\nCE upstream - 2018-04-11 12:26 UTC\n\nSee merge request gitlab-org/gitlab-ee!5326",
+ author_name: 'Rémy Coutable',
+ author_email: 'remy@rymai.me',
+ authored_date: '2018-04-11T14:04:39.000Z',
+ committer_name: 'Rémy Coutable',
+ committer_email: 'remy@rymai.me',
+ committed_date: '2018-04-11T14:04:39.000Z',
+ author: {
+ id: 128633,
+ name: 'Rémy Coutable',
+ username: 'rymai',
+ state: 'active',
+ avatar_url:
+ 'https://secure.gravatar.com/avatar/263da227929cc0035cb0eba512bcf81a?s=80\u0026d=identicon',
+ web_url: 'https://gitlab.com/rymai',
+ path: '/rymai',
+ },
+ author_gravatar_url:
+ 'https://secure.gravatar.com/avatar/263da227929cc0035cb0eba512bcf81a?s=80\u0026d=identicon',
+ commit_url:
+ 'https://gitlab.com/gitlab-org/gitlab/commit/e6a2885c503825792cb8a84a8731295e361bd059',
+ commit_path: '/gitlab-org/gitlab/commit/e6a2885c503825792cb8a84a8731295e361bd059',
+ },
+ cancel_path: '/gitlab-org/gitlab/pipelines/20333396/cancel',
+ triggered_by: null,
+ triggered: [],
+};
+
+export const stageReply = {
+ name: 'deploy',
+ title: 'deploy: running',
+ latest_statuses: [
+ {
+ id: 928,
+ name: 'stop staging',
+ started: false,
+ build_path: '/twitter/flight/-/jobs/928',
+ cancel_path: '/twitter/flight/-/jobs/928/cancel',
+ playable: false,
+ created_at: '2018-04-04T20:02:02.728Z',
+ updated_at: '2018-04-04T20:02:02.766Z',
+ status: {
+ icon: 'status_pending',
+ text: 'pending',
+ label: 'pending',
+ group: 'pending',
+ tooltip: 'pending',
+ has_details: true,
+ details_path: '/twitter/flight/-/jobs/928',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_pending-db32e1faf94b9f89530ac519790920d1f18ea8f6af6cd2e0a26cd6840cacf101.ico',
+ action: {
+ icon: 'cancel',
+ title: 'Cancel',
+ path: '/twitter/flight/-/jobs/928/cancel',
+ method: 'post',
+ },
+ },
+ },
+ {
+ id: 926,
+ name: 'production',
+ started: false,
+ build_path: '/twitter/flight/-/jobs/926',
+ retry_path: '/twitter/flight/-/jobs/926/retry',
+ play_path: '/twitter/flight/-/jobs/926/play',
+ playable: true,
+ created_at: '2018-04-04T20:00:57.202Z',
+ updated_at: '2018-04-04T20:11:13.110Z',
+ status: {
+ icon: 'status_canceled',
+ text: 'canceled',
+ label: 'manual play action',
+ group: 'canceled',
+ tooltip: 'canceled',
+ has_details: true,
+ details_path: '/twitter/flight/-/jobs/926',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_canceled-5491840b9b6feafba0bc599cbd49ee9580321dc809683856cf1b0d51532b1af6.ico',
+ action: {
+ icon: 'play',
+ title: 'Play',
+ path: '/twitter/flight/-/jobs/926/play',
+ method: 'post',
+ },
+ },
+ },
+ {
+ id: 217,
+ name: 'staging',
+ started: '2018-03-07T08:41:46.234Z',
+ build_path: '/twitter/flight/-/jobs/217',
+ retry_path: '/twitter/flight/-/jobs/217/retry',
+ playable: false,
+ created_at: '2018-03-07T14:41:58.093Z',
+ updated_at: '2018-03-07T14:41:58.093Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/twitter/flight/-/jobs/217',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/twitter/flight/-/jobs/217/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ tooltip: 'running',
+ has_details: true,
+ details_path: '/twitter/flight/pipelines/13#deploy',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
+ },
+ path: '/twitter/flight/pipelines/13#deploy',
+ dropdown_path: '/twitter/flight/pipelines/13/stage.json?stage=deploy',
+};
diff --git a/spec/frontend/pipelines/pipelines_spec.js b/spec/frontend/pipelines/pipelines_spec.js
new file mode 100644
index 00000000000..40cd0ad9047
--- /dev/null
+++ b/spec/frontend/pipelines/pipelines_spec.js
@@ -0,0 +1,659 @@
+import { mount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import waitForPromises from 'helpers/wait_for_promises';
+import PipelinesComponent from '~/pipelines/components/pipelines.vue';
+import Store from '~/pipelines/stores/pipelines_store';
+import { pipelineWithStages, stageReply } from './mock_data';
+
+describe('Pipelines', () => {
+ const jsonFixtureName = 'pipelines/pipelines.json';
+
+ preloadFixtures(jsonFixtureName);
+
+ let pipelines;
+ let wrapper;
+ let mock;
+
+ const paths = {
+ endpoint: 'twitter/flight/pipelines.json',
+ autoDevopsPath: '/help/topics/autodevops/index.md',
+ helpPagePath: '/help/ci/quick_start/README',
+ emptyStateSvgPath: '/assets/illustrations/pipelines_empty.svg',
+ errorStateSvgPath: '/assets/illustrations/pipelines_failed.svg',
+ noPipelinesSvgPath: '/assets/illustrations/pipelines_pending.svg',
+ ciLintPath: '/ci/lint',
+ resetCachePath: '/twitter/flight/settings/ci_cd/reset_cache',
+ newPipelinePath: '/twitter/flight/pipelines/new',
+ };
+
+ const noPermissions = {
+ endpoint: 'twitter/flight/pipelines.json',
+ autoDevopsPath: '/help/topics/autodevops/index.md',
+ helpPagePath: '/help/ci/quick_start/README',
+ emptyStateSvgPath: '/assets/illustrations/pipelines_empty.svg',
+ errorStateSvgPath: '/assets/illustrations/pipelines_failed.svg',
+ noPipelinesSvgPath: '/assets/illustrations/pipelines_pending.svg',
+ };
+
+ const defaultProps = {
+ hasGitlabCi: true,
+ canCreatePipeline: true,
+ ...paths,
+ };
+
+ const createComponent = (props = defaultProps, methods) => {
+ wrapper = mount(PipelinesComponent, {
+ propsData: {
+ store: new Store(),
+ ...props,
+ },
+ methods: {
+ ...methods,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ pipelines = getJSONFixture(jsonFixtureName);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ mock.restore();
+ });
+
+ describe('With permission', () => {
+ describe('With pipelines in main tab', () => {
+ beforeEach(() => {
+ mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
+ createComponent();
+ return waitForPromises();
+ });
+
+ it('renders tabs', () => {
+ expect(wrapper.find('.js-pipelines-tab-all').text()).toContain('All');
+ });
+
+ it('renders Run Pipeline link', () => {
+ expect(wrapper.find('.js-run-pipeline').attributes('href')).toBe(paths.newPipelinePath);
+ });
+
+ it('renders CI Lint link', () => {
+ expect(wrapper.find('.js-ci-lint').attributes('href')).toBe(paths.ciLintPath);
+ });
+
+ it('renders Clear Runner Cache button', () => {
+ expect(wrapper.find('.js-clear-cache').text()).toBe('Clear Runner Caches');
+ });
+
+ it('renders pipelines table', () => {
+ expect(wrapper.findAll('.gl-responsive-table-row')).toHaveLength(
+ pipelines.pipelines.length + 1,
+ );
+ });
+ });
+
+ describe('Without pipelines on main tab with CI', () => {
+ beforeEach(() => {
+ mock.onGet('twitter/flight/pipelines.json').reply(200, {
+ pipelines: [],
+ count: {
+ all: 0,
+ pending: 0,
+ running: 0,
+ finished: 0,
+ },
+ });
+
+ createComponent();
+
+ return waitForPromises();
+ });
+
+ it('renders tabs', () => {
+ expect(wrapper.find('.js-pipelines-tab-all').text()).toContain('All');
+ });
+
+ it('renders Run Pipeline link', () => {
+ expect(wrapper.find('.js-run-pipeline').attributes('href')).toEqual(paths.newPipelinePath);
+ });
+
+ it('renders CI Lint link', () => {
+ expect(wrapper.find('.js-ci-lint').attributes('href')).toEqual(paths.ciLintPath);
+ });
+
+ it('renders Clear Runner Cache button', () => {
+ expect(wrapper.find('.js-clear-cache').text()).toEqual('Clear Runner Caches');
+ });
+
+ it('renders tab empty state', () => {
+ expect(wrapper.find('.empty-state h4').text()).toEqual('There are currently no pipelines.');
+ });
+ });
+
+ describe('Without pipelines nor CI', () => {
+ beforeEach(() => {
+ mock.onGet('twitter/flight/pipelines.json').reply(200, {
+ pipelines: [],
+ count: {
+ all: 0,
+ pending: 0,
+ running: 0,
+ finished: 0,
+ },
+ });
+
+ createComponent({ hasGitlabCi: false, canCreatePipeline: true, ...paths });
+
+ return waitForPromises();
+ });
+
+ it('renders empty state', () => {
+ expect(wrapper.find('.js-empty-state h4').text()).toEqual('Build with confidence');
+
+ expect(wrapper.find('.js-get-started-pipelines').attributes('href')).toEqual(
+ paths.helpPagePath,
+ );
+ });
+
+ it('does not render tabs nor buttons', () => {
+ expect(wrapper.find('.js-pipelines-tab-all').exists()).toBeFalsy();
+ expect(wrapper.find('.js-run-pipeline').exists()).toBeFalsy();
+ expect(wrapper.find('.js-ci-lint').exists()).toBeFalsy();
+ expect(wrapper.find('.js-clear-cache').exists()).toBeFalsy();
+ });
+ });
+
+ describe('When API returns error', () => {
+ beforeEach(() => {
+ mock.onGet('twitter/flight/pipelines.json').reply(500, {});
+ createComponent({ hasGitlabCi: false, canCreatePipeline: true, ...paths });
+
+ return waitForPromises();
+ });
+
+ it('renders tabs', () => {
+ expect(wrapper.find('.js-pipelines-tab-all').text()).toContain('All');
+ });
+
+ it('renders buttons', () => {
+ expect(wrapper.find('.js-run-pipeline').attributes('href')).toEqual(paths.newPipelinePath);
+
+ expect(wrapper.find('.js-ci-lint').attributes('href')).toEqual(paths.ciLintPath);
+ expect(wrapper.find('.js-clear-cache').text()).toEqual('Clear Runner Caches');
+ });
+
+ it('renders error state', () => {
+ expect(wrapper.find('.empty-state').text()).toContain(
+ 'There was an error fetching the pipelines.',
+ );
+ });
+ });
+ });
+
+ describe('Without permission', () => {
+ describe('With pipelines in main tab', () => {
+ beforeEach(() => {
+ mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
+
+ createComponent({ hasGitlabCi: false, canCreatePipeline: false, ...noPermissions });
+
+ return waitForPromises();
+ });
+
+ it('renders tabs', () => {
+ expect(wrapper.find('.js-pipelines-tab-all').text()).toContain('All');
+ });
+
+ it('does not render buttons', () => {
+ expect(wrapper.find('.js-run-pipeline').exists()).toBeFalsy();
+ expect(wrapper.find('.js-ci-lint').exists()).toBeFalsy();
+ expect(wrapper.find('.js-clear-cache').exists()).toBeFalsy();
+ });
+
+ it('renders pipelines table', () => {
+ expect(wrapper.findAll('.gl-responsive-table-row')).toHaveLength(
+ pipelines.pipelines.length + 1,
+ );
+ });
+ });
+
+ describe('Without pipelines on main tab with CI', () => {
+ beforeEach(() => {
+ mock.onGet('twitter/flight/pipelines.json').reply(200, {
+ pipelines: [],
+ count: {
+ all: 0,
+ pending: 0,
+ running: 0,
+ finished: 0,
+ },
+ });
+
+ createComponent({ hasGitlabCi: true, canCreatePipeline: false, ...noPermissions });
+
+ return waitForPromises();
+ });
+
+ it('renders tabs', () => {
+ expect(wrapper.find('.js-pipelines-tab-all').text()).toContain('All');
+ });
+
+ it('does not render buttons', () => {
+ expect(wrapper.find('.js-run-pipeline').exists()).toBeFalsy();
+ expect(wrapper.find('.js-ci-lint').exists()).toBeFalsy();
+ expect(wrapper.find('.js-clear-cache').exists()).toBeFalsy();
+ });
+
+ it('renders tab empty state', () => {
+ expect(wrapper.find('.empty-state h4').text()).toEqual('There are currently no pipelines.');
+ });
+ });
+
+ describe('Without pipelines nor CI', () => {
+ beforeEach(() => {
+ mock.onGet('twitter/flight/pipelines.json').reply(200, {
+ pipelines: [],
+ count: {
+ all: 0,
+ pending: 0,
+ running: 0,
+ finished: 0,
+ },
+ });
+
+ createComponent({ hasGitlabCi: false, canCreatePipeline: false, ...noPermissions });
+
+ return waitForPromises();
+ });
+
+ it('renders empty state without button to set CI', () => {
+ expect(wrapper.find('.js-empty-state').text()).toEqual(
+ 'This project is not currently set up to run pipelines.',
+ );
+
+ expect(wrapper.find('.js-get-started-pipelines').exists()).toBeFalsy();
+ });
+
+ it('does not render tabs or buttons', () => {
+ expect(wrapper.find('.js-pipelines-tab-all').exists()).toBeFalsy();
+ expect(wrapper.find('.js-run-pipeline').exists()).toBeFalsy();
+ expect(wrapper.find('.js-ci-lint').exists()).toBeFalsy();
+ expect(wrapper.find('.js-clear-cache').exists()).toBeFalsy();
+ });
+ });
+
+ describe('When API returns error', () => {
+ beforeEach(() => {
+ mock.onGet('twitter/flight/pipelines.json').reply(500, {});
+
+ createComponent({ hasGitlabCi: false, canCreatePipeline: true, ...noPermissions });
+
+ return waitForPromises();
+ });
+
+ it('renders tabs', () => {
+ expect(wrapper.find('.js-pipelines-tab-all').text()).toContain('All');
+ });
+
+ it('does not renders buttons', () => {
+ expect(wrapper.find('.js-run-pipeline').exists()).toBeFalsy();
+ expect(wrapper.find('.js-ci-lint').exists()).toBeFalsy();
+ expect(wrapper.find('.js-clear-cache').exists()).toBeFalsy();
+ });
+
+ it('renders error state', () => {
+ expect(wrapper.find('.empty-state').text()).toContain(
+ 'There was an error fetching the pipelines.',
+ );
+ });
+ });
+ });
+
+ describe('successful request', () => {
+ describe('with pipelines', () => {
+ beforeEach(() => {
+ mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
+
+ createComponent();
+ return waitForPromises();
+ });
+
+ it('should render table', () => {
+ expect(wrapper.find('.table-holder').exists()).toBe(true);
+ expect(wrapper.findAll('.gl-responsive-table-row')).toHaveLength(
+ pipelines.pipelines.length + 1,
+ );
+ });
+
+ it('should render navigation tabs', () => {
+ expect(wrapper.find('.js-pipelines-tab-pending').text()).toContain('Pending');
+
+ expect(wrapper.find('.js-pipelines-tab-all').text()).toContain('All');
+
+ expect(wrapper.find('.js-pipelines-tab-running').text()).toContain('Running');
+
+ expect(wrapper.find('.js-pipelines-tab-finished').text()).toContain('Finished');
+
+ expect(wrapper.find('.js-pipelines-tab-branches').text()).toContain('Branches');
+
+ expect(wrapper.find('.js-pipelines-tab-tags').text()).toContain('Tags');
+ });
+
+ it('should make an API request when using tabs', () => {
+ const updateContentMock = jest.fn(() => {});
+ createComponent(
+ { hasGitlabCi: true, canCreatePipeline: true, ...paths },
+ {
+ updateContent: updateContentMock,
+ },
+ );
+
+ return waitForPromises().then(() => {
+ wrapper.find('.js-pipelines-tab-finished').trigger('click');
+
+ expect(updateContentMock).toHaveBeenCalledWith({ scope: 'finished', page: '1' });
+ });
+ });
+
+ describe('with pagination', () => {
+ it('should make an API request when using pagination', () => {
+ const updateContentMock = jest.fn(() => {});
+ createComponent(
+ { hasGitlabCi: true, canCreatePipeline: true, ...paths },
+ {
+ updateContent: updateContentMock,
+ },
+ );
+
+ return waitForPromises()
+ .then(() => {
+ // Mock pagination
+ wrapper.vm.store.state.pageInfo = {
+ page: 1,
+ total: 10,
+ perPage: 2,
+ nextPage: 2,
+ totalPages: 5,
+ };
+
+ return wrapper.vm.$nextTick();
+ })
+ .then(() => {
+ wrapper.find('.next-page-item').trigger('click');
+
+ expect(updateContentMock).toHaveBeenCalledWith({ scope: 'all', page: '2' });
+ });
+ });
+ });
+ });
+ });
+
+ describe('methods', () => {
+ beforeEach(() => {
+ jest.spyOn(window.history, 'pushState').mockImplementation(() => null);
+ });
+
+ describe('onChangeTab', () => {
+ it('should set page to 1', () => {
+ const updateContentMock = jest.fn(() => {});
+ createComponent(
+ { hasGitlabCi: true, canCreatePipeline: true, ...paths },
+ {
+ updateContent: updateContentMock,
+ },
+ );
+
+ wrapper.vm.onChangeTab('running');
+
+ expect(updateContentMock).toHaveBeenCalledWith({ scope: 'running', page: '1' });
+ });
+ });
+
+ describe('onChangePage', () => {
+ it('should update page and keep scope', () => {
+ const updateContentMock = jest.fn(() => {});
+ createComponent(
+ { hasGitlabCi: true, canCreatePipeline: true, ...paths },
+ {
+ updateContent: updateContentMock,
+ },
+ );
+
+ wrapper.vm.onChangePage(4);
+
+ expect(updateContentMock).toHaveBeenCalledWith({ scope: wrapper.vm.scope, page: '4' });
+ });
+ });
+ });
+
+ describe('computed properties', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ describe('tabs', () => {
+ it('returns default tabs', () => {
+ expect(wrapper.vm.tabs).toEqual([
+ { name: 'All', scope: 'all', count: undefined, isActive: true },
+ { name: 'Pending', scope: 'pending', count: undefined, isActive: false },
+ { name: 'Running', scope: 'running', count: undefined, isActive: false },
+ { name: 'Finished', scope: 'finished', count: undefined, isActive: false },
+ { name: 'Branches', scope: 'branches', isActive: false },
+ { name: 'Tags', scope: 'tags', isActive: false },
+ ]);
+ });
+ });
+
+ describe('emptyTabMessage', () => {
+ it('returns message with scope', () => {
+ wrapper.vm.scope = 'pending';
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.emptyTabMessage).toEqual('There are currently no pending pipelines.');
+ });
+ });
+
+ it('returns message without scope when scope is `all`', () => {
+ expect(wrapper.vm.emptyTabMessage).toEqual('There are currently no pipelines.');
+ });
+ });
+
+ describe('stateToRender', () => {
+ it('returns loading state when the app is loading', () => {
+ expect(wrapper.vm.stateToRender).toEqual('loading');
+ });
+
+ it('returns error state when app has error', () => {
+ wrapper.vm.hasError = true;
+ wrapper.vm.isLoading = false;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.stateToRender).toEqual('error');
+ });
+ });
+
+ it('returns table list when app has pipelines', () => {
+ wrapper.vm.isLoading = false;
+ wrapper.vm.hasError = false;
+ wrapper.vm.state.pipelines = pipelines.pipelines;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.stateToRender).toEqual('tableList');
+ });
+ });
+
+ it('returns empty tab when app does not have pipelines but project has pipelines', () => {
+ wrapper.vm.state.count.all = 10;
+ wrapper.vm.isLoading = false;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.stateToRender).toEqual('emptyTab');
+ });
+ });
+
+ it('returns empty tab when project has CI', () => {
+ wrapper.vm.isLoading = false;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.stateToRender).toEqual('emptyTab');
+ });
+ });
+
+ it('returns empty state when project does not have pipelines nor CI', () => {
+ createComponent({ hasGitlabCi: false, canCreatePipeline: true, ...paths });
+
+ wrapper.vm.isLoading = false;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.stateToRender).toEqual('emptyState');
+ });
+ });
+ });
+
+ describe('shouldRenderTabs', () => {
+ it('returns true when state is loading & has already made the first request', () => {
+ wrapper.vm.isLoading = true;
+ wrapper.vm.hasMadeRequest = true;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.shouldRenderTabs).toEqual(true);
+ });
+ });
+
+ it('returns true when state is tableList & has already made the first request', () => {
+ wrapper.vm.isLoading = false;
+ wrapper.vm.state.pipelines = pipelines.pipelines;
+ wrapper.vm.hasMadeRequest = true;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.shouldRenderTabs).toEqual(true);
+ });
+ });
+
+ it('returns true when state is error & has already made the first request', () => {
+ wrapper.vm.isLoading = false;
+ wrapper.vm.hasError = true;
+ wrapper.vm.hasMadeRequest = true;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.shouldRenderTabs).toEqual(true);
+ });
+ });
+
+ it('returns true when state is empty tab & has already made the first request', () => {
+ wrapper.vm.isLoading = false;
+ wrapper.vm.state.count.all = 10;
+ wrapper.vm.hasMadeRequest = true;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.shouldRenderTabs).toEqual(true);
+ });
+ });
+
+ it('returns false when has not made first request', () => {
+ wrapper.vm.hasMadeRequest = false;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.shouldRenderTabs).toEqual(false);
+ });
+ });
+
+ it('returns false when state is empty state', () => {
+ createComponent({ hasGitlabCi: false, canCreatePipeline: true, ...paths });
+
+ wrapper.vm.isLoading = false;
+ wrapper.vm.hasMadeRequest = true;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.shouldRenderTabs).toEqual(false);
+ });
+ });
+ });
+
+ describe('shouldRenderButtons', () => {
+ it('returns true when it has paths & has made the first request', () => {
+ wrapper.vm.hasMadeRequest = true;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.shouldRenderButtons).toEqual(true);
+ });
+ });
+
+ it('returns false when it has not made the first request', () => {
+ wrapper.vm.hasMadeRequest = false;
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.shouldRenderButtons).toEqual(false);
+ });
+ });
+ });
+ });
+
+ describe('updates results when a staged is clicked', () => {
+ beforeEach(() => {
+ const copyPipeline = Object.assign({}, pipelineWithStages);
+ copyPipeline.id += 1;
+ mock
+ .onGet('twitter/flight/pipelines.json')
+ .reply(
+ 200,
+ {
+ pipelines: [pipelineWithStages],
+ count: {
+ all: 1,
+ finished: 1,
+ pending: 0,
+ running: 0,
+ },
+ },
+ {
+ 'POLL-INTERVAL': 100,
+ },
+ )
+ .onGet(pipelineWithStages.details.stages[0].dropdown_path)
+ .reply(200, stageReply);
+
+ createComponent();
+ });
+
+ describe('when a request is being made', () => {
+ it('stops polling, cancels the request, & restarts polling', () => {
+ const stopMock = jest.spyOn(wrapper.vm.poll, 'stop');
+ const restartMock = jest.spyOn(wrapper.vm.poll, 'restart');
+ const cancelMock = jest.spyOn(wrapper.vm.service.cancelationSource, 'cancel');
+ mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
+
+ return waitForPromises()
+ .then(() => {
+ wrapper.vm.isMakingRequest = true;
+ wrapper.find('.js-builds-dropdown-button').trigger('click');
+ })
+ .then(() => {
+ expect(cancelMock).toHaveBeenCalled();
+ expect(stopMock).toHaveBeenCalled();
+ expect(restartMock).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('when no request is being made', () => {
+ it('stops polling & restarts polling', () => {
+ const stopMock = jest.spyOn(wrapper.vm.poll, 'stop');
+ const restartMock = jest.spyOn(wrapper.vm.poll, 'restart');
+ mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
+
+ return waitForPromises()
+ .then(() => {
+ wrapper.find('.js-builds-dropdown-button').trigger('click');
+ expect(stopMock).toHaveBeenCalled();
+ })
+ .then(() => {
+ expect(restartMock).toHaveBeenCalled();
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/users_select/utils_spec.js b/spec/frontend/users_select/utils_spec.js
deleted file mode 100644
index a09935d8a04..00000000000
--- a/spec/frontend/users_select/utils_spec.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import $ from 'jquery';
-import { getAjaxUsersSelectOptions, getAjaxUsersSelectParams } from '~/users_select/utils';
-
-const options = {
- fooBar: 'baz',
- activeUserId: 1,
-};
-
-describe('getAjaxUsersSelectOptions', () => {
- it('returns options built from select data attributes', () => {
- const $select = $('<select />', { 'data-foo-bar': 'baz', 'data-user-id': 1 });
-
- expect(
- getAjaxUsersSelectOptions($select, { fooBar: 'fooBar', activeUserId: 'user-id' }),
- ).toEqual(options);
- });
-});
-
-describe('getAjaxUsersSelectParams', () => {
- it('returns query parameters built from provided options', () => {
- expect(
- getAjaxUsersSelectParams(options, {
- foo_bar: 'fooBar',
- active_user_id: 'activeUserId',
- non_existent_key: 'nonExistentKey',
- }),
- ).toEqual({
- foo_bar: 'baz',
- active_user_id: 1,
- non_existent_key: null,
- });
- });
-});
diff --git a/spec/graphql/types/jira_import_type_spec.rb b/spec/graphql/types/jira_import_type_spec.rb
index 8448a120682..e73568c9710 100644
--- a/spec/graphql/types/jira_import_type_spec.rb
+++ b/spec/graphql/types/jira_import_type_spec.rb
@@ -6,6 +6,6 @@ describe GitlabSchema.types['JiraImport'] do
it { expect(described_class.graphql_name).to eq('JiraImport') }
it 'has the expected fields' do
- expect(described_class).to have_graphql_fields(:jira_project_key, :scheduled_at, :scheduled_by)
+ expect(described_class).to have_graphql_fields(:jira_project_key, :createdAt, :scheduled_at, :scheduled_by)
end
end
diff --git a/spec/helpers/explore_helper_spec.rb b/spec/helpers/explore_helper_spec.rb
index f8240dd3a4c..1a6af3be055 100644
--- a/spec/helpers/explore_helper_spec.rb
+++ b/spec/helpers/explore_helper_spec.rb
@@ -19,23 +19,10 @@ describe ExploreHelper do
end
describe '#public_visibility_restricted?' do
- using RSpec::Parameterized::TableSyntax
+ it 'delegates to Gitlab::VisibilityLevel' do
+ expect(Gitlab::VisibilityLevel).to receive(:public_visibility_restricted?).and_call_original
- where(:visibility_levels, :expected_status) do
- nil | nil
- [Gitlab::VisibilityLevel::PRIVATE] | false
- [Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL] | false
- [Gitlab::VisibilityLevel::PUBLIC] | true
- end
-
- with_them do
- before do
- stub_application_setting(restricted_visibility_levels: visibility_levels)
- end
-
- it 'returns the expected status' do
- expect(helper.public_visibility_restricted?).to eq(expected_status)
- end
+ helper.public_visibility_restricted?
end
end
end
diff --git a/spec/helpers/projects/alert_management_helper_spec.rb b/spec/helpers/projects/alert_management_helper_spec.rb
index ee180cef692..9246d1deff6 100644
--- a/spec/helpers/projects/alert_management_helper_spec.rb
+++ b/spec/helpers/projects/alert_management_helper_spec.rb
@@ -5,21 +5,32 @@ require 'spec_helper'
describe Projects::AlertManagementHelper do
include Gitlab::Routing.url_helpers
- let(:project) { create(:project) }
+ let_it_be(:project, reload: true) { create(:project) }
+ let_it_be(:current_user) { create(:user) }
describe '#alert_management_data' do
+ let(:user_can_enable_alert_management) { false }
let(:setting_path) { project_settings_operations_path(project) }
let(:index_path) do
project_alert_management_index_path(project, format: :json)
end
+ before do
+ allow(helper)
+ .to receive(:can?)
+ .with(current_user, :admin_operations, project)
+ .and_return(user_can_enable_alert_management)
+ end
+
context 'without alert_managements_setting' do
it 'returns frontend configuration' do
- expect(alert_management_data(project)).to eq(
+ expect(alert_management_data(current_user, project)).to eq(
'index-path' => index_path,
'enable-alert-management-path' => setting_path,
- "empty-alert-svg-path" => "/images/illustrations/alert-management-empty-state.svg"
+ "empty-alert-svg-path" => "/images/illustrations/alert-management-empty-state.svg",
+ 'user-can-enable-alert-management' => 'false',
+ 'alert-management-enabled' => 'true'
)
end
end
diff --git a/spec/javascripts/deploy_keys/components/action_btn_spec.js b/spec/javascripts/deploy_keys/components/action_btn_spec.js
deleted file mode 100644
index 5bf72cc0018..00000000000
--- a/spec/javascripts/deploy_keys/components/action_btn_spec.js
+++ /dev/null
@@ -1,72 +0,0 @@
-import Vue from 'vue';
-import eventHub from '~/deploy_keys/eventhub';
-import actionBtn from '~/deploy_keys/components/action_btn.vue';
-
-describe('Deploy keys action btn', () => {
- const data = getJSONFixture('deploy_keys/keys.json');
- const deployKey = data.enabled_keys[0];
- let vm;
-
- beforeEach(done => {
- const ActionBtnComponent = Vue.extend({
- components: {
- actionBtn,
- },
- data() {
- return {
- deployKey,
- };
- },
- template: `
- <action-btn
- :deploy-key="deployKey"
- type="enable">
- Enable
- </action-btn>`,
- });
-
- vm = new ActionBtnComponent().$mount();
-
- Vue.nextTick()
- .then(done)
- .catch(done.fail);
- });
-
- it('renders the default slot', () => {
- expect(vm.$el.textContent.trim()).toBe('Enable');
- });
-
- it('sends eventHub event with btn type', done => {
- spyOn(eventHub, '$emit');
-
- vm.$el.click();
-
- Vue.nextTick(() => {
- expect(eventHub.$emit).toHaveBeenCalledWith('enable.key', deployKey, jasmine.anything());
-
- done();
- });
- });
-
- it('shows loading spinner after click', done => {
- vm.$el.click();
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.fa')).toBeDefined();
-
- done();
- });
- });
-
- it('disables button after click', done => {
- vm.$el.click();
-
- Vue.nextTick(() => {
- expect(vm.$el.classList.contains('disabled')).toBeTruthy();
-
- expect(vm.$el.getAttribute('disabled')).toBe('disabled');
-
- done();
- });
- });
-});
diff --git a/spec/javascripts/deploy_keys/components/app_spec.js b/spec/javascripts/deploy_keys/components/app_spec.js
deleted file mode 100644
index c9a9814d122..00000000000
--- a/spec/javascripts/deploy_keys/components/app_spec.js
+++ /dev/null
@@ -1,155 +0,0 @@
-import Vue from 'vue';
-import MockAdapter from 'axios-mock-adapter';
-import { TEST_HOST } from 'spec/test_constants';
-import axios from '~/lib/utils/axios_utils';
-import eventHub from '~/deploy_keys/eventhub';
-import deployKeysApp from '~/deploy_keys/components/app.vue';
-
-describe('Deploy keys app component', () => {
- const data = getJSONFixture('deploy_keys/keys.json');
- let vm;
- let mock;
-
- beforeEach(done => {
- // set up axios mock before component
- mock = new MockAdapter(axios);
- mock.onGet(`${TEST_HOST}/dummy/`).replyOnce(200, data);
-
- const Component = Vue.extend(deployKeysApp);
-
- vm = new Component({
- propsData: {
- endpoint: `${TEST_HOST}/dummy`,
- projectId: '8',
- },
- }).$mount();
-
- setTimeout(done);
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('renders loading icon', done => {
- vm.store.keys = {};
- vm.isLoading = false;
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelectorAll('.deploy-keys .nav-links li').length).toBe(0);
-
- expect(vm.$el.querySelector('.fa-spinner')).toBeDefined();
-
- done();
- });
- });
-
- it('renders keys panels', () => {
- expect(vm.$el.querySelectorAll('.deploy-keys .nav-links li').length).toBe(3);
- });
-
- it('renders the titles with keys count', () => {
- const textContent = selector => {
- const element = vm.$el.querySelector(`${selector}`);
-
- expect(element).not.toBeNull();
- return element.textContent.trim();
- };
-
- expect(textContent('.js-deployKeys-tab-enabled_keys')).toContain('Enabled deploy keys');
- expect(textContent('.js-deployKeys-tab-available_project_keys')).toContain(
- 'Privately accessible deploy keys',
- );
-
- expect(textContent('.js-deployKeys-tab-public_keys')).toContain(
- 'Publicly accessible deploy keys',
- );
-
- expect(textContent('.js-deployKeys-tab-enabled_keys .badge')).toBe(
- `${vm.store.keys.enabled_keys.length}`,
- );
-
- expect(textContent('.js-deployKeys-tab-available_project_keys .badge')).toBe(
- `${vm.store.keys.available_project_keys.length}`,
- );
-
- expect(textContent('.js-deployKeys-tab-public_keys .badge')).toBe(
- `${vm.store.keys.public_keys.length}`,
- );
- });
-
- it('does not render key panels when keys object is empty', done => {
- vm.store.keys = {};
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelectorAll('.deploy-keys .nav-links li').length).toBe(0);
-
- done();
- });
- });
-
- it('re-fetches deploy keys when enabling a key', done => {
- const key = data.public_keys[0];
-
- spyOn(vm.service, 'getKeys');
- spyOn(vm.service, 'enableKey').and.callFake(() => Promise.resolve());
-
- eventHub.$emit('enable.key', key);
-
- Vue.nextTick(() => {
- expect(vm.service.enableKey).toHaveBeenCalledWith(key.id);
- expect(vm.service.getKeys).toHaveBeenCalled();
- done();
- });
- });
-
- it('re-fetches deploy keys when disabling a key', done => {
- const key = data.public_keys[0];
-
- spyOn(window, 'confirm').and.returnValue(true);
- spyOn(vm.service, 'getKeys');
- spyOn(vm.service, 'disableKey').and.callFake(() => Promise.resolve());
-
- eventHub.$emit('disable.key', key);
-
- Vue.nextTick(() => {
- expect(vm.service.disableKey).toHaveBeenCalledWith(key.id);
- expect(vm.service.getKeys).toHaveBeenCalled();
- done();
- });
- });
-
- it('calls disableKey when removing a key', done => {
- const key = data.public_keys[0];
-
- spyOn(window, 'confirm').and.returnValue(true);
- spyOn(vm.service, 'getKeys');
- spyOn(vm.service, 'disableKey').and.callFake(() => Promise.resolve());
-
- eventHub.$emit('remove.key', key);
-
- Vue.nextTick(() => {
- expect(vm.service.disableKey).toHaveBeenCalledWith(key.id);
- expect(vm.service.getKeys).toHaveBeenCalled();
- done();
- });
- });
-
- it('hasKeys returns true when there are keys', () => {
- expect(vm.hasKeys).toEqual(3);
- });
-
- it('resets disable button loading state', done => {
- spyOn(window, 'confirm').and.returnValue(false);
-
- const btn = vm.$el.querySelector('.btn-warning');
-
- btn.click();
-
- Vue.nextTick(() => {
- expect(btn.querySelector('.btn-warning')).not.toExist();
-
- done();
- });
- });
-});
diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js
deleted file mode 100644
index 7117dc4a9ee..00000000000
--- a/spec/javascripts/deploy_keys/components/key_spec.js
+++ /dev/null
@@ -1,157 +0,0 @@
-import Vue from 'vue';
-import DeployKeysStore from '~/deploy_keys/store';
-import key from '~/deploy_keys/components/key.vue';
-import { getTimeago } from '~/lib/utils/datetime_utility';
-
-describe('Deploy keys key', () => {
- let vm;
- const KeyComponent = Vue.extend(key);
- const data = getJSONFixture('deploy_keys/keys.json');
- const createComponent = deployKey => {
- const store = new DeployKeysStore();
- store.keys = data;
-
- vm = new KeyComponent({
- propsData: {
- deployKey,
- store,
- endpoint: 'https://test.host/dummy/endpoint',
- },
- }).$mount();
- };
-
- describe('enabled key', () => {
- const deployKey = data.enabled_keys[0];
-
- beforeEach(done => {
- createComponent(deployKey);
-
- setTimeout(done);
- });
-
- it('renders the keys title', () => {
- expect(vm.$el.querySelector('.title').textContent.trim()).toContain('My title');
- });
-
- it('renders human friendly formatted created date', () => {
- expect(vm.$el.querySelector('.key-created-at').textContent.trim()).toBe(
- `${getTimeago().format(deployKey.created_at)}`,
- );
- });
-
- it('shows pencil button for editing', () => {
- expect(vm.$el.querySelector('.btn .ic-pencil')).toExist();
- });
-
- it('shows disable button when the project is not deletable', () => {
- expect(vm.$el.querySelector('.btn .ic-cancel')).toExist();
- });
-
- it('shows remove button when the project is deletable', done => {
- vm.deployKey.destroyed_when_orphaned = true;
- vm.deployKey.almost_orphaned = true;
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.btn .ic-remove')).toExist();
- done();
- });
- });
- });
-
- describe('deploy key labels', () => {
- it('shows write access title when key has write access', done => {
- vm.deployKey.deploy_keys_projects[0].can_push = true;
-
- Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.deploy-project-label').getAttribute('data-original-title'),
- ).toBe('Write access allowed');
- done();
- });
- });
-
- it('does not show write access title when key has write access', done => {
- vm.deployKey.deploy_keys_projects[0].can_push = false;
-
- Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.deploy-project-label').getAttribute('data-original-title'),
- ).toBe('Read access only');
- done();
- });
- });
-
- it('shows expandable button if more than two projects', () => {
- const labels = vm.$el.querySelectorAll('.deploy-project-label');
-
- expect(labels.length).toBe(2);
- expect(labels[1].textContent).toContain('others');
- expect(labels[1].getAttribute('data-original-title')).toContain('Expand');
- });
-
- it('expands all project labels after click', done => {
- const { length } = vm.deployKey.deploy_keys_projects;
- vm.$el.querySelectorAll('.deploy-project-label')[1].click();
-
- Vue.nextTick(() => {
- const labels = vm.$el.querySelectorAll('.deploy-project-label');
-
- expect(labels.length).toBe(length);
- expect(labels[1].textContent).not.toContain(`+${length} others`);
- expect(labels[1].getAttribute('data-original-title')).not.toContain('Expand');
- done();
- });
- });
-
- it('shows two projects', done => {
- vm.deployKey.deploy_keys_projects = [...vm.deployKey.deploy_keys_projects].slice(0, 2);
-
- Vue.nextTick(() => {
- const labels = vm.$el.querySelectorAll('.deploy-project-label');
-
- expect(labels.length).toBe(2);
- expect(labels[1].textContent).toContain(
- vm.deployKey.deploy_keys_projects[1].project.full_name,
- );
- done();
- });
- });
- });
-
- describe('public keys', () => {
- const deployKey = data.public_keys[0];
-
- beforeEach(done => {
- createComponent(deployKey);
-
- setTimeout(done);
- });
-
- it('renders deploy keys without any enabled projects', done => {
- vm.deployKey.deploy_keys_projects = [];
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.deploy-project-list').textContent.trim()).toBe('None');
-
- done();
- });
- });
-
- it('shows enable button', () => {
- expect(vm.$el.querySelectorAll('.btn')[0].textContent.trim()).toBe('Enable');
- });
-
- it('shows pencil button for editing', () => {
- expect(vm.$el.querySelector('.btn .ic-pencil')).toExist();
- });
-
- it('shows disable button when key is enabled', done => {
- vm.store.keys.enabled_keys.push(deployKey);
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.btn .ic-cancel')).toExist();
-
- done();
- });
- });
- });
-});
diff --git a/spec/javascripts/deploy_keys/components/keys_panel_spec.js b/spec/javascripts/deploy_keys/components/keys_panel_spec.js
deleted file mode 100644
index f71f5ccf082..00000000000
--- a/spec/javascripts/deploy_keys/components/keys_panel_spec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import Vue from 'vue';
-import DeployKeysStore from '~/deploy_keys/store';
-import deployKeysPanel from '~/deploy_keys/components/keys_panel.vue';
-
-describe('Deploy keys panel', () => {
- const data = getJSONFixture('deploy_keys/keys.json');
- let vm;
-
- beforeEach(done => {
- const DeployKeysPanelComponent = Vue.extend(deployKeysPanel);
- const store = new DeployKeysStore();
- store.keys = data;
-
- vm = new DeployKeysPanelComponent({
- propsData: {
- title: 'test',
- keys: data.enabled_keys,
- showHelpBox: true,
- store,
- endpoint: 'https://test.host/dummy/endpoint',
- },
- }).$mount();
-
- setTimeout(done);
- });
-
- it('renders list of keys', () => {
- expect(vm.$el.querySelectorAll('.deploy-key').length).toBe(vm.keys.length);
- });
-
- it('renders table header', () => {
- const tableHeader = vm.$el.querySelector('.table-row-header');
-
- expect(tableHeader).toExist();
- expect(tableHeader.textContent).toContain('Deploy key');
- expect(tableHeader.textContent).toContain('Project usage');
- expect(tableHeader.textContent).toContain('Created');
- });
-
- it('renders help box if keys are empty', done => {
- vm.keys = [];
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.settings-message')).toBeDefined();
-
- expect(vm.$el.querySelector('.settings-message').textContent.trim()).toBe(
- 'No deploy keys found. Create one with the form above.',
- );
-
- done();
- });
- });
-
- it('renders no table header if keys are empty', done => {
- vm.keys = [];
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.table-row-header')).not.toExist();
-
- done();
- });
- });
-});
diff --git a/spec/javascripts/dirty_submit/dirty_submit_collection_spec.js b/spec/javascripts/dirty_submit/dirty_submit_collection_spec.js
deleted file mode 100644
index 47be0b3ce9d..00000000000
--- a/spec/javascripts/dirty_submit/dirty_submit_collection_spec.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import DirtySubmitCollection from '~/dirty_submit/dirty_submit_collection';
-import { setInputValue, createForm } from './helper';
-
-describe('DirtySubmitCollection', () => {
- it('disables submits until there are changes', done => {
- const testElementsCollection = [createForm(), createForm()];
- const forms = testElementsCollection.map(testElements => testElements.form);
-
- new DirtySubmitCollection(forms); // eslint-disable-line no-new
-
- testElementsCollection.forEach(testElements => {
- const { input, submit } = testElements;
- const originalValue = input.value;
-
- expect(submit.disabled).toBe(true);
-
- return setInputValue(input, `${originalValue} changes`)
- .then(() => {
- expect(submit.disabled).toBe(false);
- })
- .then(() => setInputValue(input, originalValue))
- .then(() => {
- expect(submit.disabled).toBe(true);
- })
- .then(done)
- .catch(done.fail);
- });
- });
-});
diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js
deleted file mode 100644
index 5cd91413c5f..00000000000
--- a/spec/javascripts/pipelines/pipelines_spec.js
+++ /dev/null
@@ -1,783 +0,0 @@
-import Vue from 'vue';
-import MockAdapter from 'axios-mock-adapter';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import axios from '~/lib/utils/axios_utils';
-import pipelinesComp from '~/pipelines/components/pipelines.vue';
-import Store from '~/pipelines/stores/pipelines_store';
-import { pipelineWithStages, stageReply } from './mock_data';
-
-describe('Pipelines', () => {
- const jsonFixtureName = 'pipelines/pipelines.json';
-
- preloadFixtures(jsonFixtureName);
-
- let PipelinesComponent;
- let pipelines;
- let vm;
- let mock;
-
- const paths = {
- endpoint: 'twitter/flight/pipelines.json',
- autoDevopsPath: '/help/topics/autodevops/index.md',
- helpPagePath: '/help/ci/quick_start/README',
- emptyStateSvgPath: '/assets/illustrations/pipelines_empty.svg',
- errorStateSvgPath: '/assets/illustrations/pipelines_failed.svg',
- noPipelinesSvgPath: '/assets/illustrations/pipelines_pending.svg',
- ciLintPath: '/ci/lint',
- resetCachePath: '/twitter/flight/settings/ci_cd/reset_cache',
- newPipelinePath: '/twitter/flight/pipelines/new',
- };
-
- const noPermissions = {
- endpoint: 'twitter/flight/pipelines.json',
- autoDevopsPath: '/help/topics/autodevops/index.md',
- helpPagePath: '/help/ci/quick_start/README',
- emptyStateSvgPath: '/assets/illustrations/pipelines_empty.svg',
- errorStateSvgPath: '/assets/illustrations/pipelines_failed.svg',
- noPipelinesSvgPath: '/assets/illustrations/pipelines_pending.svg',
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
-
- pipelines = getJSONFixture(jsonFixtureName);
-
- PipelinesComponent = Vue.extend(pipelinesComp);
- });
-
- afterEach(() => {
- vm.$destroy();
- mock.restore();
- });
-
- describe('With permission', () => {
- describe('With pipelines in main tab', () => {
- beforeEach(done => {
- mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
-
- vm = mountComponent(PipelinesComponent, {
- store: new Store(),
- hasGitlabCi: true,
- canCreatePipeline: true,
- ...paths,
- });
-
- setTimeout(() => {
- done();
- });
- });
-
- it('renders tabs', () => {
- expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All');
- });
-
- it('renders Run Pipeline link', () => {
- expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(
- paths.newPipelinePath,
- );
- });
-
- it('renders CI Lint link', () => {
- expect(vm.$el.querySelector('.js-ci-lint').getAttribute('href')).toEqual(paths.ciLintPath);
- });
-
- it('renders Clear Runner Cache button', () => {
- expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual(
- 'Clear Runner Caches',
- );
- });
-
- it('renders pipelines table', () => {
- expect(vm.$el.querySelectorAll('.gl-responsive-table-row').length).toEqual(
- pipelines.pipelines.length + 1,
- );
- });
- });
-
- describe('Without pipelines on main tab with CI', () => {
- beforeEach(done => {
- mock.onGet('twitter/flight/pipelines.json').reply(200, {
- pipelines: [],
- count: {
- all: 0,
- pending: 0,
- running: 0,
- finished: 0,
- },
- });
- vm = mountComponent(PipelinesComponent, {
- store: new Store(),
- hasGitlabCi: true,
- canCreatePipeline: true,
- ...paths,
- });
-
- setTimeout(() => {
- done();
- });
- });
-
- it('renders tabs', () => {
- expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All');
- });
-
- it('renders Run Pipeline link', () => {
- expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(
- paths.newPipelinePath,
- );
- });
-
- it('renders CI Lint link', () => {
- expect(vm.$el.querySelector('.js-ci-lint').getAttribute('href')).toEqual(paths.ciLintPath);
- });
-
- it('renders Clear Runner Cache button', () => {
- expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual(
- 'Clear Runner Caches',
- );
- });
-
- it('renders tab empty state', () => {
- expect(vm.$el.querySelector('.empty-state h4').textContent.trim()).toEqual(
- 'There are currently no pipelines.',
- );
- });
- });
-
- describe('Without pipelines nor CI', () => {
- beforeEach(done => {
- mock.onGet('twitter/flight/pipelines.json').reply(200, {
- pipelines: [],
- count: {
- all: 0,
- pending: 0,
- running: 0,
- finished: 0,
- },
- });
- vm = mountComponent(PipelinesComponent, {
- store: new Store(),
- hasGitlabCi: false,
- canCreatePipeline: true,
- ...paths,
- });
-
- setTimeout(() => {
- done();
- });
- });
-
- it('renders empty state', () => {
- expect(vm.$el.querySelector('.js-empty-state h4').textContent.trim()).toEqual(
- 'Build with confidence',
- );
-
- expect(vm.$el.querySelector('.js-get-started-pipelines').getAttribute('href')).toEqual(
- paths.helpPagePath,
- );
- });
-
- it('does not render tabs nor buttons', () => {
- expect(vm.$el.querySelector('.js-pipelines-tab-all')).toBeNull();
- expect(vm.$el.querySelector('.js-run-pipeline')).toBeNull();
- expect(vm.$el.querySelector('.js-ci-lint')).toBeNull();
- expect(vm.$el.querySelector('.js-clear-cache')).toBeNull();
- });
- });
-
- describe('When API returns error', () => {
- beforeEach(done => {
- mock.onGet('twitter/flight/pipelines.json').reply(500, {});
- vm = mountComponent(PipelinesComponent, {
- store: new Store(),
- hasGitlabCi: false,
- canCreatePipeline: true,
- ...paths,
- });
-
- setTimeout(() => {
- done();
- });
- });
-
- it('renders tabs', () => {
- expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All');
- });
-
- it('renders buttons', () => {
- expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(
- paths.newPipelinePath,
- );
-
- expect(vm.$el.querySelector('.js-ci-lint').getAttribute('href')).toEqual(paths.ciLintPath);
- expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual(
- 'Clear Runner Caches',
- );
- });
-
- it('renders error state', () => {
- expect(vm.$el.querySelector('.empty-state').textContent.trim()).toContain(
- 'There was an error fetching the pipelines.',
- );
- });
- });
- });
-
- describe('Without permission', () => {
- describe('With pipelines in main tab', () => {
- beforeEach(done => {
- mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
-
- vm = mountComponent(PipelinesComponent, {
- store: new Store(),
- hasGitlabCi: false,
- canCreatePipeline: false,
- ...noPermissions,
- });
-
- setTimeout(() => {
- done();
- });
- });
-
- it('renders tabs', () => {
- expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All');
- });
-
- it('does not render buttons', () => {
- expect(vm.$el.querySelector('.js-run-pipeline')).toBeNull();
- expect(vm.$el.querySelector('.js-ci-lint')).toBeNull();
- expect(vm.$el.querySelector('.js-clear-cache')).toBeNull();
- });
-
- it('renders pipelines table', () => {
- expect(vm.$el.querySelectorAll('.gl-responsive-table-row').length).toEqual(
- pipelines.pipelines.length + 1,
- );
- });
- });
-
- describe('Without pipelines on main tab with CI', () => {
- beforeEach(done => {
- mock.onGet('twitter/flight/pipelines.json').reply(200, {
- pipelines: [],
- count: {
- all: 0,
- pending: 0,
- running: 0,
- finished: 0,
- },
- });
-
- vm = mountComponent(PipelinesComponent, {
- store: new Store(),
- hasGitlabCi: true,
- canCreatePipeline: false,
- ...noPermissions,
- });
-
- setTimeout(() => {
- done();
- });
- });
-
- it('renders tabs', () => {
- expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All');
- });
-
- it('does not render buttons', () => {
- expect(vm.$el.querySelector('.js-run-pipeline')).toBeNull();
- expect(vm.$el.querySelector('.js-ci-lint')).toBeNull();
- expect(vm.$el.querySelector('.js-clear-cache')).toBeNull();
- });
-
- it('renders tab empty state', () => {
- expect(vm.$el.querySelector('.empty-state h4').textContent.trim()).toEqual(
- 'There are currently no pipelines.',
- );
- });
- });
-
- describe('Without pipelines nor CI', () => {
- beforeEach(done => {
- mock.onGet('twitter/flight/pipelines.json').reply(200, {
- pipelines: [],
- count: {
- all: 0,
- pending: 0,
- running: 0,
- finished: 0,
- },
- });
-
- vm = mountComponent(PipelinesComponent, {
- store: new Store(),
- hasGitlabCi: false,
- canCreatePipeline: false,
- ...noPermissions,
- });
-
- setTimeout(() => {
- done();
- });
- });
-
- it('renders empty state without button to set CI', () => {
- expect(vm.$el.querySelector('.js-empty-state').textContent.trim()).toEqual(
- 'This project is not currently set up to run pipelines.',
- );
-
- expect(vm.$el.querySelector('.js-get-started-pipelines')).toBeNull();
- });
-
- it('does not render tabs or buttons', () => {
- expect(vm.$el.querySelector('.js-pipelines-tab-all')).toBeNull();
- expect(vm.$el.querySelector('.js-run-pipeline')).toBeNull();
- expect(vm.$el.querySelector('.js-ci-lint')).toBeNull();
- expect(vm.$el.querySelector('.js-clear-cache')).toBeNull();
- });
- });
-
- describe('When API returns error', () => {
- beforeEach(done => {
- mock.onGet('twitter/flight/pipelines.json').reply(500, {});
-
- vm = mountComponent(PipelinesComponent, {
- store: new Store(),
- hasGitlabCi: false,
- canCreatePipeline: true,
- ...noPermissions,
- });
-
- setTimeout(() => {
- done();
- });
- });
-
- it('renders tabs', () => {
- expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All');
- });
-
- it('does not renders buttons', () => {
- expect(vm.$el.querySelector('.js-run-pipeline')).toBeNull();
- expect(vm.$el.querySelector('.js-ci-lint')).toBeNull();
- expect(vm.$el.querySelector('.js-clear-cache')).toBeNull();
- });
-
- it('renders error state', () => {
- expect(vm.$el.querySelector('.empty-state').textContent.trim()).toContain(
- 'There was an error fetching the pipelines.',
- );
- });
- });
- });
-
- describe('successful request', () => {
- describe('with pipelines', () => {
- beforeEach(() => {
- mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
-
- vm = mountComponent(PipelinesComponent, {
- store: new Store(),
- hasGitlabCi: true,
- canCreatePipeline: true,
- ...paths,
- });
- });
-
- it('should render table', done => {
- setTimeout(() => {
- expect(vm.$el.querySelector('.table-holder')).toBeDefined();
- expect(vm.$el.querySelectorAll('.gl-responsive-table-row').length).toEqual(
- pipelines.pipelines.length + 1,
- );
- done();
- });
- });
-
- it('should render navigation tabs', done => {
- setTimeout(() => {
- expect(vm.$el.querySelector('.js-pipelines-tab-pending').textContent.trim()).toContain(
- 'Pending',
- );
-
- expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All');
-
- expect(vm.$el.querySelector('.js-pipelines-tab-running').textContent.trim()).toContain(
- 'Running',
- );
-
- expect(vm.$el.querySelector('.js-pipelines-tab-finished').textContent.trim()).toContain(
- 'Finished',
- );
-
- expect(vm.$el.querySelector('.js-pipelines-tab-branches').textContent.trim()).toContain(
- 'Branches',
- );
-
- expect(vm.$el.querySelector('.js-pipelines-tab-tags').textContent.trim()).toContain(
- 'Tags',
- );
- done();
- });
- });
-
- it('should make an API request when using tabs', done => {
- setTimeout(() => {
- spyOn(vm, 'updateContent');
- vm.$el.querySelector('.js-pipelines-tab-finished').click();
-
- expect(vm.updateContent).toHaveBeenCalledWith({ scope: 'finished', page: '1' });
- done();
- });
- });
-
- describe('with pagination', () => {
- it('should make an API request when using pagination', done => {
- setTimeout(() => {
- spyOn(vm, 'updateContent');
- // Mock pagination
- vm.store.state.pageInfo = {
- page: 1,
- total: 10,
- perPage: 2,
- nextPage: 2,
- totalPages: 5,
- };
-
- vm.$nextTick(() => {
- vm.$el.querySelector('.next-page-item').click();
-
- expect(vm.updateContent).toHaveBeenCalledWith({ scope: 'all', page: '2' });
-
- done();
- });
- });
- });
- });
- });
- });
-
- describe('methods', () => {
- beforeEach(() => {
- spyOn(window.history, 'pushState').and.stub();
- });
-
- describe('updateContent', () => {
- it('should set given parameters', () => {
- vm = mountComponent(PipelinesComponent, {
- store: new Store(),
- hasGitlabCi: true,
- canCreatePipeline: true,
- ...paths,
- });
- vm.updateContent({ scope: 'finished', page: '4' });
-
- expect(vm.page).toEqual('4');
- expect(vm.scope).toEqual('finished');
- expect(vm.requestData.scope).toEqual('finished');
- expect(vm.requestData.page).toEqual('4');
- });
- });
-
- describe('onChangeTab', () => {
- it('should set page to 1', () => {
- vm = mountComponent(PipelinesComponent, {
- store: new Store(),
- hasGitlabCi: true,
- canCreatePipeline: true,
- ...paths,
- });
- spyOn(vm, 'updateContent');
-
- vm.onChangeTab('running');
-
- expect(vm.updateContent).toHaveBeenCalledWith({ scope: 'running', page: '1' });
- });
- });
-
- describe('onChangePage', () => {
- it('should update page and keep scope', () => {
- vm = mountComponent(PipelinesComponent, {
- store: new Store(),
- hasGitlabCi: true,
- canCreatePipeline: true,
- ...paths,
- });
- spyOn(vm, 'updateContent');
-
- vm.onChangePage(4);
-
- expect(vm.updateContent).toHaveBeenCalledWith({ scope: vm.scope, page: '4' });
- });
- });
- });
-
- describe('computed properties', () => {
- beforeEach(() => {
- vm = mountComponent(PipelinesComponent, {
- store: new Store(),
- hasGitlabCi: true,
- canCreatePipeline: true,
- ...paths,
- });
- });
-
- describe('tabs', () => {
- it('returns default tabs', () => {
- expect(vm.tabs).toEqual([
- { name: 'All', scope: 'all', count: undefined, isActive: true },
- { name: 'Pending', scope: 'pending', count: undefined, isActive: false },
- { name: 'Running', scope: 'running', count: undefined, isActive: false },
- { name: 'Finished', scope: 'finished', count: undefined, isActive: false },
- { name: 'Branches', scope: 'branches', isActive: false },
- { name: 'Tags', scope: 'tags', isActive: false },
- ]);
- });
- });
-
- describe('emptyTabMessage', () => {
- it('returns message with scope', done => {
- vm.scope = 'pending';
-
- vm.$nextTick(() => {
- expect(vm.emptyTabMessage).toEqual('There are currently no pending pipelines.');
- done();
- });
- });
-
- it('returns message without scope when scope is `all`', () => {
- expect(vm.emptyTabMessage).toEqual('There are currently no pipelines.');
- });
- });
-
- describe('stateToRender', () => {
- it('returns loading state when the app is loading', () => {
- expect(vm.stateToRender).toEqual('loading');
- });
-
- it('returns error state when app has error', done => {
- vm.hasError = true;
- vm.isLoading = false;
-
- vm.$nextTick(() => {
- expect(vm.stateToRender).toEqual('error');
- done();
- });
- });
-
- it('returns table list when app has pipelines', done => {
- vm.isLoading = false;
- vm.hasError = false;
- vm.state.pipelines = pipelines.pipelines;
-
- vm.$nextTick(() => {
- expect(vm.stateToRender).toEqual('tableList');
-
- done();
- });
- });
-
- it('returns empty tab when app does not have pipelines but project has pipelines', done => {
- vm.state.count.all = 10;
- vm.isLoading = false;
-
- vm.$nextTick(() => {
- expect(vm.stateToRender).toEqual('emptyTab');
-
- done();
- });
- });
-
- it('returns empty tab when project has CI', done => {
- vm.isLoading = false;
- vm.$nextTick(() => {
- expect(vm.stateToRender).toEqual('emptyTab');
-
- done();
- });
- });
-
- it('returns empty state when project does not have pipelines nor CI', done => {
- vm.isLoading = false;
- vm.hasGitlabCi = false;
- vm.$nextTick(() => {
- expect(vm.stateToRender).toEqual('emptyState');
-
- done();
- });
- });
- });
-
- describe('shouldRenderTabs', () => {
- it('returns true when state is loading & has already made the first request', done => {
- vm.isLoading = true;
- vm.hasMadeRequest = true;
-
- vm.$nextTick(() => {
- expect(vm.shouldRenderTabs).toEqual(true);
-
- done();
- });
- });
-
- it('returns true when state is tableList & has already made the first request', done => {
- vm.isLoading = false;
- vm.state.pipelines = pipelines.pipelines;
- vm.hasMadeRequest = true;
-
- vm.$nextTick(() => {
- expect(vm.shouldRenderTabs).toEqual(true);
-
- done();
- });
- });
-
- it('returns true when state is error & has already made the first request', done => {
- vm.isLoading = false;
- vm.hasError = true;
- vm.hasMadeRequest = true;
-
- vm.$nextTick(() => {
- expect(vm.shouldRenderTabs).toEqual(true);
-
- done();
- });
- });
-
- it('returns true when state is empty tab & has already made the first request', done => {
- vm.isLoading = false;
- vm.state.count.all = 10;
- vm.hasMadeRequest = true;
-
- vm.$nextTick(() => {
- expect(vm.shouldRenderTabs).toEqual(true);
-
- done();
- });
- });
-
- it('returns false when has not made first request', done => {
- vm.hasMadeRequest = false;
-
- vm.$nextTick(() => {
- expect(vm.shouldRenderTabs).toEqual(false);
-
- done();
- });
- });
-
- it('returns false when state is empty state', done => {
- vm.isLoading = false;
- vm.hasMadeRequest = true;
- vm.hasGitlabCi = false;
-
- vm.$nextTick(() => {
- expect(vm.shouldRenderTabs).toEqual(false);
-
- done();
- });
- });
- });
-
- describe('shouldRenderButtons', () => {
- it('returns true when it has paths & has made the first request', done => {
- vm.hasMadeRequest = true;
-
- vm.$nextTick(() => {
- expect(vm.shouldRenderButtons).toEqual(true);
-
- done();
- });
- });
-
- it('returns false when it has not made the first request', done => {
- vm.hasMadeRequest = false;
-
- vm.$nextTick(() => {
- expect(vm.shouldRenderButtons).toEqual(false);
-
- done();
- });
- });
- });
- });
-
- describe('updates results when a staged is clicked', () => {
- beforeEach(() => {
- const copyPipeline = Object.assign({}, pipelineWithStages);
- copyPipeline.id += 1;
- mock
- .onGet('twitter/flight/pipelines.json')
- .reply(
- 200,
- {
- pipelines: [pipelineWithStages],
- count: {
- all: 1,
- finished: 1,
- pending: 0,
- running: 0,
- },
- },
- {
- 'POLL-INTERVAL': 100,
- },
- )
- .onGet(pipelineWithStages.details.stages[0].dropdown_path)
- .reply(200, stageReply);
-
- vm = mountComponent(PipelinesComponent, {
- store: new Store(),
- hasGitlabCi: true,
- canCreatePipeline: true,
- ...paths,
- });
- });
-
- describe('when a request is being made', () => {
- it('stops polling, cancels the request, & restarts polling', done => {
- spyOn(vm.poll, 'stop');
- spyOn(vm.poll, 'restart');
- spyOn(vm.service.cancelationSource, 'cancel').and.callThrough();
-
- setTimeout(() => {
- vm.isMakingRequest = true;
- return vm
- .$nextTick()
- .then(() => {
- vm.$el.querySelector('.js-builds-dropdown-button').click();
- })
- .then(() => {
- expect(vm.service.cancelationSource.cancel).toHaveBeenCalled();
- expect(vm.poll.stop).toHaveBeenCalled();
-
- setTimeout(() => {
- expect(vm.poll.restart).toHaveBeenCalled();
- done();
- }, 0);
- })
- .catch(done.fail);
- }, 0);
- });
- });
-
- describe('when no request is being made', () => {
- it('stops polling & restarts polling', done => {
- spyOn(vm.poll, 'stop');
- spyOn(vm.poll, 'restart');
-
- setTimeout(() => {
- vm.$el.querySelector('.js-builds-dropdown-button').click();
-
- expect(vm.poll.stop).toHaveBeenCalled();
-
- setTimeout(() => {
- expect(vm.poll.restart).toHaveBeenCalled();
- done();
- }, 0);
- }, 0);
- });
- });
- });
-});
diff --git a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
index 9033b71b19f..f82e49f9323 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
@@ -5,11 +5,13 @@ require 'spec_helper'
describe Gitlab::Ci::Pipeline::Chain::Sequence do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
+
let(:pipeline) { build_stubbed(:ci_pipeline) }
let(:command) { Gitlab::Ci::Pipeline::Chain::Command.new }
let(:first_step) { spy('first step') }
let(:second_step) { spy('second step') }
let(:sequence) { [first_step, second_step] }
+ let(:histogram) { spy('prometheus metric') }
subject do
described_class.new(pipeline, command, sequence)
@@ -52,5 +54,13 @@ describe Gitlab::Ci::Pipeline::Chain::Sequence do
it 'returns a pipeline object' do
expect(subject.build!).to eq pipeline
end
+
+ it 'adds sequence duration to duration histogram' do
+ allow(command).to receive(:duration_histogram).and_return(histogram)
+
+ subject.build!
+
+ expect(histogram).to have_received(:observe)
+ end
end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
index a11a9d08503..2a4a911cf38 100644
--- a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
@@ -3,6 +3,10 @@
require 'spec_helper'
describe Gitlab::Kubernetes::Helm::BaseCommand do
+ subject(:base_command) do
+ test_class.new(rbac)
+ end
+
let(:application) { create(:clusters_applications_helm) }
let(:rbac) { false }
@@ -30,87 +34,17 @@ describe Gitlab::Kubernetes::Helm::BaseCommand do
end
end
- let(:base_command) do
- test_class.new(rbac)
- end
-
- subject { base_command }
-
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) { '' }
end
- describe '#pod_resource' do
- subject { base_command.pod_resource }
-
- it 'returns a kubeclient resoure with pod content for application' do
- is_expected.to be_an_instance_of ::Kubeclient::Resource
- end
-
- context 'when rbac is true' do
- let(:rbac) { true }
-
- it 'also returns a kubeclient resource' do
- is_expected.to be_an_instance_of ::Kubeclient::Resource
- end
- end
- end
-
describe '#pod_name' do
subject { base_command.pod_name }
it { is_expected.to eq('install-test-class-name') }
end
- describe '#service_account_resource' do
- let(:resource) do
- Kubeclient::Resource.new(metadata: { name: 'tiller', namespace: 'gitlab-managed-apps' })
- end
-
- subject { base_command.service_account_resource }
-
- context 'rbac is enabled' do
- let(:rbac) { true }
-
- it 'generates a Kubeclient resource for the tiller ServiceAccount' do
- is_expected.to eq(resource)
- end
- end
-
- context 'rbac is not enabled' do
- let(:rbac) { false }
-
- it 'generates nothing' do
- is_expected.to be_nil
- end
- end
- end
-
- describe '#cluster_role_binding_resource' do
- let(:resource) do
- Kubeclient::Resource.new(
- metadata: { name: 'tiller-admin' },
- roleRef: { apiGroup: 'rbac.authorization.k8s.io', kind: 'ClusterRole', name: 'cluster-admin' },
- subjects: [{ kind: 'ServiceAccount', name: 'tiller', namespace: 'gitlab-managed-apps' }]
- )
- end
-
- subject { base_command.cluster_role_binding_resource }
-
- context 'rbac is enabled' do
- let(:rbac) { true }
-
- it 'generates a Kubeclient resource for the ClusterRoleBinding for tiller' do
- is_expected.to eq(resource)
- end
- end
-
- context 'rbac is not enabled' do
- let(:rbac) { false }
-
- it 'generates nothing' do
- is_expected.to be_nil
- end
- end
+ it_behaves_like 'helm command' do
+ let(:command) { base_command }
end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/delete_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/delete_command_spec.rb
index 82e15864687..e1ca56b0ba6 100644
--- a/spec/lib/gitlab/kubernetes/helm/delete_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/delete_command_spec.rb
@@ -3,14 +3,13 @@
require 'spec_helper'
describe Gitlab::Kubernetes::Helm::DeleteCommand do
+ subject(:delete_command) { described_class.new(name: app_name, rbac: rbac, files: files) }
+
let(:app_name) { 'app-name' }
let(:rbac) { true }
let(:files) { {} }
- let(:delete_command) { described_class.new(name: app_name, rbac: rbac, files: files) }
-
- subject { delete_command }
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
export HELM_HOST="localhost:44134"
@@ -26,7 +25,7 @@ describe Gitlab::Kubernetes::Helm::DeleteCommand do
stub_feature_flags(managed_apps_local_tiller: false)
end
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
helm init --upgrade
@@ -48,7 +47,7 @@ describe Gitlab::Kubernetes::Helm::DeleteCommand do
EOS
end
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
helm init --upgrade
@@ -67,29 +66,13 @@ describe Gitlab::Kubernetes::Helm::DeleteCommand do
end
end
- describe '#pod_resource' do
- subject { delete_command.pod_resource }
-
- context 'rbac is enabled' do
- let(:rbac) { true }
-
- it 'generates a pod that uses the tiller serviceAccountName' do
- expect(subject.spec.serviceAccountName).to eq('tiller')
- end
- end
-
- context 'rbac is not enabled' do
- let(:rbac) { false }
-
- it 'generates a pod that uses the default serviceAccountName' do
- expect(subject.spec.serviceAcccountName).to be_nil
- end
- end
- end
-
describe '#pod_name' do
subject { delete_command.pod_name }
it { is_expected.to eq('uninstall-app-name') }
end
+
+ it_behaves_like 'helm command' do
+ let(:command) { delete_command }
+ end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
index 13021a08f9f..05d9b63d12b 100644
--- a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
@@ -3,25 +3,24 @@
require 'spec_helper'
describe Gitlab::Kubernetes::Helm::InitCommand do
+ subject(:init_command) { described_class.new(name: application.name, files: files, rbac: rbac) }
+
let(:application) { create(:clusters_applications_helm) }
let(:rbac) { false }
let(:files) { {} }
- let(:init_command) { described_class.new(name: application.name, files: files, rbac: rbac) }
- let(:commands) do
- <<~EOS
- helm init --tiller-tls --tiller-tls-verify --tls-ca-cert /data/helm/helm/config/ca.pem --tiller-tls-cert /data/helm/helm/config/cert.pem --tiller-tls-key /data/helm/helm/config/key.pem
- EOS
+ it_behaves_like 'helm command generator' do
+ let(:commands) do
+ <<~EOS
+ helm init --tiller-tls --tiller-tls-verify --tls-ca-cert /data/helm/helm/config/ca.pem --tiller-tls-cert /data/helm/helm/config/cert.pem --tiller-tls-key /data/helm/helm/config/key.pem
+ EOS
+ end
end
- subject { init_command }
-
- it_behaves_like 'helm commands'
-
context 'on a rbac-enabled cluster' do
let(:rbac) { true }
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
helm init --tiller-tls --tiller-tls-verify --tls-ca-cert /data/helm/helm/config/ca.pem --tiller-tls-cert /data/helm/helm/config/cert.pem --tiller-tls-key /data/helm/helm/config/key.pem --service-account tiller
@@ -30,57 +29,7 @@ describe Gitlab::Kubernetes::Helm::InitCommand do
end
end
- describe '#rbac?' do
- subject { init_command.rbac? }
-
- context 'rbac is enabled' do
- let(:rbac) { true }
-
- it { is_expected.to be_truthy }
- end
-
- context 'rbac is not enabled' do
- let(:rbac) { false }
-
- it { is_expected.to be_falsey }
- end
- end
-
- describe '#config_map_resource' do
- let(:metadata) do
- {
- name: 'values-content-configuration-helm',
- namespace: 'gitlab-managed-apps',
- labels: { name: 'values-content-configuration-helm' }
- }
- end
-
- let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: files) }
-
- subject { init_command.config_map_resource }
-
- it 'returns a KubeClient resource with config map content for the application' do
- is_expected.to eq(resource)
- end
- end
-
- describe '#pod_resource' do
- subject { init_command.pod_resource }
-
- context 'rbac is enabled' do
- let(:rbac) { true }
-
- it 'generates a pod that uses the tiller serviceAccountName' do
- expect(subject.spec.serviceAccountName).to eq('tiller')
- end
- end
-
- context 'rbac is not enabled' do
- let(:rbac) { false }
-
- it 'generates a pod that uses the default serviceAccountName' do
- expect(subject.spec.serviceAcccountName).to be_nil
- end
- end
+ it_behaves_like 'helm command' do
+ let(:command) { init_command }
end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
index a5ed8f57bf3..abd29e97505 100644
--- a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
@@ -3,14 +3,7 @@
require 'spec_helper'
describe Gitlab::Kubernetes::Helm::InstallCommand do
- let(:files) { { 'ca.pem': 'some file content' } }
- let(:repository) { 'https://repository.example.com' }
- let(:rbac) { false }
- let(:version) { '1.2.3' }
- let(:preinstall) { nil }
- let(:postinstall) { nil }
-
- let(:install_command) do
+ subject(:install_command) do
described_class.new(
name: 'app-name',
chart: 'chart-name',
@@ -23,9 +16,14 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
)
end
- subject { install_command }
+ let(:files) { { 'ca.pem': 'some file content' } }
+ let(:repository) { 'https://repository.example.com' }
+ let(:rbac) { false }
+ let(:version) { '1.2.3' }
+ let(:preinstall) { nil }
+ let(:postinstall) { nil }
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
export HELM_HOST="localhost:44134"
@@ -66,7 +64,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
EOS
end
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
helm init --upgrade
@@ -97,7 +95,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
context 'when rbac is true' do
let(:rbac) { true }
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
export HELM_HOST="localhost:44134"
@@ -128,7 +126,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
context 'when there is a pre-install script' do
let(:preinstall) { ['/bin/date', '/bin/true'] }
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
export HELM_HOST="localhost:44134"
@@ -161,7 +159,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
context 'when there is a post-install script' do
let(:postinstall) { ['/bin/date', "/bin/false\n"] }
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
export HELM_HOST="localhost:44134"
@@ -194,7 +192,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
context 'when there is no ca.pem file' do
let(:files) { { 'file.txt': 'some content' } }
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
export HELM_HOST="localhost:44134"
@@ -225,7 +223,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
context 'when there is no version' do
let(:version) { nil }
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
export HELM_HOST="localhost:44134"
@@ -252,57 +250,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
end
end
- describe '#rbac?' do
- subject { install_command.rbac? }
-
- context 'rbac is enabled' do
- let(:rbac) { true }
-
- it { is_expected.to be_truthy }
- end
-
- context 'rbac is not enabled' do
- let(:rbac) { false }
-
- it { is_expected.to be_falsey }
- end
- end
-
- describe '#pod_resource' do
- subject { install_command.pod_resource }
-
- context 'rbac is enabled' do
- let(:rbac) { true }
-
- it 'generates a pod that uses the tiller serviceAccountName' do
- expect(subject.spec.serviceAccountName).to eq('tiller')
- end
- end
-
- context 'rbac is not enabled' do
- let(:rbac) { false }
-
- it 'generates a pod that uses the default serviceAccountName' do
- expect(subject.spec.serviceAcccountName).to be_nil
- end
- end
- end
-
- describe '#config_map_resource' do
- let(:metadata) do
- {
- name: "values-content-configuration-app-name",
- namespace: 'gitlab-managed-apps',
- labels: { name: "values-content-configuration-app-name" }
- }
- end
-
- let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: files) }
-
- subject { install_command.config_map_resource }
-
- it 'returns a KubeClient resource with config map content for the application' do
- is_expected.to eq(resource)
- end
+ it_behaves_like 'helm command' do
+ let(:command) { install_command }
end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/patch_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/patch_command_spec.rb
index e69570f5371..eee842fa7d6 100644
--- a/spec/lib/gitlab/kubernetes/helm/patch_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/patch_command_spec.rb
@@ -33,7 +33,7 @@ describe Gitlab::Kubernetes::Helm::PatchCommand do
EOS
end
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
helm init --upgrade
@@ -57,7 +57,7 @@ describe Gitlab::Kubernetes::Helm::PatchCommand do
end
end
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
export HELM_HOST="localhost:44134"
@@ -83,7 +83,7 @@ describe Gitlab::Kubernetes::Helm::PatchCommand do
context 'when rbac is true' do
let(:rbac) { true }
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
export HELM_HOST="localhost:44134"
@@ -110,7 +110,7 @@ describe Gitlab::Kubernetes::Helm::PatchCommand do
context 'when there is no ca.pem file' do
let(:files) { { 'file.txt': 'some content' } }
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
export HELM_HOST="localhost:44134"
@@ -134,69 +134,19 @@ describe Gitlab::Kubernetes::Helm::PatchCommand do
end
end
- describe '#pod_name' do
- subject { patch_command.pod_name }
-
- it { is_expected.to eq 'install-app-name' }
- end
-
context 'when there is no version' do
let(:version) { nil }
it { expect { patch_command }.to raise_error(ArgumentError, 'version is required') }
end
- describe '#rbac?' do
- subject { patch_command.rbac? }
-
- context 'rbac is enabled' do
- let(:rbac) { true }
-
- it { is_expected.to be_truthy }
- end
-
- context 'rbac is not enabled' do
- let(:rbac) { false }
-
- it { is_expected.to be_falsey }
- end
- end
-
- describe '#pod_resource' do
- subject { patch_command.pod_resource }
-
- context 'rbac is enabled' do
- let(:rbac) { true }
-
- it 'generates a pod that uses the tiller serviceAccountName' do
- expect(subject.spec.serviceAccountName).to eq('tiller')
- end
- end
-
- context 'rbac is not enabled' do
- let(:rbac) { false }
+ describe '#pod_name' do
+ subject { patch_command.pod_name }
- it 'generates a pod that uses the default serviceAccountName' do
- expect(subject.spec.serviceAcccountName).to be_nil
- end
- end
+ it { is_expected.to eq 'install-app-name' }
end
- describe '#config_map_resource' do
- let(:metadata) do
- {
- name: "values-content-configuration-app-name",
- namespace: 'gitlab-managed-apps',
- labels: { name: "values-content-configuration-app-name" }
- }
- end
-
- let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: files) }
-
- subject { patch_command.config_map_resource }
-
- it 'returns a KubeClient resource with config map content for the application' do
- is_expected.to eq(resource)
- end
+ it_behaves_like 'helm command' do
+ let(:command) { patch_command }
end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/reset_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/reset_command_spec.rb
index 2a89b04723d..981bb4e4abf 100644
--- a/spec/lib/gitlab/kubernetes/helm/reset_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/reset_command_spec.rb
@@ -3,14 +3,13 @@
require 'spec_helper'
describe Gitlab::Kubernetes::Helm::ResetCommand do
+ subject(:reset_command) { described_class.new(name: name, rbac: rbac, files: files) }
+
let(:rbac) { true }
let(:name) { 'helm' }
let(:files) { {} }
- let(:reset_command) { described_class.new(name: name, rbac: rbac, files: files) }
-
- subject { reset_command }
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
helm reset
@@ -23,7 +22,7 @@ describe Gitlab::Kubernetes::Helm::ResetCommand do
context 'when there is a ca.pem file' do
let(:files) { { 'ca.pem': 'some file content' } }
- it_behaves_like 'helm commands' do
+ it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS1.squish + "\n" + <<~EOS2
helm reset
@@ -39,29 +38,13 @@ describe Gitlab::Kubernetes::Helm::ResetCommand do
end
end
- describe '#pod_resource' do
- subject { reset_command.pod_resource }
-
- context 'rbac is enabled' do
- let(:rbac) { true }
-
- it 'generates a pod that uses the tiller serviceAccountName' do
- expect(subject.spec.serviceAccountName).to eq('tiller')
- end
- end
-
- context 'rbac is not enabled' do
- let(:rbac) { false }
-
- it 'generates a pod that uses the default serviceAccountName' do
- expect(subject.spec.serviceAcccountName).to be_nil
- end
- end
- end
-
describe '#pod_name' do
subject { reset_command.pod_name }
it { is_expected.to eq('uninstall-helm') }
end
+
+ it_behaves_like 'helm command' do
+ let(:command) { reset_command }
+ end
end
diff --git a/spec/lib/gitlab/visibility_level_spec.rb b/spec/lib/gitlab/visibility_level_spec.rb
index 16a05af2216..a249b3a235e 100644
--- a/spec/lib/gitlab/visibility_level_spec.rb
+++ b/spec/lib/gitlab/visibility_level_spec.rb
@@ -96,6 +96,30 @@ describe Gitlab::VisibilityLevel do
end
end
+ describe '.restricted_level?, .non_restricted_level?, and .public_level_restricted?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:visibility_levels, :expected_status) do
+ nil | false
+ [Gitlab::VisibilityLevel::PRIVATE] | false
+ [Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL] | false
+ [Gitlab::VisibilityLevel::PUBLIC] | true
+ [Gitlab::VisibilityLevel::PUBLIC, Gitlab::VisibilityLevel::INTERNAL] | true
+ end
+
+ with_them do
+ before do
+ stub_application_setting(restricted_visibility_levels: visibility_levels)
+ end
+
+ it 'returns the expected status' do
+ expect(described_class.restricted_level?(Gitlab::VisibilityLevel::PUBLIC)).to eq(expected_status)
+ expect(described_class.non_restricted_level?(Gitlab::VisibilityLevel::PUBLIC)).to eq(!expected_status)
+ expect(described_class.public_visibility_restricted?).to eq(expected_status)
+ end
+ end
+ end
+
describe '#visibility_level_decreased?' do
let(:project) { create(:project, :internal) }
diff --git a/spec/models/jira_import_state_spec.rb b/spec/models/jira_import_state_spec.rb
index 4d91bf25b5e..99f9e035205 100644
--- a/spec/models/jira_import_state_spec.rb
+++ b/spec/models/jira_import_state_spec.rb
@@ -124,6 +124,7 @@ describe JiraImportState do
jira_import.schedule
expect(jira_import.jid).to eq('some-job-id')
+ expect(jira_import.scheduled_at).to be_within(1.second).of(Time.now)
end
end
diff --git a/spec/requests/api/graphql/project/jira_import_spec.rb b/spec/requests/api/graphql/project/jira_import_spec.rb
index 2e631fb56ba..e063068eb1a 100644
--- a/spec/requests/api/graphql/project/jira_import_spec.rb
+++ b/spec/requests/api/graphql/project/jira_import_spec.rb
@@ -18,6 +18,7 @@ describe 'query Jira import data' do
jiraImports {
nodes {
jiraProjectKey
+ createdAt
scheduledAt
scheduledBy {
username
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 07e7a48d8c4..95d64ee8124 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -60,14 +60,14 @@ describe API::Settings, 'Settings' do
default_projects_limit: 3,
default_project_creation: 2,
password_authentication_enabled_for_web: false,
- repository_storages: ['custom'],
+ repository_storages: 'custom',
plantuml_enabled: true,
plantuml_url: 'http://plantuml.example.com',
sourcegraph_enabled: true,
sourcegraph_url: 'https://sourcegraph.com',
sourcegraph_public_only: false,
default_snippet_visibility: 'internal',
- restricted_visibility_levels: ['public'],
+ restricted_visibility_levels: 'public',
default_artifacts_expire_in: '2 days',
help_page_text: 'custom help text',
help_page_hide_commercial_content: true,
@@ -89,7 +89,9 @@ describe API::Settings, 'Settings' do
push_event_hooks_limit: 2,
push_event_activities_limit: 2,
snippet_size_limit: 5,
- issues_create_limit: 300
+ issues_create_limit: 300,
+ disabled_oauth_sign_in_sources: 'unknown',
+ import_sources: 'github,bitbucket'
}
expect(response).to have_gitlab_http_status(:ok)
@@ -127,6 +129,8 @@ describe API::Settings, 'Settings' do
expect(json_response['push_event_activities_limit']).to eq(2)
expect(json_response['snippet_size_limit']).to eq(5)
expect(json_response['issues_create_limit']).to eq(300)
+ expect(json_response['disabled_oauth_sign_in_sources']).to eq([])
+ expect(json_response['import_sources']).to match_array(%w(github bitbucket))
end
end
diff --git a/spec/rubocop/cop/api/grape_api_instance_spec.rb b/spec/rubocop/cop/api/grape_api_instance_spec.rb
new file mode 100644
index 00000000000..0199377f104
--- /dev/null
+++ b/spec/rubocop/cop/api/grape_api_instance_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rubocop'
+require_relative '../../../support/helpers/expect_offense'
+require_relative '../../../../rubocop/cop/api/grape_api_instance'
+
+describe RuboCop::Cop::API::GrapeAPIInstance do
+ include CopHelper
+ include ExpectOffense
+
+ subject(:cop) { described_class.new }
+
+ it 'adds an offense when inheriting from Grape::API' do
+ inspect_source(<<~CODE.strip_indent)
+ class SomeAPI < Grape::API
+ end
+ CODE
+
+ expect(cop.offenses.size).to eq(1)
+ end
+
+ it 'does not add an offense when inheriting from Grape::API::Instance' do
+ inspect_source(<<~CODE.strip_indent)
+ class SomeAPI < Grape::API::Instance
+ end
+ CODE
+
+ expect(cop.offenses.size).to be_zero
+ end
+end
diff --git a/spec/rubocop/cop/api/grape_array_missing_coerce_spec.rb b/spec/rubocop/cop/api/grape_array_missing_coerce_spec.rb
new file mode 100644
index 00000000000..8252e07837d
--- /dev/null
+++ b/spec/rubocop/cop/api/grape_array_missing_coerce_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rubocop'
+require_relative '../../../support/helpers/expect_offense'
+require_relative '../../../../rubocop/cop/api/grape_array_missing_coerce'
+
+describe RuboCop::Cop::API::GrapeArrayMissingCoerce do
+ include CopHelper
+ include ExpectOffense
+
+ subject(:cop) { described_class.new }
+
+ it 'adds an offense with a required parameter' do
+ inspect_source(<<~CODE.strip_indent)
+ class SomeAPI < Grape::API::Instance
+ params do
+ requires :values, type: Array[String]
+ end
+ end
+ CODE
+
+ expect(cop.offenses.size).to eq(1)
+ end
+
+ it 'adds an offense with an optional parameter' do
+ inspect_source(<<~CODE.strip_indent)
+ class SomeAPI < Grape::API::Instance
+ params do
+ optional :values, type: Array[String]
+ end
+ end
+ CODE
+
+ expect(cop.offenses.size).to eq(1)
+ end
+
+ it 'does not add an offense' do
+ inspect_source(<<~CODE.strip_indent)
+ class SomeAPI < Grape::API::Instance
+ params do
+ requires :values, type: Array[String], coerce_with: ->(val) { val.split(',').map(&:strip) }
+ requires :milestone, type: String, desc: 'Milestone title'
+ optional :assignee_id, types: [Integer, String], integer_none_any: true,
+ desc: 'Return issues which are assigned to the user with the given ID'
+ end
+ end
+ CODE
+
+ expect(cop.offenses.size).to be_zero
+ end
+
+ it 'does not add an offense for unrelated classes' do
+ inspect_source(<<~CODE.strip_indent)
+ class SomeClass
+ params do
+ requires :values, type: Array[String]
+ end
+ end
+ CODE
+
+ expect(cop.offenses.size).to be_zero
+ end
+end
diff --git a/spec/rubocop/cop/code_reuse/worker_spec.rb b/spec/rubocop/cop/code_reuse/worker_spec.rb
index 97acaeb7643..9005b5a0611 100644
--- a/spec/rubocop/cop/code_reuse/worker_spec.rb
+++ b/spec/rubocop/cop/code_reuse/worker_spec.rb
@@ -31,7 +31,7 @@ describe RuboCop::Cop::CodeReuse::Worker do
.and_return(true)
expect_offense(<<~SOURCE)
- class Foo < Grape::API
+ class Foo < Grape::API::Instance
resource :projects do
get '/' do
FooWorker.perform_async
diff --git a/spec/rubocop/cop/gitlab/json_spec.rb b/spec/rubocop/cop/gitlab/json_spec.rb
new file mode 100644
index 00000000000..d64f60c8583
--- /dev/null
+++ b/spec/rubocop/cop/gitlab/json_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../../rubocop/cop/gitlab/json'
+
+describe RuboCop::Cop::Gitlab::Json do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ shared_examples('registering call offense') do |options|
+ let(:offending_lines) { options[:offending_lines] }
+
+ it 'registers an offense when the class calls JSON' do
+ inspect_source(source)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(offending_lines.size)
+ expect(cop.offenses.map(&:line)).to eq(offending_lines)
+ end
+ end
+ end
+
+ context 'when JSON is called' do
+ it_behaves_like 'registering call offense', offending_lines: [3] do
+ let(:source) do
+ <<~RUBY
+ class Foo
+ def bar
+ JSON.parse('{ "foo": "bar" }')
+ end
+ end
+ RUBY
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/helm_commands_shared_examples.rb b/spec/support/shared_examples/helm_commands_shared_examples.rb
new file mode 100644
index 00000000000..f0624fbf29f
--- /dev/null
+++ b/spec/support/shared_examples/helm_commands_shared_examples.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+shared_examples 'helm command generator' do
+ describe '#generate_script' do
+ let(:helm_setup) do
+ <<~EOS
+ set -xeo pipefail
+ EOS
+ end
+
+ it 'returns appropriate command' do
+ expect(subject.generate_script.strip).to eq((helm_setup + commands).strip)
+ end
+ end
+end
+
+shared_examples 'helm command' do
+ describe '#rbac?' do
+ subject { command.rbac? }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#pod_resource' do
+ subject { command.pod_resource }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it { is_expected.to be_an_instance_of ::Kubeclient::Resource }
+
+ it 'generates a pod that uses the tiller serviceAccountName' do
+ expect(subject.spec.serviceAccountName).to eq('tiller')
+ end
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it { is_expected.to be_an_instance_of ::Kubeclient::Resource }
+
+ it 'generates a pod that uses the default serviceAccountName' do
+ expect(subject.spec.serviceAcccountName).to be_nil
+ end
+ end
+ end
+
+ describe '#config_map_resource' do
+ subject { command.config_map_resource }
+
+ let(:metadata) do
+ {
+ name: "values-content-configuration-#{command.name}",
+ namespace: 'gitlab-managed-apps',
+ labels: { name: "values-content-configuration-#{command.name}" }
+ }
+ end
+
+ let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: command.files) }
+
+ it 'returns a KubeClient resource with config map content for the application' do
+ is_expected.to eq(resource)
+ end
+ end
+
+ describe '#service_account_resource' do
+ let(:resource) do
+ Kubeclient::Resource.new(metadata: { name: 'tiller', namespace: 'gitlab-managed-apps' })
+ end
+
+ subject { command.service_account_resource }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it 'generates a Kubeclient resource for the tiller ServiceAccount' do
+ is_expected.to eq(resource)
+ end
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it 'generates nothing' do
+ is_expected.to be_nil
+ end
+ end
+ end
+
+ describe '#cluster_role_binding_resource' do
+ let(:resource) do
+ Kubeclient::Resource.new(
+ metadata: { name: 'tiller-admin' },
+ roleRef: { apiGroup: 'rbac.authorization.k8s.io', kind: 'ClusterRole', name: 'cluster-admin' },
+ subjects: [{ kind: 'ServiceAccount', name: 'tiller', namespace: 'gitlab-managed-apps' }]
+ )
+ end
+
+ subject(:cluster_role_binding_resource) { command.cluster_role_binding_resource }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it 'generates a Kubeclient resource for the ClusterRoleBinding for tiller' do
+ is_expected.to eq(resource)
+ end
+
+ it 'binds the account in #service_account_resource' do
+ expect(cluster_role_binding_resource.subjects.first.name).to eq(command.service_account_resource.metadata.name)
+ end
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it 'generates nothing' do
+ is_expected.to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/helm_generated_script_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/helm_generated_script_shared_examples.rb
deleted file mode 100644
index bbf8a946f8b..00000000000
--- a/spec/support/shared_examples/lib/gitlab/helm_generated_script_shared_examples.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'helm commands' do
- describe '#generate_script' do
- let(:helm_setup) do
- <<~EOS
- set -xeo pipefail
- EOS
- end
-
- it 'returns appropriate command' do
- expect(subject.generate_script.strip).to eq((helm_setup + commands).strip)
- end
- end
-end