diff options
Diffstat (limited to 'spec/javascripts/clusters')
5 files changed, 675 insertions, 0 deletions
diff --git a/spec/javascripts/clusters/clusters_bundle_spec.js b/spec/javascripts/clusters/clusters_bundle_spec.js new file mode 100644 index 00000000000..027e8001053 --- /dev/null +++ b/spec/javascripts/clusters/clusters_bundle_spec.js @@ -0,0 +1,257 @@ +import Clusters from '~/clusters/clusters_bundle'; +import { + APPLICATION_INSTALLABLE, + APPLICATION_INSTALLING, + APPLICATION_INSTALLED, + REQUEST_LOADING, + REQUEST_SUCCESS, + REQUEST_FAILURE, +} from '~/clusters/constants'; +import getSetTimeoutPromise from '../helpers/set_timeout_promise_helper'; + +describe('Clusters', () => { + let cluster; + preloadFixtures('clusters/show_cluster.html.raw'); + + beforeEach(() => { + loadFixtures('clusters/show_cluster.html.raw'); + cluster = new Clusters(); + }); + + afterEach(() => { + cluster.destroy(); + }); + + describe('toggle', () => { + it('should update the button and the input field on click', () => { + cluster.toggleButton.click(); + + expect( + cluster.toggleButton.classList, + ).not.toContain('checked'); + + expect( + cluster.toggleInput.getAttribute('value'), + ).toEqual('false'); + }); + }); + + 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_INSTALLABLE, title: 'Helm Tiller' }, + }); + + expect(document.querySelector('.js-cluster-application-notice .flash-text')).toBeNull(); + }); + + it('shows an alert when something gets newly installed', () => { + cluster.checkForNewInstalls({ + ...INITIAL_APP_MAP, + helm: { status: APPLICATION_INSTALLING, title: 'Helm Tiller' }, + }, { + ...INITIAL_APP_MAP, + helm: { status: APPLICATION_INSTALLED, title: 'Helm Tiller' }, + }); + + expect(document.querySelector('.js-cluster-application-notice .flash-text')).toBeDefined(); + expect(document.querySelector('.js-cluster-application-notice .flash-text').textContent.trim()).toEqual('Helm Tiller was successfully installed on your cluster'); + }); + + it('shows an alert when multiple things gets newly installed', () => { + cluster.checkForNewInstalls({ + ...INITIAL_APP_MAP, + helm: { status: APPLICATION_INSTALLING, title: 'Helm Tiller' }, + ingress: { status: APPLICATION_INSTALLABLE, title: 'Ingress' }, + }, { + ...INITIAL_APP_MAP, + helm: { status: APPLICATION_INSTALLED, title: 'Helm Tiller' }, + ingress: { status: APPLICATION_INSTALLED, title: 'Ingress' }, + }); + + expect(document.querySelector('.js-cluster-application-notice .flash-text')).toBeDefined(); + expect(document.querySelector('.js-cluster-application-notice .flash-text').textContent.trim()).toEqual('Helm Tiller, Ingress was successfully installed on your 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', () => { + 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', (done) => { + spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); + expect(cluster.store.state.applications.helm.requestStatus).toEqual(null); + + cluster.installApplication('helm'); + + expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_LOADING); + expect(cluster.store.state.applications.helm.requestReason).toEqual(null); + expect(cluster.service.installApplication).toHaveBeenCalledWith('helm'); + + getSetTimeoutPromise() + .then(() => { + expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_SUCCESS); + expect(cluster.store.state.applications.helm.requestReason).toEqual(null); + }) + .then(done) + .catch(done.fail); + }); + + it('tries to install ingress', (done) => { + spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); + expect(cluster.store.state.applications.ingress.requestStatus).toEqual(null); + + cluster.installApplication('ingress'); + + expect(cluster.store.state.applications.ingress.requestStatus).toEqual(REQUEST_LOADING); + expect(cluster.store.state.applications.ingress.requestReason).toEqual(null); + expect(cluster.service.installApplication).toHaveBeenCalledWith('ingress'); + + getSetTimeoutPromise() + .then(() => { + expect(cluster.store.state.applications.ingress.requestStatus).toEqual(REQUEST_SUCCESS); + expect(cluster.store.state.applications.ingress.requestReason).toEqual(null); + }) + .then(done) + .catch(done.fail); + }); + + it('tries to install runner', (done) => { + spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); + expect(cluster.store.state.applications.runner.requestStatus).toEqual(null); + + cluster.installApplication('runner'); + + expect(cluster.store.state.applications.runner.requestStatus).toEqual(REQUEST_LOADING); + expect(cluster.store.state.applications.runner.requestReason).toEqual(null); + expect(cluster.service.installApplication).toHaveBeenCalledWith('runner'); + + getSetTimeoutPromise() + .then(() => { + expect(cluster.store.state.applications.runner.requestStatus).toEqual(REQUEST_SUCCESS); + expect(cluster.store.state.applications.runner.requestReason).toEqual(null); + }) + .then(done) + .catch(done.fail); + }); + + 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('helm'); + + expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_LOADING); + 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); + }); + }); +}); diff --git a/spec/javascripts/clusters/components/application_row_spec.js b/spec/javascripts/clusters/components/application_row_spec.js new file mode 100644 index 00000000000..e671c18e1a5 --- /dev/null +++ b/spec/javascripts/clusters/components/application_row_spec.js @@ -0,0 +1,237 @@ +import Vue from 'vue'; +import eventHub from '~/clusters/event_hub'; +import { + APPLICATION_NOT_INSTALLABLE, + APPLICATION_SCHEDULED, + APPLICATION_INSTALLABLE, + APPLICATION_INSTALLING, + APPLICATION_INSTALLED, + APPLICATION_ERROR, + REQUEST_LOADING, + REQUEST_SUCCESS, + 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 disabled "Install" when APPLICATION_NOT_INSTALLABLE', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_NOT_INSTALLABLE, + }); + + expect(vm.installButtonLabel).toEqual('Install'); + expect(vm.installButtonLoading).toEqual(false); + expect(vm.installButtonDisabled).toEqual(true); + }); + + it('has enabled "Install" when APPLICATION_INSTALLABLE', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_INSTALLABLE, + }); + + expect(vm.installButtonLabel).toEqual('Install'); + expect(vm.installButtonLoading).toEqual(false); + expect(vm.installButtonDisabled).toEqual(false); + }); + + it('has loading "Installing" when APPLICATION_SCHEDULED', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_SCHEDULED, + }); + + expect(vm.installButtonLabel).toEqual('Installing'); + expect(vm.installButtonLoading).toEqual(true); + expect(vm.installButtonDisabled).toEqual(true); + }); + + it('has loading "Installing" when APPLICATION_INSTALLING', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_INSTALLING, + }); + + expect(vm.installButtonLabel).toEqual('Installing'); + expect(vm.installButtonLoading).toEqual(true); + expect(vm.installButtonDisabled).toEqual(true); + }); + + it('has disabled "Installed" when APPLICATION_INSTALLED', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_INSTALLED, + }); + + expect(vm.installButtonLabel).toEqual('Installed'); + expect(vm.installButtonLoading).toEqual(false); + expect(vm.installButtonDisabled).toEqual(true); + }); + + it('has enabled "Install" when APPLICATION_ERROR', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_ERROR, + }); + + expect(vm.installButtonLabel).toEqual('Install'); + expect(vm.installButtonLoading).toEqual(false); + expect(vm.installButtonDisabled).toEqual(false); + }); + + it('has loading "Install" when REQUEST_LOADING', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_INSTALLABLE, + requestStatus: REQUEST_LOADING, + }); + + expect(vm.installButtonLabel).toEqual('Install'); + expect(vm.installButtonLoading).toEqual(true); + expect(vm.installButtonDisabled).toEqual(true); + }); + + it('has disabled "Install" when REQUEST_SUCCESS', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_INSTALLABLE, + requestStatus: REQUEST_SUCCESS, + }); + + expect(vm.installButtonLabel).toEqual('Install'); + expect(vm.installButtonLoading).toEqual(false); + expect(vm.installButtonDisabled).toEqual(true); + }); + + it('has enabled "Install" when REQUEST_FAILURE (so you can try installing again)', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_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_INSTALLABLE, + }); + const installButton = vm.$el.querySelector('.js-cluster-application-install-button'); + + installButton.click(); + + expect(eventHub.$emit).toHaveBeenCalledWith('installApplication', DEFAULT_APPLICATION_STATE.id); + }); + + it('clicking disabled install button emits nothing', () => { + spyOn(eventHub, '$emit'); + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_INSTALLING, + }); + const installButton = vm.$el.querySelector('.js-cluster-application-install-button'); + + expect(vm.installButtonDisabled).toEqual(true); + + installButton.click(); + + expect(eventHub.$emit).not.toHaveBeenCalled(); + }); + }); + + 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_ERROR', () => { + const statusReason = 'We broke it 0.0'; + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_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_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 new file mode 100644 index 00000000000..7460da031c4 --- /dev/null +++ b/spec/javascripts/clusters/components/applications_spec.js @@ -0,0 +1,42 @@ +import Vue from 'vue'; +import applications from '~/clusters/components/applications.vue'; +import mountComponent from '../../helpers/vue_mount_component_helper'; + +describe('Applications', () => { + let vm; + let Applications; + + beforeEach(() => { + Applications = Vue.extend(applications); + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('', () => { + beforeEach(() => { + vm = mountComponent(Applications, { + applications: { + helm: { title: 'Helm Tiller' }, + ingress: { title: 'Ingress' }, + runner: { title: 'GitLab Runner' }, + }, + }); + }); + + it('renders a row for Helm Tiller', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-helm')).toBeDefined(); + }); + + it('renders a row for Ingress', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-ingress')).toBeDefined(); + }); + + /* * / + it('renders a row for GitLab Runner', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-runner')).toBeDefined(); + }); + /* */ + }); +}); diff --git a/spec/javascripts/clusters/services/mock_data.js b/spec/javascripts/clusters/services/mock_data.js new file mode 100644 index 00000000000..af6b6a73819 --- /dev/null +++ b/spec/javascripts/clusters/services/mock_data.js @@ -0,0 +1,50 @@ +import { + APPLICATION_INSTALLABLE, + APPLICATION_INSTALLING, + APPLICATION_ERROR, +} 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_INSTALLABLE, + status_reason: null, + }, { + name: 'ingress', + status: APPLICATION_ERROR, + status_reason: 'Cannot connect', + }, { + name: 'runner', + status: APPLICATION_INSTALLING, + status_reason: null, + }], + }, + }, + }, + POST: { + '/gitlab-org/gitlab-shell/clusters/1/applications/helm': { }, + '/gitlab-org/gitlab-shell/clusters/1/applications/ingress': { }, + '/gitlab-org/gitlab-shell/clusters/1/applications/runner': { }, + }, +}; + +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, +}; + +export { + CLUSTERS_MOCK_DATA, + DEFAULT_APPLICATION_STATE, +}; diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js new file mode 100644 index 00000000000..cb8b3d38e2e --- /dev/null +++ b/spec/javascripts/clusters/stores/clusters_store_spec.js @@ -0,0 +1,89 @@ +import ClustersStore from '~/clusters/stores/clusters_store'; +import { APPLICATION_INSTALLING } 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_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, + status: mockResponseData.status, + statusReason: mockResponseData.status_reason, + 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, + }, + runner: { + title: 'GitLab Runner', + status: mockResponseData.applications[2].status, + statusReason: mockResponseData.applications[2].status_reason, + requestStatus: null, + requestReason: null, + }, + }, + }); + }); + }); +}); |