diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index 70af333a0dd..bc2e71b99f2 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -353,8 +353,10 @@ export default class Clusters {
saveKnativeDomain(data) {
const appId = data.id;
- this.store.updateAppProperty(appId, 'status', APPLICATION_STATUS.UPDATING);
- this.service.updateApplication(appId, data.params);
+ this.store.updateApplication(appId);
+ this.service.updateApplication(appId, data.params).catch(() => {
+ this.store.notifyUpdateFailure(appId);
+ });
}
setKnativeHostname(data) {
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index 73760da9b98..2d129245d37 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -15,6 +15,7 @@ import prometheusLogo from 'images/cluster_app_logos/prometheus.png';
import { s__, sprintf } from '../../locale';
import applicationRow from './application_row.vue';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
+import KnativeDomainEditor from './knative_domain_editor.vue';
import { CLUSTER_TYPE, APPLICATION_STATUS, INGRESS } from '../constants';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import eventHub from '~/clusters/event_hub';
@@ -25,6 +26,7 @@ export default {
clipboardButton,
LoadingButton,
GlLoadingIcon,
+ KnativeDomainEditor,
},
props: {
type: {
@@ -154,64 +156,21 @@ export default {
knative() {
return this.applications.knative;
},
- knativeInstalled() {
- return (
- this.knative.status === APPLICATION_STATUS.INSTALLED ||
- this.knativeUpgrading ||
- this.knativeUpgradeFailed ||
- this.knative.status === APPLICATION_STATUS.UPDATED
- );
- },
- knativeUpgrading() {
- return (
- this.knative.status === APPLICATION_STATUS.UPDATING ||
- this.knative.status === APPLICATION_STATUS.SCHEDULED
- );
- },
- knativeUpgradeFailed() {
- return this.knative.status === APPLICATION_STATUS.UPDATE_ERRORED;
- },
- knativeExternalEndpoint() {
- return this.knative.externalIp || this.knative.externalHostname;
- },
- knativeDescription() {
- return sprintf(
- _.escape(
- s__(
- `ClusterIntegration|Installing Knative may incur additional costs. Learn more about %{pricingLink}.`,
- ),
- ),
- {
- pricingLink: `
- ${_.escape(s__('ClusterIntegration|pricing'))} `,
- },
- false,
- );
- },
- canUpdateKnativeEndpoint() {
- return this.knativeExternalEndpoint && !this.knativeUpgradeFailed && !this.knativeUpgrading;
- },
- knativeHostname: {
- get() {
- return this.knative.hostname;
- },
- set(hostname) {
- eventHub.$emit('setKnativeHostname', {
- id: 'knative',
- hostname,
- });
- },
- },
},
created() {
this.helmInstallIllustration = helmInstallIllustration;
},
methods: {
- saveKnativeDomain() {
+ saveKnativeDomain(hostname) {
eventHub.$emit('saveKnativeDomain', {
id: 'knative',
- params: { hostname: this.knative.hostname },
+ params: { hostname },
+ });
+ },
+ setKnativeHostname(hostname) {
+ eventHub.$emit('setKnativeHostname', {
+ id: 'knative',
+ hostname,
});
},
},
@@ -318,9 +277,9 @@ export default {
generated endpoint in order to access
your application after it has been deployed.`)
}}
- {{
- __('More information')
- }}
+
+ {{ __('More information') }}
+
@@ -330,9 +289,9 @@ export default {
the process of being assigned. Please check your Kubernetes
cluster or Quotas on Google Kubernetes Engine if it takes a long time.`)
}}
- {{
- __('More information')
- }}
+
+ {{ __('More information') }}
+
@@ -361,9 +320,9 @@ export default {
diff --git a/app/assets/javascripts/clusters/components/knative_domain_editor.vue b/app/assets/javascripts/clusters/components/knative_domain_editor.vue
new file mode 100644
index 00000000000..480228619a5
--- /dev/null
+++ b/app/assets/javascripts/clusters/components/knative_domain_editor.vue
@@ -0,0 +1,150 @@
+
+
+
+
+
+ {{ s__('ClusterIntegration|Something went wrong while updating Knative domain name.') }}
+
+
+
+
+
+ {{ s__('ClusterIntegration|Knative Domain Name:') }}
+
+
+
+
+
+
+
+
+ {{
+ s__(
+ `ClusterIntegration|To access your application after deployment, point a wildcard DNS to the Knative Endpoint.`,
+ )
+ }}
+
+ {{ __('More information') }}
+
+
+
+
+ {{
+ s__(`ClusterIntegration|The endpoint is in
+ the process of being assigned. Please check your Kubernetes
+ cluster or Quotas on Google Kubernetes Engine if it takes a long time.`)
+ }}
+
+
+
+
+
+
diff --git a/spec/frontend/clusters/components/applications_spec.js b/spec/frontend/clusters/components/applications_spec.js
index 8bcf02f0a34..221ebb143be 100644
--- a/spec/frontend/clusters/components/applications_spec.js
+++ b/spec/frontend/clusters/components/applications_spec.js
@@ -1,9 +1,11 @@
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';
+import eventHub from '~/clusters/event_hub';
+import { shallowMount } from '@vue/test-utils';
+import KnativeDomainEditor from '~/clusters/components/knative_domain_editor.vue';
describe('Applications', () => {
let vm;
@@ -277,73 +279,48 @@ describe('Applications', () => {
});
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);
+ const propsData = {
+ applications: {
+ ...APPLICATIONS_MOCK_STATE,
+ knative: {
+ title: 'Knative',
+ hostname: 'example.com',
+ status: 'installed',
+ externalIp: '1.1.1.1',
+ installed: true,
+ },
+ },
+ };
+ const newHostname = 'newhostname.com';
+ let wrapper;
+ let knativeDomainEditor;
- expect(vm.$el.querySelector('.js-knative-endpoint').value).toEqual('1.1.1.1');
+ beforeEach(() => {
+ wrapper = shallowMount(Applications, { propsData });
+ jest.spyOn(eventHub, '$emit');
- expect(
- vm.$el
- .querySelector('.js-knative-endpoint-clipboard-btn')
- .getAttribute('data-clipboard-text'),
- ).toEqual('1.1.1.1');
- });
+ knativeDomainEditor = wrapper.find(KnativeDomainEditor);
+ });
- 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,
- );
- });
+ afterEach(() => {
+ wrapper.destroy();
+ });
- it('renders an update/save Knative domain button', () => {
- expect(vm.$el.querySelector('.js-knative-save-domain-button')).not.toBe(null);
- });
+ it('emits saveKnativeDomain event when knative domain editor emits save event', () => {
+ knativeDomainEditor.vm.$emit('save', newHostname);
- 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' },
- });
- });
+ expect(eventHub.$emit).toHaveBeenCalledWith('saveKnativeDomain', {
+ id: 'knative',
+ params: { hostname: newHostname },
});
+ });
- 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',
- },
- },
- });
+ it('emits setKnativeHostname event when knative domain editor emits change event', () => {
+ wrapper.find(KnativeDomainEditor).vm.$emit('set', newHostname);
- expect(vm.$el.querySelector('.js-knative-ip-loading-icon')).not.toBe(null);
- expect(vm.$el.querySelector('.js-no-knative-endpoint-message')).not.toBe(null);
- });
+ expect(eventHub.$emit).toHaveBeenCalledWith('setKnativeHostname', {
+ id: 'knative',
+ hostname: newHostname,
});
});
});
diff --git a/spec/frontend/clusters/components/knative_domain_editor_spec.js b/spec/frontend/clusters/components/knative_domain_editor_spec.js
new file mode 100644
index 00000000000..242b5701f8b
--- /dev/null
+++ b/spec/frontend/clusters/components/knative_domain_editor_spec.js
@@ -0,0 +1,141 @@
+import { shallowMount } from '@vue/test-utils';
+import KnativeDomainEditor from '~/clusters/components/knative_domain_editor.vue';
+import LoadingButton from '~/vue_shared/components/loading_button.vue';
+import { APPLICATION_STATUS } from '~/clusters/constants';
+
+const { UPDATING } = APPLICATION_STATUS;
+
+describe('KnativeDomainEditor', () => {
+ let wrapper;
+ let knative;
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(KnativeDomainEditor, {
+ propsData: { ...props },
+ });
+ };
+
+ beforeEach(() => {
+ knative = {
+ title: 'Knative',
+ hostname: 'example.com',
+ installed: true,
+ };
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('knative has an assigned IP address', () => {
+ beforeEach(() => {
+ knative.externalIp = '1.1.1.1';
+ createComponent({ knative });
+ });
+
+ it('renders ip address with a clipboard button', () => {
+ expect(wrapper.find('.js-knative-endpoint').exists()).toBe(true);
+ expect(wrapper.find('.js-knative-endpoint').element.value).toEqual(knative.externalIp);
+ });
+
+ it('displays ip address clipboard button', () => {
+ expect(wrapper.find('.js-knative-endpoint-clipboard-btn').attributes('text')).toEqual(
+ knative.externalIp,
+ );
+ });
+
+ it('renders domain & allows editing', () => {
+ const domainNameInput = wrapper.find('.js-knative-domainname');
+
+ expect(domainNameInput.element.value).toEqual(knative.hostname);
+ expect(domainNameInput.attributes('readonly')).toBeFalsy();
+ });
+
+ it('renders an update/save Knative domain button', () => {
+ expect(wrapper.find('.js-knative-save-domain-button').exists()).toBe(true);
+ });
+ });
+
+ describe('knative without ip address', () => {
+ beforeEach(() => {
+ knative.externalIp = null;
+ createComponent({ knative });
+ });
+
+ it('renders an input text with a loading icon', () => {
+ expect(wrapper.find('.js-knative-ip-loading-icon').exists()).toBe(true);
+ });
+
+ it('renders message indicating there is not IP address assigned', () => {
+ expect(wrapper.find('.js-no-knative-endpoint-message').exists()).toBe(true);
+ });
+ });
+
+ describe('clicking save changes button', () => {
+ beforeEach(() => {
+ createComponent({ knative });
+ });
+
+ it('triggers save event and pass current knative hostname', () => {
+ wrapper.find(LoadingButton).vm.$emit('click');
+ expect(wrapper.emitted('save')[0]).toEqual([knative.hostname]);
+ });
+ });
+
+ describe('when knative domain name was saved successfully', () => {
+ beforeEach(() => {
+ createComponent({ knative });
+ });
+
+ it('displays toast indicating a successful update', () => {
+ wrapper.vm.$toast = { show: jest.fn() };
+ wrapper.setProps({ knative: Object.assign({ updateSuccessful: true }, knative) });
+
+ return wrapper.vm.$nextTick(() => {
+ expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(
+ 'Knative domain name was updated successfully.',
+ );
+ });
+ });
+ });
+
+ describe('when knative domain name input changes', () => {
+ it('emits "set" event with updated domain name', () => {
+ const newHostname = 'newhostname.com';
+
+ wrapper.setData({ knativeHostname: newHostname });
+
+ expect(wrapper.emitted('set')[0]).toEqual([newHostname]);
+ });
+ });
+
+ describe('when updating knative domain name failed', () => {
+ beforeEach(() => {
+ createComponent({ knative });
+ });
+
+ it('displays an error banner indicating the operation failure', () => {
+ wrapper.setProps({ knative: { updateFailed: true, ...knative } });
+
+ expect(wrapper.find('.js-cluster-knative-domain-name-failure-message').exists()).toBe(true);
+ });
+ });
+
+ describe(`when knative status is ${UPDATING}`, () => {
+ beforeEach(() => {
+ createComponent({ knative: { status: UPDATING, ...knative } });
+ });
+
+ it('renders loading spinner in save button', () => {
+ expect(wrapper.find(LoadingButton).props('loading')).toBe(true);
+ });
+
+ it('renders disabled save button', () => {
+ expect(wrapper.find(LoadingButton).props('disabled')).toBe(true);
+ });
+
+ it('renders save button with "Saving" label', () => {
+ expect(wrapper.find(LoadingButton).props('label')).toBe('Saving');
+ });
+ });
+});