diff options
Diffstat (limited to 'spec/frontend/deploy_keys')
-rw-r--r-- | spec/frontend/deploy_keys/components/action_btn_spec.js | 54 | ||||
-rw-r--r-- | spec/frontend/deploy_keys/components/app_spec.js | 142 | ||||
-rw-r--r-- | spec/frontend/deploy_keys/components/key_spec.js | 161 | ||||
-rw-r--r-- | spec/frontend/deploy_keys/components/keys_panel_spec.js | 63 |
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); + }); +}); |