From 17bae7c797bbf0a78bf8b16e0423a40ee767bf21 Mon Sep 17 00:00:00 2001 From: Chris Baumbauer Date: Mon, 21 Jan 2019 16:00:16 -0800 Subject: Modified Knative list view to provide more details --- .../features/projects/serverless/functions_spec.rb | 9 ++- .../serverless/components/environment_row_spec.js | 81 ++++++++++++++++++++++ .../serverless/components/function_row_spec.js | 33 +++++++++ .../serverless/components/functions_spec.js | 68 ++++++++++++++++++ spec/javascripts/serverless/components/url_spec.js | 28 ++++++++ spec/javascripts/serverless/mock_data.js | 79 +++++++++++++++++++++ .../serverless/stores/serverless_store_spec.js | 36 ++++++++++ 7 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 spec/javascripts/serverless/components/environment_row_spec.js create mode 100644 spec/javascripts/serverless/components/function_row_spec.js create mode 100644 spec/javascripts/serverless/components/functions_spec.js create mode 100644 spec/javascripts/serverless/components/url_spec.js create mode 100644 spec/javascripts/serverless/mock_data.js create mode 100644 spec/javascripts/serverless/stores/serverless_store_spec.js (limited to 'spec') diff --git a/spec/features/projects/serverless/functions_spec.rb b/spec/features/projects/serverless/functions_spec.rb index 766c63725b3..aa71669de98 100644 --- a/spec/features/projects/serverless/functions_spec.rb +++ b/spec/features/projects/serverless/functions_spec.rb @@ -1,6 +1,10 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'Functions', :js do + include KubernetesHelpers + let(:project) { create(:project) } let(:user) { create(:user) } @@ -34,11 +38,14 @@ describe 'Functions', :js do end context 'when the user has a cluster and knative installed and visits the serverless page' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:service) { cluster.platform_kubernetes } let(:knative) { create(:clusters_applications_knative, :installed, cluster: cluster) } let(:project) { knative.cluster.project } before do + stub_kubeclient_knative_services + stub_kubeclient_service_pods visit project_serverless_functions_path(project) end diff --git a/spec/javascripts/serverless/components/environment_row_spec.js b/spec/javascripts/serverless/components/environment_row_spec.js new file mode 100644 index 00000000000..bdf7a714910 --- /dev/null +++ b/spec/javascripts/serverless/components/environment_row_spec.js @@ -0,0 +1,81 @@ +import Vue from 'vue'; + +import environmentRowComponent from '~/serverless/components/environment_row.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import ServerlessStore from '~/serverless/stores/serverless_store'; + +import { mockServerlessFunctions, mockServerlessFunctionsDiffEnv } from '../mock_data'; + +const createComponent = (env, envName) => + mountComponent(Vue.extend(environmentRowComponent), { env, envName }); + +describe('environment row component', () => { + describe('default global cluster case', () => { + let vm; + + beforeEach(() => { + const store = new ServerlessStore(false, '/cluster_path', 'help_path'); + store.updateFunctionsFromServer(mockServerlessFunctions); + vm = createComponent(store.state.functions['*'], '*'); + }); + + it('has the correct envId', () => { + expect(vm.envId).toEqual('env-global'); + vm.$destroy(); + }); + + it('is open by default', () => { + expect(vm.isOpenClass).toEqual({ 'is-open': true }); + vm.$destroy(); + }); + + it('generates correct output', () => { + expect(vm.$el.querySelectorAll('li').length).toEqual(2); + expect(vm.$el.id).toEqual('env-global'); + expect(vm.$el.classList.contains('is-open')).toBe(true); + expect(vm.$el.querySelector('div.title').innerHTML.trim()).toEqual('*'); + + vm.$destroy(); + }); + + it('opens and closes correctly', () => { + expect(vm.isOpen).toBe(true); + + vm.toggleOpen(); + Vue.nextTick(() => { + expect(vm.isOpen).toBe(false); + }); + + vm.$destroy(); + }); + }); + + describe('default named cluster case', () => { + let vm; + + beforeEach(() => { + const store = new ServerlessStore(false, '/cluster_path', 'help_path'); + store.updateFunctionsFromServer(mockServerlessFunctionsDiffEnv); + vm = createComponent(store.state.functions.test, 'test'); + }); + + it('has the correct envId', () => { + expect(vm.envId).toEqual('env-test'); + vm.$destroy(); + }); + + it('is open by default', () => { + expect(vm.isOpenClass).toEqual({ 'is-open': true }); + vm.$destroy(); + }); + + it('generates correct output', () => { + expect(vm.$el.querySelectorAll('li').length).toEqual(1); + expect(vm.$el.id).toEqual('env-test'); + expect(vm.$el.classList.contains('is-open')).toBe(true); + expect(vm.$el.querySelector('div.title').innerHTML.trim()).toEqual('test'); + + vm.$destroy(); + }); + }); +}); diff --git a/spec/javascripts/serverless/components/function_row_spec.js b/spec/javascripts/serverless/components/function_row_spec.js new file mode 100644 index 00000000000..6933a8f6c87 --- /dev/null +++ b/spec/javascripts/serverless/components/function_row_spec.js @@ -0,0 +1,33 @@ +import Vue from 'vue'; + +import functionRowComponent from '~/serverless/components/function_row.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; + +import { mockServerlessFunction } from '../mock_data'; + +const createComponent = func => mountComponent(Vue.extend(functionRowComponent), { func }); + +describe('functionRowComponent', () => { + it('Parses the function details correctly', () => { + const vm = createComponent(mockServerlessFunction); + + expect(vm.$el.querySelector('b').innerHTML).toEqual(mockServerlessFunction.name); + expect(vm.$el.querySelector('span').innerHTML).toEqual(mockServerlessFunction.image); + expect(vm.$el.querySelector('time').getAttribute('data-original-title')).not.toBe(null); + expect(vm.$el.querySelector('div.url-text-field').innerHTML).toEqual( + mockServerlessFunction.url, + ); + + vm.$destroy(); + }); + + it('handles clicks correctly', () => { + const vm = createComponent(mockServerlessFunction); + + expect(vm.checkClass(vm.$el.querySelector('p'))).toBe(true); // check somewhere inside the row + expect(vm.checkClass(vm.$el.querySelector('svg'))).toBe(false); // check a button image + expect(vm.checkClass(vm.$el.querySelector('div.url-text-field'))).toBe(false); // check the url bar + + vm.$destroy(); + }); +}); diff --git a/spec/javascripts/serverless/components/functions_spec.js b/spec/javascripts/serverless/components/functions_spec.js new file mode 100644 index 00000000000..85cfe71281f --- /dev/null +++ b/spec/javascripts/serverless/components/functions_spec.js @@ -0,0 +1,68 @@ +import Vue from 'vue'; + +import functionsComponent from '~/serverless/components/functions.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import ServerlessStore from '~/serverless/stores/serverless_store'; + +import { mockServerlessFunctions } from '../mock_data'; + +const createComponent = ( + functions, + installed = true, + loadingData = true, + hasFunctionData = true, +) => { + const component = Vue.extend(functionsComponent); + + return mountComponent(component, { + functions, + installed, + clustersPath: '/testClusterPath', + helpPath: '/helpPath', + loadingData, + hasFunctionData, + }); +}; + +describe('functionsComponent', () => { + it('should render empty state when Knative is not installed', () => { + const vm = createComponent({}, false); + + expect(vm.$el.querySelector('div.row').classList.contains('js-empty-state')).toBe(true); + expect(vm.$el.querySelector('h4.state-title').innerHTML.trim()).toEqual( + 'Getting started with serverless', + ); + + vm.$destroy(); + }); + + it('should render a loading component', () => { + const vm = createComponent({}); + + expect(vm.$el.querySelector('.gl-responsive-table-row')).not.toBe(null); + expect(vm.$el.querySelector('div.animation-container')).not.toBe(null); + }); + + it('should render empty state when there is no function data', () => { + const vm = createComponent({}, true, false, false); + + expect( + vm.$el.querySelector('.empty-state, .js-empty-state').classList.contains('js-empty-state'), + ).toBe(true); + + expect(vm.$el.querySelector('h4.state-title').innerHTML.trim()).toEqual( + 'No functions available', + ); + + vm.$destroy(); + }); + + it('should render the functions list', () => { + const store = new ServerlessStore(false, '/cluster_path', 'help_path'); + store.updateFunctionsFromServer(mockServerlessFunctions); + const vm = createComponent(store.state.functions, true, false); + + expect(vm.$el.querySelector('div.groups-list-tree-container')).not.toBe(null); + expect(vm.$el.querySelector('#env-global').classList.contains('has-children')).toBe(true); + }); +}); diff --git a/spec/javascripts/serverless/components/url_spec.js b/spec/javascripts/serverless/components/url_spec.js new file mode 100644 index 00000000000..21a879a49bb --- /dev/null +++ b/spec/javascripts/serverless/components/url_spec.js @@ -0,0 +1,28 @@ +import Vue from 'vue'; + +import urlComponent from '~/serverless/components/url.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; + +const createComponent = uri => { + const component = Vue.extend(urlComponent); + + return mountComponent(component, { + uri, + }); +}; + +describe('urlComponent', () => { + it('should render correctly', () => { + const uri = 'http://testfunc.apps.example.com'; + const vm = createComponent(uri); + + expect(vm.$el.classList.contains('clipboard-group')).toBe(true); + expect(vm.$el.querySelector('.js-clipboard-btn').getAttribute('data-clipboard-text')).toEqual( + uri, + ); + + expect(vm.$el.querySelector('.url-text-field').innerHTML).toEqual(uri); + + vm.$destroy(); + }); +}); diff --git a/spec/javascripts/serverless/mock_data.js b/spec/javascripts/serverless/mock_data.js new file mode 100644 index 00000000000..ecd393b174c --- /dev/null +++ b/spec/javascripts/serverless/mock_data.js @@ -0,0 +1,79 @@ +export const mockServerlessFunctions = [ + { + name: 'testfunc1', + namespace: 'tm-example', + environment_scope: '*', + cluster_id: 46, + detail_url: '/testuser/testproj/serverless/functions/*/testfunc1', + podcount: null, + created_at: '2019-02-05T01:01:23Z', + url: 'http://testfunc1.tm-example.apps.example.com', + description: 'A test service', + image: 'knative-test-container-buildtemplate', + }, + { + name: 'testfunc2', + namespace: 'tm-example', + environment_scope: '*', + cluster_id: 46, + detail_url: '/testuser/testproj/serverless/functions/*/testfunc2', + podcount: null, + created_at: '2019-02-05T01:01:23Z', + url: 'http://testfunc2.tm-example.apps.example.com', + description: 'A second test service\nThis one with additional descriptions', + image: 'knative-test-echo-buildtemplate', + }, +]; + +export const mockServerlessFunctionsDiffEnv = [ + { + name: 'testfunc1', + namespace: 'tm-example', + environment_scope: '*', + cluster_id: 46, + detail_url: '/testuser/testproj/serverless/functions/*/testfunc1', + podcount: null, + created_at: '2019-02-05T01:01:23Z', + url: 'http://testfunc1.tm-example.apps.example.com', + description: 'A test service', + image: 'knative-test-container-buildtemplate', + }, + { + name: 'testfunc2', + namespace: 'tm-example', + environment_scope: 'test', + cluster_id: 46, + detail_url: '/testuser/testproj/serverless/functions/*/testfunc2', + podcount: null, + created_at: '2019-02-05T01:01:23Z', + url: 'http://testfunc2.tm-example.apps.example.com', + description: 'A second test service\nThis one with additional descriptions', + image: 'knative-test-echo-buildtemplate', + }, +]; + +export const mockServerlessFunction = { + name: 'testfunc1', + namespace: 'tm-example', + environment_scope: '*', + cluster_id: 46, + detail_url: '/testuser/testproj/serverless/functions/*/testfunc1', + podcount: '3', + created_at: '2019-02-05T01:01:23Z', + url: 'http://testfunc1.tm-example.apps.example.com', + description: 'A test service', + image: 'knative-test-container-buildtemplate', +}; + +export const mockMultilineServerlessFunction = { + name: 'testfunc1', + namespace: 'tm-example', + environment_scope: '*', + cluster_id: 46, + detail_url: '/testuser/testproj/serverless/functions/*/testfunc1', + podcount: '3', + created_at: '2019-02-05T01:01:23Z', + url: 'http://testfunc1.tm-example.apps.example.com', + description: 'testfunc1\nA test service line\\nWith additional services', + image: 'knative-test-container-buildtemplate', +}; diff --git a/spec/javascripts/serverless/stores/serverless_store_spec.js b/spec/javascripts/serverless/stores/serverless_store_spec.js new file mode 100644 index 00000000000..72fd903d7d1 --- /dev/null +++ b/spec/javascripts/serverless/stores/serverless_store_spec.js @@ -0,0 +1,36 @@ +import ServerlessStore from '~/serverless/stores/serverless_store'; +import { mockServerlessFunctions, mockServerlessFunctionsDiffEnv } from '../mock_data'; + +describe('Serverless Functions Store', () => { + let store; + + beforeEach(() => { + store = new ServerlessStore(false, '/cluster_path', 'help_path'); + }); + + describe('#updateFunctionsFromServer', () => { + it('should pass an empty hash object', () => { + store.updateFunctionsFromServer(); + + expect(store.state.functions).toEqual({}); + }); + + it('should group functions to one global environment', () => { + const mockServerlessData = mockServerlessFunctions; + store.updateFunctionsFromServer(mockServerlessData); + + expect(Object.keys(store.state.functions)).toEqual(jasmine.objectContaining(['*'])); + expect(store.state.functions['*'].length).toEqual(2); + }); + + it('should group functions to multiple environments', () => { + const mockServerlessData = mockServerlessFunctionsDiffEnv; + store.updateFunctionsFromServer(mockServerlessData); + + expect(Object.keys(store.state.functions)).toEqual(jasmine.objectContaining(['*'])); + expect(store.state.functions['*'].length).toEqual(1); + expect(store.state.functions.test.length).toEqual(1); + expect(store.state.functions.test[0].name).toEqual('testfunc2'); + }); + }); +}); -- cgit v1.2.3