From be8997ca7c504a9f7fc0821c1b014d9260e00b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Alc=C3=A1ntara?= Date: Mon, 8 Apr 2019 07:31:27 +0000 Subject: Migrate clusters tests to jest Move cluster applications manager tests from karma to jest. Fixes some migration issues related with timeouts, and HTTP request expectations. --- app/assets/javascripts/clusters/clusters_bundle.js | 2 +- .../58981-migrate-clusters-tests-to-jest.yml | 5 + spec/frontend/clusters/clusters_bundle_spec.js | 376 +++++++++++++++++++ .../clusters/components/application_row_spec.js | 406 +++++++++++++++++++++ .../clusters/components/applications_spec.js | 350 ++++++++++++++++++ spec/frontend/clusters/services/mock_data.js | 130 +++++++ .../clusters/stores/clusters_store_spec.js | 142 +++++++ spec/javascripts/clusters/clusters_bundle_spec.js | 360 ------------------ .../clusters/components/application_row_spec.js | 406 --------------------- .../clusters/components/applications_spec.js | 350 ------------------ spec/javascripts/clusters/services/mock_data.js | 130 ------- .../clusters/stores/clusters_store_spec.js | 142 ------- 12 files changed, 1410 insertions(+), 1389 deletions(-) create mode 100644 changelogs/unreleased/58981-migrate-clusters-tests-to-jest.yml create mode 100644 spec/frontend/clusters/clusters_bundle_spec.js create mode 100644 spec/frontend/clusters/components/application_row_spec.js create mode 100644 spec/frontend/clusters/components/applications_spec.js create mode 100644 spec/frontend/clusters/services/mock_data.js create mode 100644 spec/frontend/clusters/stores/clusters_store_spec.js delete mode 100644 spec/javascripts/clusters/clusters_bundle_spec.js delete mode 100644 spec/javascripts/clusters/components/application_row_spec.js delete mode 100644 spec/javascripts/clusters/components/applications_spec.js delete mode 100644 spec/javascripts/clusters/services/mock_data.js delete mode 100644 spec/javascripts/clusters/stores/clusters_store_spec.js diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js index a88e4b7b314..df855261b3c 100644 --- a/app/assets/javascripts/clusters/clusters_bundle.js +++ b/app/assets/javascripts/clusters/clusters_bundle.js @@ -262,7 +262,7 @@ export default class Clusters { this.store.updateAppProperty(appId, 'requestReason', null); this.store.updateAppProperty(appId, 'statusReason', null); - this.service.installApplication(appId, data.params).catch(() => { + return this.service.installApplication(appId, data.params).catch(() => { this.store.updateAppProperty(appId, 'requestStatus', REQUEST_FAILURE); this.store.updateAppProperty( appId, diff --git a/changelogs/unreleased/58981-migrate-clusters-tests-to-jest.yml b/changelogs/unreleased/58981-migrate-clusters-tests-to-jest.yml new file mode 100644 index 00000000000..3df13dbb960 --- /dev/null +++ b/changelogs/unreleased/58981-migrate-clusters-tests-to-jest.yml @@ -0,0 +1,5 @@ +--- +title: Migrate clusters tests to jest +merge_request: 27013 +author: +type: other diff --git a/spec/frontend/clusters/clusters_bundle_spec.js b/spec/frontend/clusters/clusters_bundle_spec.js new file mode 100644 index 00000000000..eea7bd87257 --- /dev/null +++ b/spec/frontend/clusters/clusters_bundle_spec.js @@ -0,0 +1,376 @@ +import Clusters from '~/clusters/clusters_bundle'; +import { + REQUEST_SUBMITTED, + REQUEST_FAILURE, + APPLICATION_STATUS, + INGRESS_DOMAIN_SUFFIX, +} from '~/clusters/constants'; +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; +import { loadHTMLFixture } from 'helpers/fixtures'; +import { setTestTimeout } from 'helpers/timeout'; +import $ from 'jquery'; + +describe('Clusters', () => { + setTestTimeout(500); + + let cluster; + let mock; + + const mockGetClusterStatusRequest = () => { + const { statusPath } = document.querySelector('.js-edit-cluster-form').dataset; + + mock = new MockAdapter(axios); + + mock.onGet(statusPath).reply(200); + }; + + beforeEach(() => { + loadHTMLFixture('clusters/show_cluster.html'); + }); + + beforeEach(() => { + mockGetClusterStatusRequest(); + }); + + beforeEach(() => { + cluster = new Clusters(); + }); + + afterEach(() => { + cluster.destroy(); + mock.restore(); + }); + + describe('toggle', () => { + it('should update the button and the input field on click', done => { + const toggleButton = document.querySelector( + '.js-cluster-enable-toggle-area .js-project-feature-toggle', + ); + const toggleInput = document.querySelector( + '.js-cluster-enable-toggle-area .js-project-feature-toggle-input', + ); + + $(toggleInput).one('trigger-change', () => { + expect(toggleButton.classList).not.toContain('is-checked'); + expect(toggleInput.getAttribute('value')).toEqual('false'); + done(); + }); + + toggleButton.click(); + }); + }); + + describe('showToken', () => { + it('should update token field type', () => { + cluster.showTokenButton.click(); + + expect(cluster.tokenField.getAttribute('type')).toEqual('text'); + + cluster.showTokenButton.click(); + + expect(cluster.tokenField.getAttribute('type')).toEqual('password'); + }); + + it('should update show token button text', () => { + cluster.showTokenButton.click(); + + expect(cluster.showTokenButton.textContent).toEqual('Hide'); + + cluster.showTokenButton.click(); + + expect(cluster.showTokenButton.textContent).toEqual('Show'); + }); + }); + + describe('checkForNewInstalls', () => { + const INITIAL_APP_MAP = { + helm: { status: null, title: 'Helm Tiller' }, + ingress: { status: null, title: 'Ingress' }, + runner: { status: null, title: 'GitLab Runner' }, + }; + + it('does not show alert when things transition from initial null state to something', () => { + cluster.checkForNewInstalls(INITIAL_APP_MAP, { + ...INITIAL_APP_MAP, + helm: { status: APPLICATION_STATUS.INSTALLABLE, title: 'Helm Tiller' }, + }); + + const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text'); + + expect(flashMessage).toBeNull(); + }); + + it('shows an alert when something gets newly installed', () => { + cluster.checkForNewInstalls( + { + ...INITIAL_APP_MAP, + helm: { status: APPLICATION_STATUS.INSTALLING, title: 'Helm Tiller' }, + }, + { + ...INITIAL_APP_MAP, + helm: { status: APPLICATION_STATUS.INSTALLED, title: 'Helm Tiller' }, + }, + ); + + const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text'); + + expect(flashMessage).not.toBeNull(); + expect(flashMessage.textContent.trim()).toEqual( + 'Helm Tiller was successfully installed on your Kubernetes cluster', + ); + }); + + it('shows an alert when multiple things gets newly installed', () => { + cluster.checkForNewInstalls( + { + ...INITIAL_APP_MAP, + helm: { status: APPLICATION_STATUS.INSTALLING, title: 'Helm Tiller' }, + ingress: { status: APPLICATION_STATUS.INSTALLABLE, title: 'Ingress' }, + }, + { + ...INITIAL_APP_MAP, + helm: { status: APPLICATION_STATUS.INSTALLED, title: 'Helm Tiller' }, + ingress: { status: APPLICATION_STATUS.INSTALLED, title: 'Ingress' }, + }, + ); + + const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text'); + + expect(flashMessage).not.toBeNull(); + expect(flashMessage.textContent.trim()).toEqual( + 'Helm Tiller, Ingress was successfully installed on your Kubernetes cluster', + ); + }); + }); + + describe('updateContainer', () => { + describe('when creating cluster', () => { + it('should show the creating container', () => { + cluster.updateContainer(null, 'creating'); + + expect(cluster.creatingContainer.classList.contains('hidden')).toBeFalsy(); + + expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); + }); + + it('should continue to show `creating` banner with subsequent updates of the same status', () => { + cluster.updateContainer('creating', 'creating'); + + expect(cluster.creatingContainer.classList.contains('hidden')).toBeFalsy(); + + expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); + }); + }); + + describe('when cluster is created', () => { + it('should show the success container and fresh the page', () => { + cluster.updateContainer(null, 'created'); + + expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.successContainer.classList.contains('hidden')).toBeFalsy(); + + expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); + }); + + it('should not show a banner when status is already `created`', () => { + cluster.updateContainer('created', 'created'); + + expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); + }); + }); + + describe('when cluster has error', () => { + it('should show the error container', () => { + cluster.updateContainer(null, 'errored', 'this is an error'); + + expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy(); + + expect(cluster.errorReasonContainer.textContent).toContain('this is an error'); + }); + + it('should show `error` banner when previously `creating`', () => { + cluster.updateContainer('creating', 'errored'); + + expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy(); + }); + }); + }); + + describe('installApplication', () => { + it('tries to install helm', () => { + jest.spyOn(cluster.service, 'installApplication').mockResolvedValueOnce(); + + expect(cluster.store.state.applications.helm.requestStatus).toEqual(null); + + cluster.installApplication({ id: 'helm' }); + + expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_SUBMITTED); + expect(cluster.store.state.applications.helm.requestReason).toEqual(null); + expect(cluster.service.installApplication).toHaveBeenCalledWith('helm', undefined); + }); + + it('tries to install ingress', () => { + jest.spyOn(cluster.service, 'installApplication').mockResolvedValueOnce(); + + expect(cluster.store.state.applications.ingress.requestStatus).toEqual(null); + + cluster.installApplication({ id: 'ingress' }); + + expect(cluster.store.state.applications.ingress.requestStatus).toEqual(REQUEST_SUBMITTED); + expect(cluster.store.state.applications.ingress.requestReason).toEqual(null); + expect(cluster.service.installApplication).toHaveBeenCalledWith('ingress', undefined); + }); + + it('tries to install runner', () => { + jest.spyOn(cluster.service, 'installApplication').mockResolvedValueOnce(); + + expect(cluster.store.state.applications.runner.requestStatus).toEqual(null); + + cluster.installApplication({ id: 'runner' }); + + expect(cluster.store.state.applications.runner.requestStatus).toEqual(REQUEST_SUBMITTED); + expect(cluster.store.state.applications.runner.requestReason).toEqual(null); + expect(cluster.service.installApplication).toHaveBeenCalledWith('runner', undefined); + }); + + it('tries to install jupyter', () => { + jest.spyOn(cluster.service, 'installApplication').mockResolvedValueOnce(); + + expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(null); + cluster.installApplication({ + id: 'jupyter', + params: { hostname: cluster.store.state.applications.jupyter.hostname }, + }); + + expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(REQUEST_SUBMITTED); + expect(cluster.store.state.applications.jupyter.requestReason).toEqual(null); + expect(cluster.service.installApplication).toHaveBeenCalledWith('jupyter', { + hostname: cluster.store.state.applications.jupyter.hostname, + }); + }); + + it('sets error request status when the request fails', () => { + jest + .spyOn(cluster.service, 'installApplication') + .mockRejectedValueOnce(new Error('STUBBED ERROR')); + + expect(cluster.store.state.applications.helm.requestStatus).toEqual(null); + + const promise = cluster.installApplication({ id: 'helm' }); + + expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_SUBMITTED); + expect(cluster.store.state.applications.helm.requestReason).toEqual(null); + expect(cluster.service.installApplication).toHaveBeenCalled(); + + return promise.then(() => { + expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_FAILURE); + expect(cluster.store.state.applications.helm.requestReason).toBeDefined(); + }); + }); + }); + + describe('handleSuccess', () => { + beforeEach(() => { + jest.spyOn(cluster.store, 'updateStateFromServer').mockReturnThis(); + jest.spyOn(cluster, 'toggleIngressDomainHelpText').mockReturnThis(); + jest.spyOn(cluster, 'checkForNewInstalls').mockReturnThis(); + jest.spyOn(cluster, 'updateContainer').mockReturnThis(); + + cluster.handleSuccess({ data: {} }); + }); + + it('updates clusters store', () => { + expect(cluster.store.updateStateFromServer).toHaveBeenCalled(); + }); + + it('checks for new installable apps', () => { + expect(cluster.checkForNewInstalls).toHaveBeenCalled(); + }); + + it('toggles ingress domain help text', () => { + expect(cluster.toggleIngressDomainHelpText).toHaveBeenCalled(); + }); + + it('updates message containers', () => { + expect(cluster.updateContainer).toHaveBeenCalled(); + }); + }); + + describe('toggleIngressDomainHelpText', () => { + const { INSTALLED, INSTALLABLE, NOT_INSTALLABLE } = APPLICATION_STATUS; + let ingressPreviousState; + let ingressNewState; + + beforeEach(() => { + ingressPreviousState = { status: INSTALLABLE }; + ingressNewState = { status: INSTALLED, externalIp: '127.0.0.1' }; + }); + + describe(`when ingress application new status is ${INSTALLED}`, () => { + beforeEach(() => { + ingressNewState.status = INSTALLED; + cluster.toggleIngressDomainHelpText(ingressPreviousState, ingressNewState); + }); + + it('displays custom domain help text', () => { + expect(cluster.ingressDomainHelpText.classList.contains('hide')).toEqual(false); + }); + + it('updates ingress external ip address', () => { + expect(cluster.ingressDomainSnippet.textContent).toEqual( + `${ingressNewState.externalIp}${INGRESS_DOMAIN_SUFFIX}`, + ); + }); + }); + + describe(`when ingress application new status is different from ${INSTALLED}`, () => { + it('hides custom domain help text', () => { + ingressNewState.status = NOT_INSTALLABLE; + cluster.ingressDomainHelpText.classList.remove('hide'); + + cluster.toggleIngressDomainHelpText(ingressPreviousState, ingressNewState); + + expect(cluster.ingressDomainHelpText.classList.contains('hide')).toEqual(true); + }); + }); + + describe('when ingress application new status and old status are the same', () => { + it('does not display custom domain help text', () => { + ingressPreviousState.status = INSTALLED; + ingressNewState.status = ingressPreviousState.status; + + cluster.toggleIngressDomainHelpText(ingressPreviousState, ingressNewState); + + expect(cluster.ingressDomainHelpText.classList.contains('hide')).toEqual(true); + }); + }); + + describe(`when ingress new status is ${INSTALLED} and there isn’t an ip assigned`, () => { + it('does not display custom domain help text', () => { + ingressNewState.externalIp = null; + + cluster.toggleIngressDomainHelpText(ingressPreviousState, ingressNewState); + + expect(cluster.ingressDomainHelpText.classList.contains('hide')).toEqual(true); + }); + }); + }); +}); diff --git a/spec/frontend/clusters/components/application_row_spec.js b/spec/frontend/clusters/components/application_row_spec.js new file mode 100644 index 00000000000..b28d0075d06 --- /dev/null +++ b/spec/frontend/clusters/components/application_row_spec.js @@ -0,0 +1,406 @@ +import Vue from 'vue'; +import eventHub from '~/clusters/event_hub'; +import { APPLICATION_STATUS, REQUEST_SUBMITTED, REQUEST_FAILURE } from '~/clusters/constants'; +import applicationRow from '~/clusters/components/application_row.vue'; +import mountComponent from 'helpers/vue_mount_component_helper'; +import { DEFAULT_APPLICATION_STATE } from '../services/mock_data'; + +describe('Application Row', () => { + let vm; + let ApplicationRow; + + beforeEach(() => { + ApplicationRow = Vue.extend(applicationRow); + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('Title', () => { + it('shows title', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + titleLink: null, + }); + const title = vm.$el.querySelector('.js-cluster-application-title'); + + expect(title.tagName).toEqual('SPAN'); + expect(title.textContent.trim()).toEqual(DEFAULT_APPLICATION_STATE.title); + }); + + it('shows title link', () => { + expect(DEFAULT_APPLICATION_STATE.titleLink).toBeDefined(); + + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + }); + const title = vm.$el.querySelector('.js-cluster-application-title'); + + expect(title.tagName).toEqual('A'); + expect(title.textContent.trim()).toEqual(DEFAULT_APPLICATION_STATE.title); + }); + }); + + describe('Install button', () => { + it('has indeterminate state on page load', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: null, + }); + + expect(vm.installButtonLabel).toBeUndefined(); + }); + + it('has install button', () => { + const installationBtn = vm.$el.querySelector('.js-cluster-application-install-button'); + + expect(installationBtn).not.toBe(null); + }); + + it('has disabled "Install" when APPLICATION_STATUS.NOT_INSTALLABLE', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.NOT_INSTALLABLE, + }); + + expect(vm.installButtonLabel).toEqual('Install'); + expect(vm.installButtonLoading).toEqual(false); + expect(vm.installButtonDisabled).toEqual(true); + }); + + it('has enabled "Install" when APPLICATION_STATUS.INSTALLABLE', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.INSTALLABLE, + }); + + expect(vm.installButtonLabel).toEqual('Install'); + expect(vm.installButtonLoading).toEqual(false); + expect(vm.installButtonDisabled).toEqual(false); + }); + + it('has loading "Installing" when APPLICATION_STATUS.SCHEDULED', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.SCHEDULED, + }); + + expect(vm.installButtonLabel).toEqual('Installing'); + expect(vm.installButtonLoading).toEqual(true); + expect(vm.installButtonDisabled).toEqual(true); + }); + + it('has loading "Installing" when APPLICATION_STATUS.INSTALLING', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.INSTALLING, + }); + + expect(vm.installButtonLabel).toEqual('Installing'); + expect(vm.installButtonLoading).toEqual(true); + expect(vm.installButtonDisabled).toEqual(true); + }); + + it('has loading "Installing" when REQUEST_SUBMITTED', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.INSTALLABLE, + requestStatus: REQUEST_SUBMITTED, + }); + + expect(vm.installButtonLabel).toEqual('Installing'); + expect(vm.installButtonLoading).toEqual(true); + expect(vm.installButtonDisabled).toEqual(true); + }); + + it('has disabled "Installed" when APPLICATION_STATUS.INSTALLED', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.INSTALLED, + }); + + expect(vm.installButtonLabel).toEqual('Installed'); + expect(vm.installButtonLoading).toEqual(false); + expect(vm.installButtonDisabled).toEqual(true); + }); + + it('has disabled "Installed" when APPLICATION_STATUS.UPDATING', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.UPDATING, + }); + + expect(vm.installButtonLabel).toEqual('Installed'); + expect(vm.installButtonLoading).toEqual(false); + expect(vm.installButtonDisabled).toEqual(true); + }); + + it('has enabled "Install" when APPLICATION_STATUS.ERROR', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.ERROR, + }); + + expect(vm.installButtonLabel).toEqual('Install'); + expect(vm.installButtonLoading).toEqual(false); + expect(vm.installButtonDisabled).toEqual(false); + }); + + it('has enabled "Install" when REQUEST_FAILURE (so you can try installing again)', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.INSTALLABLE, + requestStatus: REQUEST_FAILURE, + }); + + expect(vm.installButtonLabel).toEqual('Install'); + expect(vm.installButtonLoading).toEqual(false); + expect(vm.installButtonDisabled).toEqual(false); + }); + + it('clicking install button emits event', () => { + jest.spyOn(eventHub, '$emit'); + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.INSTALLABLE, + }); + const installButton = vm.$el.querySelector('.js-cluster-application-install-button'); + + installButton.click(); + + expect(eventHub.$emit).toHaveBeenCalledWith('installApplication', { + id: DEFAULT_APPLICATION_STATE.id, + params: {}, + }); + }); + + it('clicking install button when installApplicationRequestParams are provided emits event', () => { + jest.spyOn(eventHub, '$emit'); + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.INSTALLABLE, + installApplicationRequestParams: { hostname: 'jupyter' }, + }); + const installButton = vm.$el.querySelector('.js-cluster-application-install-button'); + + installButton.click(); + + expect(eventHub.$emit).toHaveBeenCalledWith('installApplication', { + id: DEFAULT_APPLICATION_STATE.id, + params: { hostname: 'jupyter' }, + }); + }); + + it('clicking disabled install button emits nothing', () => { + jest.spyOn(eventHub, '$emit'); + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.INSTALLING, + }); + const installButton = vm.$el.querySelector('.js-cluster-application-install-button'); + + expect(vm.installButtonDisabled).toEqual(true); + + installButton.click(); + + expect(eventHub.$emit).not.toHaveBeenCalled(); + }); + }); + + describe('Upgrade button', () => { + it('has indeterminate state on page load', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: null, + }); + const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button'); + + expect(upgradeBtn).toBe(null); + }); + + it('has enabled "Upgrade" when "upgradeAvailable" is true', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + upgradeAvailable: true, + }); + const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button'); + + expect(upgradeBtn).not.toBe(null); + expect(upgradeBtn.innerHTML).toContain('Upgrade'); + }); + + it('has enabled "Retry update" when APPLICATION_STATUS.UPDATE_ERRORED', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.UPDATE_ERRORED, + }); + const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button'); + + expect(upgradeBtn).not.toBe(null); + expect(vm.upgradeFailed).toBe(true); + expect(upgradeBtn.innerHTML).toContain('Retry update'); + }); + + it('has disabled "Updating" when APPLICATION_STATUS.UPDATING', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.UPDATING, + }); + const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button'); + + expect(upgradeBtn).not.toBe(null); + expect(vm.isUpgrading).toBe(true); + expect(upgradeBtn.innerHTML).toContain('Updating'); + }); + + it('clicking upgrade button emits event', () => { + jest.spyOn(eventHub, '$emit'); + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.UPDATE_ERRORED, + }); + const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button'); + + upgradeBtn.click(); + + expect(eventHub.$emit).toHaveBeenCalledWith('upgradeApplication', { + id: DEFAULT_APPLICATION_STATE.id, + params: {}, + }); + }); + + it('clicking disabled upgrade button emits nothing', () => { + jest.spyOn(eventHub, '$emit'); + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.UPDATING, + }); + const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button'); + + upgradeBtn.click(); + + expect(eventHub.$emit).not.toHaveBeenCalled(); + }); + + it('displays an error message if application upgrade failed', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + title: 'GitLab Runner', + status: APPLICATION_STATUS.UPDATE_ERRORED, + }); + const failureMessage = vm.$el.querySelector( + '.js-cluster-application-upgrade-failure-message', + ); + + expect(failureMessage).not.toBe(null); + expect(failureMessage.innerHTML).toContain( + 'Update failed. Please check the logs and try again.', + ); + }); + }); + + describe('Version', () => { + it('displays a version number if application has been upgraded', () => { + const version = '0.1.45'; + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.UPDATED, + version, + }); + const upgradeDetails = vm.$el.querySelector('.js-cluster-application-upgrade-details'); + const versionEl = vm.$el.querySelector('.js-cluster-application-upgrade-version'); + + expect(upgradeDetails.innerHTML).toContain('Upgraded'); + expect(versionEl).not.toBe(null); + expect(versionEl.innerHTML).toContain(version); + }); + + it('contains a link to the chart repo if application has been upgraded', () => { + const version = '0.1.45'; + const chartRepo = 'https://gitlab.com/charts/gitlab-runner'; + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.UPDATED, + chartRepo, + version, + }); + const versionEl = vm.$el.querySelector('.js-cluster-application-upgrade-version'); + + expect(versionEl.href).toEqual(chartRepo); + expect(versionEl.target).toEqual('_blank'); + }); + + it('does not display a version number if application upgrade failed', () => { + const version = '0.1.45'; + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.UPDATE_ERRORED, + version, + }); + const upgradeDetails = vm.$el.querySelector('.js-cluster-application-upgrade-details'); + const versionEl = vm.$el.querySelector('.js-cluster-application-upgrade-version'); + + expect(upgradeDetails.innerHTML).toContain('failed'); + expect(versionEl).toBe(null); + }); + }); + + describe('Error block', () => { + it('does not show error block when there is no error', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: null, + requestStatus: null, + }); + const generalErrorMessage = vm.$el.querySelector( + '.js-cluster-application-general-error-message', + ); + + expect(generalErrorMessage).toBeNull(); + }); + + it('shows status reason when APPLICATION_STATUS.ERROR', () => { + const statusReason = 'We broke it 0.0'; + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.ERROR, + statusReason, + }); + const generalErrorMessage = vm.$el.querySelector( + '.js-cluster-application-general-error-message', + ); + const statusErrorMessage = vm.$el.querySelector( + '.js-cluster-application-status-error-message', + ); + + expect(generalErrorMessage.textContent.trim()).toEqual( + `Something went wrong while installing ${DEFAULT_APPLICATION_STATE.title}`, + ); + + expect(statusErrorMessage.textContent.trim()).toEqual(statusReason); + }); + + it('shows request reason when REQUEST_FAILURE', () => { + const requestReason = 'We broke thre request 0.0'; + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.INSTALLABLE, + requestStatus: REQUEST_FAILURE, + requestReason, + }); + const generalErrorMessage = vm.$el.querySelector( + '.js-cluster-application-general-error-message', + ); + const requestErrorMessage = vm.$el.querySelector( + '.js-cluster-application-request-error-message', + ); + + expect(generalErrorMessage.textContent.trim()).toEqual( + `Something went wrong while installing ${DEFAULT_APPLICATION_STATE.title}`, + ); + + expect(requestErrorMessage.textContent.trim()).toEqual(requestReason); + }); + }); +}); diff --git a/spec/frontend/clusters/components/applications_spec.js b/spec/frontend/clusters/components/applications_spec.js new file mode 100644 index 00000000000..7c54a27d950 --- /dev/null +++ b/spec/frontend/clusters/components/applications_spec.js @@ -0,0 +1,350 @@ +import Vue from 'vue'; +import applications from '~/clusters/components/applications.vue'; +import { CLUSTER_TYPE } from '~/clusters/constants'; +import eventHub from '~/clusters/event_hub'; +import mountComponent from 'helpers/vue_mount_component_helper'; +import { APPLICATIONS_MOCK_STATE } from '../services/mock_data'; + +describe('Applications', () => { + let vm; + let Applications; + + beforeEach(() => { + Applications = Vue.extend(applications); + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('Project cluster applications', () => { + beforeEach(() => { + vm = mountComponent(Applications, { + applications: APPLICATIONS_MOCK_STATE, + type: CLUSTER_TYPE.PROJECT, + }); + }); + + it('renders a row for Helm Tiller', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-helm')).not.toBeNull(); + }); + + it('renders a row for Ingress', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-ingress')).not.toBeNull(); + }); + + it('renders a row for Cert-Manager', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-cert_manager')).not.toBeNull(); + }); + + it('renders a row for Prometheus', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-prometheus')).not.toBeNull(); + }); + + it('renders a row for GitLab Runner', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-runner')).not.toBeNull(); + }); + + it('renders a row for Jupyter', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-jupyter')).not.toBeNull(); + }); + + it('renders a row for Knative', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-knative')).not.toBeNull(); + }); + }); + + describe('Group cluster applications', () => { + beforeEach(() => { + vm = mountComponent(Applications, { + type: CLUSTER_TYPE.GROUP, + applications: APPLICATIONS_MOCK_STATE, + }); + }); + + it('renders a row for Helm Tiller', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-helm')).not.toBeNull(); + }); + + it('renders a row for Ingress', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-ingress')).not.toBeNull(); + }); + + it('renders a row for Cert-Manager', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-cert_manager')).not.toBeNull(); + }); + + it('renders a row for Prometheus', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-prometheus')).toBeNull(); + }); + + it('renders a row for GitLab Runner', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-runner')).not.toBeNull(); + }); + + it('renders a row for Jupyter', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-jupyter')).toBeNull(); + }); + + it('renders a row for Knative', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-knative')).toBeNull(); + }); + }); + + describe('Ingress application', () => { + describe('when installed', () => { + describe('with ip address', () => { + it('renders ip address with a clipboard button', () => { + vm = mountComponent(Applications, { + applications: { + ...APPLICATIONS_MOCK_STATE, + ingress: { + title: 'Ingress', + status: 'installed', + externalIp: '0.0.0.0', + }, + }, + }); + + expect(vm.$el.querySelector('.js-endpoint').value).toEqual('0.0.0.0'); + + expect( + vm.$el.querySelector('.js-clipboard-btn').getAttribute('data-clipboard-text'), + ).toEqual('0.0.0.0'); + }); + }); + + describe('with hostname', () => { + it('renders hostname with a clipboard button', () => { + vm = mountComponent(Applications, { + applications: { + ingress: { + title: 'Ingress', + status: 'installed', + externalHostname: 'localhost.localdomain', + }, + helm: { title: 'Helm Tiller' }, + cert_manager: { title: 'Cert-Manager' }, + runner: { title: 'GitLab Runner' }, + prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', hostname: '' }, + knative: { title: 'Knative', hostname: '' }, + }, + }); + + expect(vm.$el.querySelector('.js-endpoint').value).toEqual('localhost.localdomain'); + + expect( + vm.$el.querySelector('.js-clipboard-btn').getAttribute('data-clipboard-text'), + ).toEqual('localhost.localdomain'); + }); + }); + + describe('without ip address', () => { + it('renders an input text with a loading icon and an alert text', () => { + vm = mountComponent(Applications, { + applications: { + ...APPLICATIONS_MOCK_STATE, + ingress: { + title: 'Ingress', + status: 'installed', + }, + }, + }); + + expect(vm.$el.querySelector('.js-ingress-ip-loading-icon')).not.toBe(null); + expect(vm.$el.querySelector('.js-no-endpoint-message')).not.toBe(null); + }); + }); + }); + + describe('before installing', () => { + it('does not render the IP address', () => { + vm = mountComponent(Applications, { + applications: APPLICATIONS_MOCK_STATE, + }); + + expect(vm.$el.textContent).not.toContain('Ingress IP Address'); + expect(vm.$el.querySelector('.js-endpoint')).toBe(null); + }); + }); + + describe('Cert-Manager application', () => { + describe('when not installed', () => { + it('renders email & allows editing', () => { + vm = mountComponent(Applications, { + applications: { + ...APPLICATIONS_MOCK_STATE, + cert_manager: { + title: 'Cert-Manager', + email: 'before@example.com', + status: 'installable', + }, + }, + }); + + expect(vm.$el.querySelector('.js-email').value).toEqual('before@example.com'); + expect(vm.$el.querySelector('.js-email').getAttribute('readonly')).toBe(null); + }); + }); + + describe('when installed', () => { + it('renders email in readonly', () => { + vm = mountComponent(Applications, { + applications: { + ...APPLICATIONS_MOCK_STATE, + cert_manager: { + title: 'Cert-Manager', + email: 'after@example.com', + status: 'installed', + }, + }, + }); + + expect(vm.$el.querySelector('.js-email').value).toEqual('after@example.com'); + expect(vm.$el.querySelector('.js-email').getAttribute('readonly')).toEqual('readonly'); + }); + }); + }); + + describe('Jupyter application', () => { + describe('with ingress installed with ip & jupyter installable', () => { + it('renders hostname active input', () => { + vm = mountComponent(Applications, { + applications: { + ...APPLICATIONS_MOCK_STATE, + ingress: { + title: 'Ingress', + status: 'installed', + externalIp: '1.1.1.1', + }, + }, + }); + + expect(vm.$el.querySelector('.js-hostname').getAttribute('readonly')).toEqual(null); + }); + }); + + describe('with ingress installed without external ip', () => { + it('does not render hostname input', () => { + vm = mountComponent(Applications, { + applications: { + ...APPLICATIONS_MOCK_STATE, + ingress: { title: 'Ingress', status: 'installed' }, + }, + }); + + expect(vm.$el.querySelector('.js-hostname')).toBe(null); + }); + }); + + describe('with ingress & jupyter installed', () => { + it('renders readonly input', () => { + vm = mountComponent(Applications, { + applications: { + ...APPLICATIONS_MOCK_STATE, + ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' }, + jupyter: { title: 'JupyterHub', status: 'installed', hostname: '' }, + }, + }); + + expect(vm.$el.querySelector('.js-hostname').getAttribute('readonly')).toEqual('readonly'); + }); + }); + + describe('without ingress installed', () => { + beforeEach(() => { + vm = mountComponent(Applications, { + applications: APPLICATIONS_MOCK_STATE, + }); + }); + + it('does not render input', () => { + expect(vm.$el.querySelector('.js-hostname')).toBe(null); + }); + + it('renders disabled install button', () => { + expect( + vm.$el + .querySelector( + '.js-cluster-application-row-jupyter .js-cluster-application-install-button', + ) + .getAttribute('disabled'), + ).toEqual('disabled'); + }); + }); + }); + }); + + describe('Knative application', () => { + describe('when installed', () => { + describe('with ip address', () => { + const props = { + applications: { + ...APPLICATIONS_MOCK_STATE, + knative: { + title: 'Knative', + hostname: 'example.com', + status: 'installed', + externalIp: '1.1.1.1', + }, + }, + }; + it('renders ip address with a clipboard button', () => { + vm = mountComponent(Applications, props); + + expect(vm.$el.querySelector('.js-knative-endpoint').value).toEqual('1.1.1.1'); + + expect( + vm.$el + .querySelector('.js-knative-endpoint-clipboard-btn') + .getAttribute('data-clipboard-text'), + ).toEqual('1.1.1.1'); + }); + + it('renders domain & allows editing', () => { + expect(vm.$el.querySelector('.js-knative-domainname').value).toEqual('example.com'); + expect(vm.$el.querySelector('.js-knative-domainname').getAttribute('readonly')).toBe( + null, + ); + }); + + it('renders an update/save Knative domain button', () => { + expect(vm.$el.querySelector('.js-knative-save-domain-button')).not.toBe(null); + }); + + it('emits event when clicking Save changes button', () => { + jest.spyOn(eventHub, '$emit'); + vm = mountComponent(Applications, props); + + const saveButton = vm.$el.querySelector('.js-knative-save-domain-button'); + + saveButton.click(); + + expect(eventHub.$emit).toHaveBeenCalledWith('saveKnativeDomain', { + id: 'knative', + params: { hostname: 'example.com' }, + }); + }); + }); + + describe('without ip address', () => { + it('renders an input text with a loading icon and an alert text', () => { + vm = mountComponent(Applications, { + applications: { + ...APPLICATIONS_MOCK_STATE, + knative: { + title: 'Knative', + hostname: 'example.com', + status: 'installed', + }, + }, + }); + + expect(vm.$el.querySelector('.js-knative-ip-loading-icon')).not.toBe(null); + expect(vm.$el.querySelector('.js-no-knative-endpoint-message')).not.toBe(null); + }); + }); + }); + }); +}); diff --git a/spec/frontend/clusters/services/mock_data.js b/spec/frontend/clusters/services/mock_data.js new file mode 100644 index 00000000000..b4d1bb710e0 --- /dev/null +++ b/spec/frontend/clusters/services/mock_data.js @@ -0,0 +1,130 @@ +import { APPLICATION_STATUS } from '~/clusters/constants'; + +const CLUSTERS_MOCK_DATA = { + GET: { + '/gitlab-org/gitlab-shell/clusters/1/status.json': { + data: { + status: 'errored', + status_reason: 'Failed to request to CloudPlatform.', + applications: [ + { + name: 'helm', + status: APPLICATION_STATUS.INSTALLABLE, + status_reason: null, + }, + { + name: 'ingress', + status: APPLICATION_STATUS.ERROR, + status_reason: 'Cannot connect', + external_ip: null, + external_hostname: null, + }, + { + name: 'runner', + status: APPLICATION_STATUS.INSTALLING, + status_reason: null, + }, + { + name: 'prometheus', + status: APPLICATION_STATUS.ERROR, + status_reason: 'Cannot connect', + }, + { + name: 'jupyter', + status: APPLICATION_STATUS.INSTALLING, + status_reason: 'Cannot connect', + }, + { + name: 'knative', + status: APPLICATION_STATUS.INSTALLING, + status_reason: 'Cannot connect', + }, + { + name: 'cert_manager', + status: APPLICATION_STATUS.ERROR, + status_reason: 'Cannot connect', + email: 'test@example.com', + }, + ], + }, + }, + '/gitlab-org/gitlab-shell/clusters/2/status.json': { + data: { + status: 'errored', + status_reason: 'Failed to request to CloudPlatform.', + applications: [ + { + name: 'helm', + status: APPLICATION_STATUS.INSTALLED, + status_reason: null, + }, + { + name: 'ingress', + status: APPLICATION_STATUS.INSTALLED, + status_reason: 'Cannot connect', + external_ip: '1.1.1.1', + external_hostname: null, + }, + { + name: 'runner', + status: APPLICATION_STATUS.INSTALLING, + status_reason: null, + }, + { + name: 'prometheus', + status: APPLICATION_STATUS.ERROR, + status_reason: 'Cannot connect', + }, + { + name: 'jupyter', + status: APPLICATION_STATUS.INSTALLABLE, + status_reason: 'Cannot connect', + }, + { + name: 'knative', + status: APPLICATION_STATUS.INSTALLABLE, + status_reason: 'Cannot connect', + }, + { + name: 'cert_manager', + status: APPLICATION_STATUS.ERROR, + status_reason: 'Cannot connect', + email: 'test@example.com', + }, + ], + }, + }, + }, + POST: { + '/gitlab-org/gitlab-shell/clusters/1/applications/helm': {}, + '/gitlab-org/gitlab-shell/clusters/1/applications/ingress': {}, + '/gitlab-org/gitlab-shell/clusters/1/applications/cert_manager': {}, + '/gitlab-org/gitlab-shell/clusters/1/applications/runner': {}, + '/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': {}, + '/gitlab-org/gitlab-shell/clusters/1/applications/jupyter': {}, + '/gitlab-org/gitlab-shell/clusters/1/applications/knative': {}, + }, +}; + +const DEFAULT_APPLICATION_STATE = { + id: 'some-app', + title: 'My App', + titleLink: 'https://about.gitlab.com/', + description: 'Some description about this interesting application!', + status: null, + statusReason: null, + requestStatus: null, + requestReason: null, +}; + +const APPLICATIONS_MOCK_STATE = { + helm: { title: 'Helm Tiller', status: 'installable' }, + ingress: { title: 'Ingress', status: 'installable' }, + cert_manager: { title: 'Cert-Manager', status: 'installable' }, + runner: { title: 'GitLab Runner' }, + prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', status: 'installable', hostname: '' }, + knative: { title: 'Knative ', status: 'installable', hostname: '' }, +}; + +export { CLUSTERS_MOCK_DATA, DEFAULT_APPLICATION_STATE, APPLICATIONS_MOCK_STATE }; diff --git a/spec/frontend/clusters/stores/clusters_store_spec.js b/spec/frontend/clusters/stores/clusters_store_spec.js new file mode 100644 index 00000000000..161722ec571 --- /dev/null +++ b/spec/frontend/clusters/stores/clusters_store_spec.js @@ -0,0 +1,142 @@ +import ClustersStore from '~/clusters/stores/clusters_store'; +import { APPLICATION_STATUS } from '~/clusters/constants'; +import { CLUSTERS_MOCK_DATA } from '../services/mock_data'; + +describe('Clusters Store', () => { + let store; + + beforeEach(() => { + store = new ClustersStore(); + }); + + describe('updateStatus', () => { + it('should store new status', () => { + expect(store.state.status).toEqual(null); + + const newStatus = 'errored'; + store.updateStatus(newStatus); + + expect(store.state.status).toEqual(newStatus); + }); + }); + + describe('updateStatusReason', () => { + it('should store new reason', () => { + expect(store.state.statusReason).toEqual(null); + + const newReason = 'Something went wrong!'; + store.updateStatusReason(newReason); + + expect(store.state.statusReason).toEqual(newReason); + }); + }); + + describe('updateAppProperty', () => { + it('should store new request status', () => { + expect(store.state.applications.helm.requestStatus).toEqual(null); + + const newStatus = APPLICATION_STATUS.INSTALLING; + store.updateAppProperty('helm', 'requestStatus', newStatus); + + expect(store.state.applications.helm.requestStatus).toEqual(newStatus); + }); + + it('should store new request reason', () => { + expect(store.state.applications.helm.requestReason).toEqual(null); + + const newReason = 'We broke it.'; + store.updateAppProperty('helm', 'requestReason', newReason); + + expect(store.state.applications.helm.requestReason).toEqual(newReason); + }); + }); + + describe('updateStateFromServer', () => { + it('should store new polling data from server', () => { + const mockResponseData = + CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/1/status.json'].data; + store.updateStateFromServer(mockResponseData); + + expect(store.state).toEqual({ + helpPath: null, + ingressHelpPath: null, + status: mockResponseData.status, + statusReason: mockResponseData.status_reason, + rbac: false, + applications: { + helm: { + title: 'Helm Tiller', + status: mockResponseData.applications[0].status, + statusReason: mockResponseData.applications[0].status_reason, + requestStatus: null, + requestReason: null, + }, + ingress: { + title: 'Ingress', + status: mockResponseData.applications[1].status, + statusReason: mockResponseData.applications[1].status_reason, + requestStatus: null, + requestReason: null, + externalIp: null, + externalHostname: null, + }, + runner: { + title: 'GitLab Runner', + status: mockResponseData.applications[2].status, + statusReason: mockResponseData.applications[2].status_reason, + requestStatus: null, + requestReason: null, + version: mockResponseData.applications[2].version, + upgradeAvailable: mockResponseData.applications[2].update_available, + chartRepo: 'https://gitlab.com/charts/gitlab-runner', + }, + prometheus: { + title: 'Prometheus', + status: mockResponseData.applications[3].status, + statusReason: mockResponseData.applications[3].status_reason, + requestStatus: null, + requestReason: null, + }, + jupyter: { + title: 'JupyterHub', + status: mockResponseData.applications[4].status, + statusReason: mockResponseData.applications[4].status_reason, + requestStatus: null, + requestReason: null, + hostname: '', + }, + knative: { + title: 'Knative', + status: mockResponseData.applications[5].status, + statusReason: mockResponseData.applications[5].status_reason, + requestStatus: null, + requestReason: null, + hostname: null, + isEditingHostName: false, + externalIp: null, + externalHostname: null, + }, + cert_manager: { + title: 'Cert-Manager', + status: mockResponseData.applications[6].status, + statusReason: mockResponseData.applications[6].status_reason, + requestStatus: null, + requestReason: null, + email: mockResponseData.applications[6].email, + }, + }, + }); + }); + + it('sets default hostname for jupyter when ingress has a ip address', () => { + const mockResponseData = + CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/2/status.json'].data; + + store.updateStateFromServer(mockResponseData); + + expect(store.state.applications.jupyter.hostname).toEqual( + `jupyter.${store.state.applications.ingress.externalIp}.nip.io`, + ); + }); + }); +}); diff --git a/spec/javascripts/clusters/clusters_bundle_spec.js b/spec/javascripts/clusters/clusters_bundle_spec.js deleted file mode 100644 index 0a98df45b5d..00000000000 --- a/spec/javascripts/clusters/clusters_bundle_spec.js +++ /dev/null @@ -1,360 +0,0 @@ -import Clusters from '~/clusters/clusters_bundle'; -import { - REQUEST_SUBMITTED, - REQUEST_FAILURE, - APPLICATION_STATUS, - INGRESS_DOMAIN_SUFFIX, -} from '~/clusters/constants'; -import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper'; - -describe('Clusters', () => { - let cluster; - preloadFixtures('clusters/show_cluster.html'); - - beforeEach(() => { - loadFixtures('clusters/show_cluster.html'); - cluster = new Clusters(); - }); - - afterEach(() => { - cluster.destroy(); - }); - - describe('toggle', () => { - it('should update the button and the input field on click', done => { - const toggleButton = document.querySelector( - '.js-cluster-enable-toggle-area .js-project-feature-toggle', - ); - const toggleInput = document.querySelector( - '.js-cluster-enable-toggle-area .js-project-feature-toggle-input', - ); - - toggleButton.click(); - - getSetTimeoutPromise() - .then(() => { - expect(toggleButton.classList).not.toContain('is-checked'); - - expect(toggleInput.getAttribute('value')).toEqual('false'); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('showToken', () => { - it('should update token field type', () => { - cluster.showTokenButton.click(); - - expect(cluster.tokenField.getAttribute('type')).toEqual('text'); - - cluster.showTokenButton.click(); - - expect(cluster.tokenField.getAttribute('type')).toEqual('password'); - }); - - it('should update show token button text', () => { - cluster.showTokenButton.click(); - - expect(cluster.showTokenButton.textContent).toEqual('Hide'); - - cluster.showTokenButton.click(); - - expect(cluster.showTokenButton.textContent).toEqual('Show'); - }); - }); - - describe('checkForNewInstalls', () => { - const INITIAL_APP_MAP = { - helm: { status: null, title: 'Helm Tiller' }, - ingress: { status: null, title: 'Ingress' }, - runner: { status: null, title: 'GitLab Runner' }, - }; - - it('does not show alert when things transition from initial null state to something', () => { - cluster.checkForNewInstalls(INITIAL_APP_MAP, { - ...INITIAL_APP_MAP, - helm: { status: APPLICATION_STATUS.INSTALLABLE, title: 'Helm Tiller' }, - }); - - const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text'); - - expect(flashMessage).toBeNull(); - }); - - it('shows an alert when something gets newly installed', () => { - cluster.checkForNewInstalls( - { - ...INITIAL_APP_MAP, - helm: { status: APPLICATION_STATUS.INSTALLING, title: 'Helm Tiller' }, - }, - { - ...INITIAL_APP_MAP, - helm: { status: APPLICATION_STATUS.INSTALLED, title: 'Helm Tiller' }, - }, - ); - - const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text'); - - expect(flashMessage).not.toBeNull(); - expect(flashMessage.textContent.trim()).toEqual( - 'Helm Tiller was successfully installed on your Kubernetes cluster', - ); - }); - - it('shows an alert when multiple things gets newly installed', () => { - cluster.checkForNewInstalls( - { - ...INITIAL_APP_MAP, - helm: { status: APPLICATION_STATUS.INSTALLING, title: 'Helm Tiller' }, - ingress: { status: APPLICATION_STATUS.INSTALLABLE, title: 'Ingress' }, - }, - { - ...INITIAL_APP_MAP, - helm: { status: APPLICATION_STATUS.INSTALLED, title: 'Helm Tiller' }, - ingress: { status: APPLICATION_STATUS.INSTALLED, title: 'Ingress' }, - }, - ); - - const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text'); - - expect(flashMessage).not.toBeNull(); - expect(flashMessage.textContent.trim()).toEqual( - 'Helm Tiller, Ingress was successfully installed on your Kubernetes cluster', - ); - }); - }); - - describe('updateContainer', () => { - describe('when creating cluster', () => { - it('should show the creating container', () => { - cluster.updateContainer(null, 'creating'); - - expect(cluster.creatingContainer.classList.contains('hidden')).toBeFalsy(); - - expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); - - expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); - }); - - it('should continue to show `creating` banner with subsequent updates of the same status', () => { - cluster.updateContainer('creating', 'creating'); - - expect(cluster.creatingContainer.classList.contains('hidden')).toBeFalsy(); - - expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); - - expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); - }); - }); - - describe('when cluster is created', () => { - it('should show the success container and fresh the page', () => { - cluster.updateContainer(null, 'created'); - - expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); - - expect(cluster.successContainer.classList.contains('hidden')).toBeFalsy(); - - expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); - }); - - it('should not show a banner when status is already `created`', () => { - cluster.updateContainer('created', 'created'); - - expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); - - expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); - - expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); - }); - }); - - describe('when cluster has error', () => { - it('should show the error container', () => { - cluster.updateContainer(null, 'errored', 'this is an error'); - - expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); - - expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); - - expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy(); - - expect(cluster.errorReasonContainer.textContent).toContain('this is an error'); - }); - - it('should show `error` banner when previously `creating`', () => { - cluster.updateContainer('creating', 'errored'); - - expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); - - expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); - - expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy(); - }); - }); - }); - - describe('installApplication', () => { - it('tries to install helm', () => { - spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); - - expect(cluster.store.state.applications.helm.requestStatus).toEqual(null); - - cluster.installApplication({ id: 'helm' }); - - expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_SUBMITTED); - expect(cluster.store.state.applications.helm.requestReason).toEqual(null); - expect(cluster.service.installApplication).toHaveBeenCalledWith('helm', undefined); - }); - - it('tries to install ingress', () => { - spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); - - expect(cluster.store.state.applications.ingress.requestStatus).toEqual(null); - - cluster.installApplication({ id: 'ingress' }); - - expect(cluster.store.state.applications.ingress.requestStatus).toEqual(REQUEST_SUBMITTED); - expect(cluster.store.state.applications.ingress.requestReason).toEqual(null); - expect(cluster.service.installApplication).toHaveBeenCalledWith('ingress', undefined); - }); - - it('tries to install runner', () => { - spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); - - expect(cluster.store.state.applications.runner.requestStatus).toEqual(null); - - cluster.installApplication({ id: 'runner' }); - - expect(cluster.store.state.applications.runner.requestStatus).toEqual(REQUEST_SUBMITTED); - expect(cluster.store.state.applications.runner.requestReason).toEqual(null); - expect(cluster.service.installApplication).toHaveBeenCalledWith('runner', undefined); - }); - - it('tries to install jupyter', () => { - spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); - - expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(null); - cluster.installApplication({ - id: 'jupyter', - params: { hostname: cluster.store.state.applications.jupyter.hostname }, - }); - - expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(REQUEST_SUBMITTED); - expect(cluster.store.state.applications.jupyter.requestReason).toEqual(null); - expect(cluster.service.installApplication).toHaveBeenCalledWith('jupyter', { - hostname: cluster.store.state.applications.jupyter.hostname, - }); - }); - - it('sets error request status when the request fails', done => { - spyOn(cluster.service, 'installApplication').and.returnValue( - Promise.reject(new Error('STUBBED ERROR')), - ); - - expect(cluster.store.state.applications.helm.requestStatus).toEqual(null); - - cluster.installApplication({ id: 'helm' }); - - expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_SUBMITTED); - expect(cluster.store.state.applications.helm.requestReason).toEqual(null); - expect(cluster.service.installApplication).toHaveBeenCalled(); - - getSetTimeoutPromise() - .then(() => { - expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_FAILURE); - expect(cluster.store.state.applications.helm.requestReason).toBeDefined(); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('handleSuccess', () => { - beforeEach(() => { - spyOn(cluster.store, 'updateStateFromServer'); - spyOn(cluster, 'toggleIngressDomainHelpText'); - spyOn(cluster, 'checkForNewInstalls'); - spyOn(cluster, 'updateContainer'); - - cluster.handleSuccess({ data: {} }); - }); - - it('updates clusters store', () => { - expect(cluster.store.updateStateFromServer).toHaveBeenCalled(); - }); - - it('checks for new installable apps', () => { - expect(cluster.checkForNewInstalls).toHaveBeenCalled(); - }); - - it('toggles ingress domain help text', () => { - expect(cluster.toggleIngressDomainHelpText).toHaveBeenCalled(); - }); - - it('updates message containers', () => { - expect(cluster.updateContainer).toHaveBeenCalled(); - }); - }); - - describe('toggleIngressDomainHelpText', () => { - const { INSTALLED, INSTALLABLE, NOT_INSTALLABLE } = APPLICATION_STATUS; - let ingressPreviousState; - let ingressNewState; - - beforeEach(() => { - ingressPreviousState = { status: INSTALLABLE }; - ingressNewState = { status: INSTALLED, externalIp: '127.0.0.1' }; - }); - - describe(`when ingress application new status is ${INSTALLED}`, () => { - beforeEach(() => { - ingressNewState.status = INSTALLED; - cluster.toggleIngressDomainHelpText(ingressPreviousState, ingressNewState); - }); - - it('displays custom domain help text', () => { - expect(cluster.ingressDomainHelpText.classList.contains('hide')).toEqual(false); - }); - - it('updates ingress external ip address', () => { - expect(cluster.ingressDomainSnippet.textContent).toEqual( - `${ingressNewState.externalIp}${INGRESS_DOMAIN_SUFFIX}`, - ); - }); - }); - - describe(`when ingress application new status is different from ${INSTALLED}`, () => { - it('hides custom domain help text', () => { - ingressNewState.status = NOT_INSTALLABLE; - cluster.ingressDomainHelpText.classList.remove('hide'); - - cluster.toggleIngressDomainHelpText(ingressPreviousState, ingressNewState); - - expect(cluster.ingressDomainHelpText.classList.contains('hide')).toEqual(true); - }); - }); - - describe('when ingress application new status and old status are the same', () => { - it('does not display custom domain help text', () => { - ingressPreviousState.status = INSTALLED; - ingressNewState.status = ingressPreviousState.status; - - cluster.toggleIngressDomainHelpText(ingressPreviousState, ingressNewState); - - expect(cluster.ingressDomainHelpText.classList.contains('hide')).toEqual(true); - }); - }); - - describe(`when ingress new status is ${INSTALLED} and there isn’t an ip assigned`, () => { - it('does not display custom domain help text', () => { - ingressNewState.externalIp = null; - - cluster.toggleIngressDomainHelpText(ingressPreviousState, ingressNewState); - - expect(cluster.ingressDomainHelpText.classList.contains('hide')).toEqual(true); - }); - }); - }); -}); diff --git a/spec/javascripts/clusters/components/application_row_spec.js b/spec/javascripts/clusters/components/application_row_spec.js deleted file mode 100644 index a2dd4e93daf..00000000000 --- a/spec/javascripts/clusters/components/application_row_spec.js +++ /dev/null @@ -1,406 +0,0 @@ -import Vue from 'vue'; -import eventHub from '~/clusters/event_hub'; -import { APPLICATION_STATUS, REQUEST_SUBMITTED, REQUEST_FAILURE } from '~/clusters/constants'; -import applicationRow from '~/clusters/components/application_row.vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import { DEFAULT_APPLICATION_STATE } from '../services/mock_data'; - -describe('Application Row', () => { - let vm; - let ApplicationRow; - - beforeEach(() => { - ApplicationRow = Vue.extend(applicationRow); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('Title', () => { - it('shows title', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - titleLink: null, - }); - const title = vm.$el.querySelector('.js-cluster-application-title'); - - expect(title.tagName).toEqual('SPAN'); - expect(title.textContent.trim()).toEqual(DEFAULT_APPLICATION_STATE.title); - }); - - it('shows title link', () => { - expect(DEFAULT_APPLICATION_STATE.titleLink).toBeDefined(); - - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - }); - const title = vm.$el.querySelector('.js-cluster-application-title'); - - expect(title.tagName).toEqual('A'); - expect(title.textContent.trim()).toEqual(DEFAULT_APPLICATION_STATE.title); - }); - }); - - describe('Install button', () => { - it('has indeterminate state on page load', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: null, - }); - - expect(vm.installButtonLabel).toBeUndefined(); - }); - - it('has install button', () => { - const installationBtn = vm.$el.querySelector('.js-cluster-application-install-button'); - - expect(installationBtn).not.toBe(null); - }); - - it('has disabled "Install" when APPLICATION_STATUS.NOT_INSTALLABLE', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.NOT_INSTALLABLE, - }); - - expect(vm.installButtonLabel).toEqual('Install'); - expect(vm.installButtonLoading).toEqual(false); - expect(vm.installButtonDisabled).toEqual(true); - }); - - it('has enabled "Install" when APPLICATION_STATUS.INSTALLABLE', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.INSTALLABLE, - }); - - expect(vm.installButtonLabel).toEqual('Install'); - expect(vm.installButtonLoading).toEqual(false); - expect(vm.installButtonDisabled).toEqual(false); - }); - - it('has loading "Installing" when APPLICATION_STATUS.SCHEDULED', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.SCHEDULED, - }); - - expect(vm.installButtonLabel).toEqual('Installing'); - expect(vm.installButtonLoading).toEqual(true); - expect(vm.installButtonDisabled).toEqual(true); - }); - - it('has loading "Installing" when APPLICATION_STATUS.INSTALLING', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.INSTALLING, - }); - - expect(vm.installButtonLabel).toEqual('Installing'); - expect(vm.installButtonLoading).toEqual(true); - expect(vm.installButtonDisabled).toEqual(true); - }); - - it('has loading "Installing" when REQUEST_SUBMITTED', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.INSTALLABLE, - requestStatus: REQUEST_SUBMITTED, - }); - - expect(vm.installButtonLabel).toEqual('Installing'); - expect(vm.installButtonLoading).toEqual(true); - expect(vm.installButtonDisabled).toEqual(true); - }); - - it('has disabled "Installed" when APPLICATION_STATUS.INSTALLED', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.INSTALLED, - }); - - expect(vm.installButtonLabel).toEqual('Installed'); - expect(vm.installButtonLoading).toEqual(false); - expect(vm.installButtonDisabled).toEqual(true); - }); - - it('has disabled "Installed" when APPLICATION_STATUS.UPDATING', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.UPDATING, - }); - - expect(vm.installButtonLabel).toEqual('Installed'); - expect(vm.installButtonLoading).toEqual(false); - expect(vm.installButtonDisabled).toEqual(true); - }); - - it('has enabled "Install" when APPLICATION_STATUS.ERROR', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.ERROR, - }); - - expect(vm.installButtonLabel).toEqual('Install'); - expect(vm.installButtonLoading).toEqual(false); - expect(vm.installButtonDisabled).toEqual(false); - }); - - it('has enabled "Install" when REQUEST_FAILURE (so you can try installing again)', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.INSTALLABLE, - requestStatus: REQUEST_FAILURE, - }); - - expect(vm.installButtonLabel).toEqual('Install'); - expect(vm.installButtonLoading).toEqual(false); - expect(vm.installButtonDisabled).toEqual(false); - }); - - it('clicking install button emits event', () => { - spyOn(eventHub, '$emit'); - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.INSTALLABLE, - }); - const installButton = vm.$el.querySelector('.js-cluster-application-install-button'); - - installButton.click(); - - expect(eventHub.$emit).toHaveBeenCalledWith('installApplication', { - id: DEFAULT_APPLICATION_STATE.id, - params: {}, - }); - }); - - it('clicking install button when installApplicationRequestParams are provided emits event', () => { - spyOn(eventHub, '$emit'); - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.INSTALLABLE, - installApplicationRequestParams: { hostname: 'jupyter' }, - }); - const installButton = vm.$el.querySelector('.js-cluster-application-install-button'); - - installButton.click(); - - expect(eventHub.$emit).toHaveBeenCalledWith('installApplication', { - id: DEFAULT_APPLICATION_STATE.id, - params: { hostname: 'jupyter' }, - }); - }); - - it('clicking disabled install button emits nothing', () => { - spyOn(eventHub, '$emit'); - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.INSTALLING, - }); - const installButton = vm.$el.querySelector('.js-cluster-application-install-button'); - - expect(vm.installButtonDisabled).toEqual(true); - - installButton.click(); - - expect(eventHub.$emit).not.toHaveBeenCalled(); - }); - }); - - describe('Upgrade button', () => { - it('has indeterminate state on page load', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: null, - }); - const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button'); - - expect(upgradeBtn).toBe(null); - }); - - it('has enabled "Upgrade" when "upgradeAvailable" is true', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - upgradeAvailable: true, - }); - const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button'); - - expect(upgradeBtn).not.toBe(null); - expect(upgradeBtn.innerHTML).toContain('Upgrade'); - }); - - it('has enabled "Retry update" when APPLICATION_STATUS.UPDATE_ERRORED', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.UPDATE_ERRORED, - }); - const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button'); - - expect(upgradeBtn).not.toBe(null); - expect(vm.upgradeFailed).toBe(true); - expect(upgradeBtn.innerHTML).toContain('Retry update'); - }); - - it('has disabled "Updating" when APPLICATION_STATUS.UPDATING', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.UPDATING, - }); - const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button'); - - expect(upgradeBtn).not.toBe(null); - expect(vm.isUpgrading).toBe(true); - expect(upgradeBtn.innerHTML).toContain('Updating'); - }); - - it('clicking upgrade button emits event', () => { - spyOn(eventHub, '$emit'); - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.UPDATE_ERRORED, - }); - const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button'); - - upgradeBtn.click(); - - expect(eventHub.$emit).toHaveBeenCalledWith('upgradeApplication', { - id: DEFAULT_APPLICATION_STATE.id, - params: {}, - }); - }); - - it('clicking disabled upgrade button emits nothing', () => { - spyOn(eventHub, '$emit'); - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.UPDATING, - }); - const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button'); - - upgradeBtn.click(); - - expect(eventHub.$emit).not.toHaveBeenCalled(); - }); - - it('displays an error message if application upgrade failed', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - title: 'GitLab Runner', - status: APPLICATION_STATUS.UPDATE_ERRORED, - }); - const failureMessage = vm.$el.querySelector( - '.js-cluster-application-upgrade-failure-message', - ); - - expect(failureMessage).not.toBe(null); - expect(failureMessage.innerHTML).toContain( - 'Update failed. Please check the logs and try again.', - ); - }); - }); - - describe('Version', () => { - it('displays a version number if application has been upgraded', () => { - const version = '0.1.45'; - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.UPDATED, - version, - }); - const upgradeDetails = vm.$el.querySelector('.js-cluster-application-upgrade-details'); - const versionEl = vm.$el.querySelector('.js-cluster-application-upgrade-version'); - - expect(upgradeDetails.innerHTML).toContain('Upgraded'); - expect(versionEl).not.toBe(null); - expect(versionEl.innerHTML).toContain(version); - }); - - it('contains a link to the chart repo if application has been upgraded', () => { - const version = '0.1.45'; - const chartRepo = 'https://gitlab.com/charts/gitlab-runner'; - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.UPDATED, - chartRepo, - version, - }); - const versionEl = vm.$el.querySelector('.js-cluster-application-upgrade-version'); - - expect(versionEl.href).toEqual(chartRepo); - expect(versionEl.target).toEqual('_blank'); - }); - - it('does not display a version number if application upgrade failed', () => { - const version = '0.1.45'; - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.UPDATE_ERRORED, - version, - }); - const upgradeDetails = vm.$el.querySelector('.js-cluster-application-upgrade-details'); - const versionEl = vm.$el.querySelector('.js-cluster-application-upgrade-version'); - - expect(upgradeDetails.innerHTML).toContain('failed'); - expect(versionEl).toBe(null); - }); - }); - - describe('Error block', () => { - it('does not show error block when there is no error', () => { - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: null, - requestStatus: null, - }); - const generalErrorMessage = vm.$el.querySelector( - '.js-cluster-application-general-error-message', - ); - - expect(generalErrorMessage).toBeNull(); - }); - - it('shows status reason when APPLICATION_STATUS.ERROR', () => { - const statusReason = 'We broke it 0.0'; - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.ERROR, - statusReason, - }); - const generalErrorMessage = vm.$el.querySelector( - '.js-cluster-application-general-error-message', - ); - const statusErrorMessage = vm.$el.querySelector( - '.js-cluster-application-status-error-message', - ); - - expect(generalErrorMessage.textContent.trim()).toEqual( - `Something went wrong while installing ${DEFAULT_APPLICATION_STATE.title}`, - ); - - expect(statusErrorMessage.textContent.trim()).toEqual(statusReason); - }); - - it('shows request reason when REQUEST_FAILURE', () => { - const requestReason = 'We broke thre request 0.0'; - vm = mountComponent(ApplicationRow, { - ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.INSTALLABLE, - requestStatus: REQUEST_FAILURE, - requestReason, - }); - const generalErrorMessage = vm.$el.querySelector( - '.js-cluster-application-general-error-message', - ); - const requestErrorMessage = vm.$el.querySelector( - '.js-cluster-application-request-error-message', - ); - - expect(generalErrorMessage.textContent.trim()).toEqual( - `Something went wrong while installing ${DEFAULT_APPLICATION_STATE.title}`, - ); - - expect(requestErrorMessage.textContent.trim()).toEqual(requestReason); - }); - }); -}); diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js deleted file mode 100644 index 0f8153ad493..00000000000 --- a/spec/javascripts/clusters/components/applications_spec.js +++ /dev/null @@ -1,350 +0,0 @@ -import Vue from 'vue'; -import applications from '~/clusters/components/applications.vue'; -import { CLUSTER_TYPE } from '~/clusters/constants'; -import eventHub from '~/clusters/event_hub'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import { APPLICATIONS_MOCK_STATE } from '../services/mock_data'; - -describe('Applications', () => { - let vm; - let Applications; - - beforeEach(() => { - Applications = Vue.extend(applications); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('Project cluster applications', () => { - beforeEach(() => { - vm = mountComponent(Applications, { - applications: APPLICATIONS_MOCK_STATE, - type: CLUSTER_TYPE.PROJECT, - }); - }); - - it('renders a row for Helm Tiller', () => { - expect(vm.$el.querySelector('.js-cluster-application-row-helm')).not.toBeNull(); - }); - - it('renders a row for Ingress', () => { - expect(vm.$el.querySelector('.js-cluster-application-row-ingress')).not.toBeNull(); - }); - - it('renders a row for Cert-Manager', () => { - expect(vm.$el.querySelector('.js-cluster-application-row-cert_manager')).not.toBeNull(); - }); - - it('renders a row for Prometheus', () => { - expect(vm.$el.querySelector('.js-cluster-application-row-prometheus')).not.toBeNull(); - }); - - it('renders a row for GitLab Runner', () => { - expect(vm.$el.querySelector('.js-cluster-application-row-runner')).not.toBeNull(); - }); - - it('renders a row for Jupyter', () => { - expect(vm.$el.querySelector('.js-cluster-application-row-jupyter')).not.toBeNull(); - }); - - it('renders a row for Knative', () => { - expect(vm.$el.querySelector('.js-cluster-application-row-knative')).not.toBeNull(); - }); - }); - - describe('Group cluster applications', () => { - beforeEach(() => { - vm = mountComponent(Applications, { - type: CLUSTER_TYPE.GROUP, - applications: APPLICATIONS_MOCK_STATE, - }); - }); - - it('renders a row for Helm Tiller', () => { - expect(vm.$el.querySelector('.js-cluster-application-row-helm')).not.toBeNull(); - }); - - it('renders a row for Ingress', () => { - expect(vm.$el.querySelector('.js-cluster-application-row-ingress')).not.toBeNull(); - }); - - it('renders a row for Cert-Manager', () => { - expect(vm.$el.querySelector('.js-cluster-application-row-cert_manager')).not.toBeNull(); - }); - - it('renders a row for Prometheus', () => { - expect(vm.$el.querySelector('.js-cluster-application-row-prometheus')).toBeNull(); - }); - - it('renders a row for GitLab Runner', () => { - expect(vm.$el.querySelector('.js-cluster-application-row-runner')).not.toBeNull(); - }); - - it('renders a row for Jupyter', () => { - expect(vm.$el.querySelector('.js-cluster-application-row-jupyter')).toBeNull(); - }); - - it('renders a row for Knative', () => { - expect(vm.$el.querySelector('.js-cluster-application-row-knative')).toBeNull(); - }); - }); - - describe('Ingress application', () => { - describe('when installed', () => { - describe('with ip address', () => { - it('renders ip address with a clipboard button', () => { - vm = mountComponent(Applications, { - applications: { - ...APPLICATIONS_MOCK_STATE, - ingress: { - title: 'Ingress', - status: 'installed', - externalIp: '0.0.0.0', - }, - }, - }); - - expect(vm.$el.querySelector('.js-endpoint').value).toEqual('0.0.0.0'); - - expect( - vm.$el.querySelector('.js-clipboard-btn').getAttribute('data-clipboard-text'), - ).toEqual('0.0.0.0'); - }); - }); - - describe('with hostname', () => { - it('renders hostname with a clipboard button', () => { - vm = mountComponent(Applications, { - applications: { - ingress: { - title: 'Ingress', - status: 'installed', - externalHostname: 'localhost.localdomain', - }, - helm: { title: 'Helm Tiller' }, - cert_manager: { title: 'Cert-Manager' }, - runner: { title: 'GitLab Runner' }, - prometheus: { title: 'Prometheus' }, - jupyter: { title: 'JupyterHub', hostname: '' }, - knative: { title: 'Knative', hostname: '' }, - }, - }); - - expect(vm.$el.querySelector('.js-endpoint').value).toEqual('localhost.localdomain'); - - expect( - vm.$el.querySelector('.js-clipboard-btn').getAttribute('data-clipboard-text'), - ).toEqual('localhost.localdomain'); - }); - }); - - describe('without ip address', () => { - it('renders an input text with a loading icon and an alert text', () => { - vm = mountComponent(Applications, { - applications: { - ...APPLICATIONS_MOCK_STATE, - ingress: { - title: 'Ingress', - status: 'installed', - }, - }, - }); - - expect(vm.$el.querySelector('.js-ingress-ip-loading-icon')).not.toBe(null); - expect(vm.$el.querySelector('.js-no-endpoint-message')).not.toBe(null); - }); - }); - }); - - describe('before installing', () => { - it('does not render the IP address', () => { - vm = mountComponent(Applications, { - applications: APPLICATIONS_MOCK_STATE, - }); - - expect(vm.$el.textContent).not.toContain('Ingress IP Address'); - expect(vm.$el.querySelector('.js-endpoint')).toBe(null); - }); - }); - - describe('Cert-Manager application', () => { - describe('when not installed', () => { - it('renders email & allows editing', () => { - vm = mountComponent(Applications, { - applications: { - ...APPLICATIONS_MOCK_STATE, - cert_manager: { - title: 'Cert-Manager', - email: 'before@example.com', - status: 'installable', - }, - }, - }); - - expect(vm.$el.querySelector('.js-email').value).toEqual('before@example.com'); - expect(vm.$el.querySelector('.js-email').getAttribute('readonly')).toBe(null); - }); - }); - - describe('when installed', () => { - it('renders email in readonly', () => { - vm = mountComponent(Applications, { - applications: { - ...APPLICATIONS_MOCK_STATE, - cert_manager: { - title: 'Cert-Manager', - email: 'after@example.com', - status: 'installed', - }, - }, - }); - - expect(vm.$el.querySelector('.js-email').value).toEqual('after@example.com'); - expect(vm.$el.querySelector('.js-email').getAttribute('readonly')).toEqual('readonly'); - }); - }); - }); - - describe('Jupyter application', () => { - describe('with ingress installed with ip & jupyter installable', () => { - it('renders hostname active input', () => { - vm = mountComponent(Applications, { - applications: { - ...APPLICATIONS_MOCK_STATE, - ingress: { - title: 'Ingress', - status: 'installed', - externalIp: '1.1.1.1', - }, - }, - }); - - expect(vm.$el.querySelector('.js-hostname').getAttribute('readonly')).toEqual(null); - }); - }); - - describe('with ingress installed without external ip', () => { - it('does not render hostname input', () => { - vm = mountComponent(Applications, { - applications: { - ...APPLICATIONS_MOCK_STATE, - ingress: { title: 'Ingress', status: 'installed' }, - }, - }); - - expect(vm.$el.querySelector('.js-hostname')).toBe(null); - }); - }); - - describe('with ingress & jupyter installed', () => { - it('renders readonly input', () => { - vm = mountComponent(Applications, { - applications: { - ...APPLICATIONS_MOCK_STATE, - ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' }, - jupyter: { title: 'JupyterHub', status: 'installed', hostname: '' }, - }, - }); - - expect(vm.$el.querySelector('.js-hostname').getAttribute('readonly')).toEqual('readonly'); - }); - }); - - describe('without ingress installed', () => { - beforeEach(() => { - vm = mountComponent(Applications, { - applications: APPLICATIONS_MOCK_STATE, - }); - }); - - it('does not render input', () => { - expect(vm.$el.querySelector('.js-hostname')).toBe(null); - }); - - it('renders disabled install button', () => { - expect( - vm.$el - .querySelector( - '.js-cluster-application-row-jupyter .js-cluster-application-install-button', - ) - .getAttribute('disabled'), - ).toEqual('disabled'); - }); - }); - }); - }); - - describe('Knative application', () => { - describe('when installed', () => { - describe('with ip address', () => { - const props = { - applications: { - ...APPLICATIONS_MOCK_STATE, - knative: { - title: 'Knative', - hostname: 'example.com', - status: 'installed', - externalIp: '1.1.1.1', - }, - }, - }; - it('renders ip address with a clipboard button', () => { - vm = mountComponent(Applications, props); - - expect(vm.$el.querySelector('.js-knative-endpoint').value).toEqual('1.1.1.1'); - - expect( - vm.$el - .querySelector('.js-knative-endpoint-clipboard-btn') - .getAttribute('data-clipboard-text'), - ).toEqual('1.1.1.1'); - }); - - it('renders domain & allows editing', () => { - expect(vm.$el.querySelector('.js-knative-domainname').value).toEqual('example.com'); - expect(vm.$el.querySelector('.js-knative-domainname').getAttribute('readonly')).toBe( - null, - ); - }); - - it('renders an update/save Knative domain button', () => { - expect(vm.$el.querySelector('.js-knative-save-domain-button')).not.toBe(null); - }); - - it('emits event when clicking Save changes button', () => { - spyOn(eventHub, '$emit'); - vm = mountComponent(Applications, props); - - const saveButton = vm.$el.querySelector('.js-knative-save-domain-button'); - - saveButton.click(); - - expect(eventHub.$emit).toHaveBeenCalledWith('saveKnativeDomain', { - id: 'knative', - params: { hostname: 'example.com' }, - }); - }); - }); - - describe('without ip address', () => { - it('renders an input text with a loading icon and an alert text', () => { - vm = mountComponent(Applications, { - applications: { - ...APPLICATIONS_MOCK_STATE, - knative: { - title: 'Knative', - hostname: 'example.com', - status: 'installed', - }, - }, - }); - - expect(vm.$el.querySelector('.js-knative-ip-loading-icon')).not.toBe(null); - expect(vm.$el.querySelector('.js-no-knative-endpoint-message')).not.toBe(null); - }); - }); - }); - }); -}); diff --git a/spec/javascripts/clusters/services/mock_data.js b/spec/javascripts/clusters/services/mock_data.js deleted file mode 100644 index b4d1bb710e0..00000000000 --- a/spec/javascripts/clusters/services/mock_data.js +++ /dev/null @@ -1,130 +0,0 @@ -import { APPLICATION_STATUS } from '~/clusters/constants'; - -const CLUSTERS_MOCK_DATA = { - GET: { - '/gitlab-org/gitlab-shell/clusters/1/status.json': { - data: { - status: 'errored', - status_reason: 'Failed to request to CloudPlatform.', - applications: [ - { - name: 'helm', - status: APPLICATION_STATUS.INSTALLABLE, - status_reason: null, - }, - { - name: 'ingress', - status: APPLICATION_STATUS.ERROR, - status_reason: 'Cannot connect', - external_ip: null, - external_hostname: null, - }, - { - name: 'runner', - status: APPLICATION_STATUS.INSTALLING, - status_reason: null, - }, - { - name: 'prometheus', - status: APPLICATION_STATUS.ERROR, - status_reason: 'Cannot connect', - }, - { - name: 'jupyter', - status: APPLICATION_STATUS.INSTALLING, - status_reason: 'Cannot connect', - }, - { - name: 'knative', - status: APPLICATION_STATUS.INSTALLING, - status_reason: 'Cannot connect', - }, - { - name: 'cert_manager', - status: APPLICATION_STATUS.ERROR, - status_reason: 'Cannot connect', - email: 'test@example.com', - }, - ], - }, - }, - '/gitlab-org/gitlab-shell/clusters/2/status.json': { - data: { - status: 'errored', - status_reason: 'Failed to request to CloudPlatform.', - applications: [ - { - name: 'helm', - status: APPLICATION_STATUS.INSTALLED, - status_reason: null, - }, - { - name: 'ingress', - status: APPLICATION_STATUS.INSTALLED, - status_reason: 'Cannot connect', - external_ip: '1.1.1.1', - external_hostname: null, - }, - { - name: 'runner', - status: APPLICATION_STATUS.INSTALLING, - status_reason: null, - }, - { - name: 'prometheus', - status: APPLICATION_STATUS.ERROR, - status_reason: 'Cannot connect', - }, - { - name: 'jupyter', - status: APPLICATION_STATUS.INSTALLABLE, - status_reason: 'Cannot connect', - }, - { - name: 'knative', - status: APPLICATION_STATUS.INSTALLABLE, - status_reason: 'Cannot connect', - }, - { - name: 'cert_manager', - status: APPLICATION_STATUS.ERROR, - status_reason: 'Cannot connect', - email: 'test@example.com', - }, - ], - }, - }, - }, - POST: { - '/gitlab-org/gitlab-shell/clusters/1/applications/helm': {}, - '/gitlab-org/gitlab-shell/clusters/1/applications/ingress': {}, - '/gitlab-org/gitlab-shell/clusters/1/applications/cert_manager': {}, - '/gitlab-org/gitlab-shell/clusters/1/applications/runner': {}, - '/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': {}, - '/gitlab-org/gitlab-shell/clusters/1/applications/jupyter': {}, - '/gitlab-org/gitlab-shell/clusters/1/applications/knative': {}, - }, -}; - -const DEFAULT_APPLICATION_STATE = { - id: 'some-app', - title: 'My App', - titleLink: 'https://about.gitlab.com/', - description: 'Some description about this interesting application!', - status: null, - statusReason: null, - requestStatus: null, - requestReason: null, -}; - -const APPLICATIONS_MOCK_STATE = { - helm: { title: 'Helm Tiller', status: 'installable' }, - ingress: { title: 'Ingress', status: 'installable' }, - cert_manager: { title: 'Cert-Manager', status: 'installable' }, - runner: { title: 'GitLab Runner' }, - prometheus: { title: 'Prometheus' }, - jupyter: { title: 'JupyterHub', status: 'installable', hostname: '' }, - knative: { title: 'Knative ', status: 'installable', hostname: '' }, -}; - -export { CLUSTERS_MOCK_DATA, DEFAULT_APPLICATION_STATE, APPLICATIONS_MOCK_STATE }; diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js deleted file mode 100644 index 161722ec571..00000000000 --- a/spec/javascripts/clusters/stores/clusters_store_spec.js +++ /dev/null @@ -1,142 +0,0 @@ -import ClustersStore from '~/clusters/stores/clusters_store'; -import { APPLICATION_STATUS } from '~/clusters/constants'; -import { CLUSTERS_MOCK_DATA } from '../services/mock_data'; - -describe('Clusters Store', () => { - let store; - - beforeEach(() => { - store = new ClustersStore(); - }); - - describe('updateStatus', () => { - it('should store new status', () => { - expect(store.state.status).toEqual(null); - - const newStatus = 'errored'; - store.updateStatus(newStatus); - - expect(store.state.status).toEqual(newStatus); - }); - }); - - describe('updateStatusReason', () => { - it('should store new reason', () => { - expect(store.state.statusReason).toEqual(null); - - const newReason = 'Something went wrong!'; - store.updateStatusReason(newReason); - - expect(store.state.statusReason).toEqual(newReason); - }); - }); - - describe('updateAppProperty', () => { - it('should store new request status', () => { - expect(store.state.applications.helm.requestStatus).toEqual(null); - - const newStatus = APPLICATION_STATUS.INSTALLING; - store.updateAppProperty('helm', 'requestStatus', newStatus); - - expect(store.state.applications.helm.requestStatus).toEqual(newStatus); - }); - - it('should store new request reason', () => { - expect(store.state.applications.helm.requestReason).toEqual(null); - - const newReason = 'We broke it.'; - store.updateAppProperty('helm', 'requestReason', newReason); - - expect(store.state.applications.helm.requestReason).toEqual(newReason); - }); - }); - - describe('updateStateFromServer', () => { - it('should store new polling data from server', () => { - const mockResponseData = - CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/1/status.json'].data; - store.updateStateFromServer(mockResponseData); - - expect(store.state).toEqual({ - helpPath: null, - ingressHelpPath: null, - status: mockResponseData.status, - statusReason: mockResponseData.status_reason, - rbac: false, - applications: { - helm: { - title: 'Helm Tiller', - status: mockResponseData.applications[0].status, - statusReason: mockResponseData.applications[0].status_reason, - requestStatus: null, - requestReason: null, - }, - ingress: { - title: 'Ingress', - status: mockResponseData.applications[1].status, - statusReason: mockResponseData.applications[1].status_reason, - requestStatus: null, - requestReason: null, - externalIp: null, - externalHostname: null, - }, - runner: { - title: 'GitLab Runner', - status: mockResponseData.applications[2].status, - statusReason: mockResponseData.applications[2].status_reason, - requestStatus: null, - requestReason: null, - version: mockResponseData.applications[2].version, - upgradeAvailable: mockResponseData.applications[2].update_available, - chartRepo: 'https://gitlab.com/charts/gitlab-runner', - }, - prometheus: { - title: 'Prometheus', - status: mockResponseData.applications[3].status, - statusReason: mockResponseData.applications[3].status_reason, - requestStatus: null, - requestReason: null, - }, - jupyter: { - title: 'JupyterHub', - status: mockResponseData.applications[4].status, - statusReason: mockResponseData.applications[4].status_reason, - requestStatus: null, - requestReason: null, - hostname: '', - }, - knative: { - title: 'Knative', - status: mockResponseData.applications[5].status, - statusReason: mockResponseData.applications[5].status_reason, - requestStatus: null, - requestReason: null, - hostname: null, - isEditingHostName: false, - externalIp: null, - externalHostname: null, - }, - cert_manager: { - title: 'Cert-Manager', - status: mockResponseData.applications[6].status, - statusReason: mockResponseData.applications[6].status_reason, - requestStatus: null, - requestReason: null, - email: mockResponseData.applications[6].email, - }, - }, - }); - }); - - it('sets default hostname for jupyter when ingress has a ip address', () => { - const mockResponseData = - CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/2/status.json'].data; - - store.updateStateFromServer(mockResponseData); - - expect(store.state.applications.jupyter.hostname).toEqual( - `jupyter.${store.state.applications.ingress.externalIp}.nip.io`, - ); - }); - }); -}); -- cgit v1.2.1