summaryrefslogtreecommitdiff
path: root/spec/frontend/deploy_keys
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/deploy_keys')
-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
4 files changed, 420 insertions, 0 deletions
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);
+ });
+});