Merge branch 'knative-list' into 'master'
Modify Serverless Listing See merge request gitlab-org/gitlab-ce!24072
This commit is contained in:
commit
c7c5492cf4
16 changed files with 513 additions and 103 deletions
|
@ -0,0 +1,65 @@
|
|||
<script>
|
||||
import FunctionRow from './function_row.vue';
|
||||
import ItemCaret from '~/groups/components/item_caret.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ItemCaret,
|
||||
FunctionRow,
|
||||
},
|
||||
props: {
|
||||
env: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
envName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isOpen: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
envId() {
|
||||
if (this.envName === '*') {
|
||||
return 'env-global';
|
||||
}
|
||||
|
||||
return `env-${this.envName}`;
|
||||
},
|
||||
isOpenClass() {
|
||||
return {
|
||||
'is-open': this.isOpen,
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleOpen() {
|
||||
this.isOpen = !this.isOpen;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li :id="envId" :class="isOpenClass" class="group-row has-children">
|
||||
<div
|
||||
class="group-row-contents d-flex justify-content-end align-items-center"
|
||||
role="button"
|
||||
@click.stop="toggleOpen"
|
||||
>
|
||||
<div class="folder-toggle-wrap d-flex align-items-center">
|
||||
<item-caret :is-group-open="isOpen" />
|
||||
</div>
|
||||
<div class="group-text flex-grow title namespace-title prepend-left-default">
|
||||
{{ envName }}
|
||||
</div>
|
||||
</div>
|
||||
<ul v-if="isOpen" class="content-list group-list-tree">
|
||||
<function-row v-for="(f, index) in env" :key="f.name" :index="index" :func="f" />
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
|
@ -1,13 +1,11 @@
|
|||
<script>
|
||||
import PodBox from './pod_box.vue';
|
||||
import ClipboardButton from '../../vue_shared/components/clipboard_button.vue';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import Url from './url.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Icon,
|
||||
PodBox,
|
||||
ClipboardButton,
|
||||
Url,
|
||||
},
|
||||
props: {
|
||||
func: {
|
||||
|
@ -36,24 +34,9 @@ export default {
|
|||
<section id="serverless-function-details">
|
||||
<h3>{{ name }}</h3>
|
||||
<div class="append-bottom-default">
|
||||
<div v-for="line in description.split('\n')" :key="line">{{ line }}<br /></div>
|
||||
</div>
|
||||
<div class="clipboard-group append-bottom-default">
|
||||
<div class="label label-monospace">{{ funcUrl }}</div>
|
||||
<clipboard-button
|
||||
:text="String(funcUrl)"
|
||||
:title="s__('ServerlessDetails|Copy URL to clipboard')"
|
||||
class="input-group-text js-clipboard-btn"
|
||||
/>
|
||||
<a
|
||||
:href="funcUrl"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer nofollow"
|
||||
class="input-group-text btn btn-default"
|
||||
>
|
||||
<icon name="external-link" />
|
||||
</a>
|
||||
<div v-for="(line, index) in description.split('\n')" :key="index">{{ line }}</div>
|
||||
</div>
|
||||
<url :uri="funcUrl" />
|
||||
|
||||
<h4>{{ s__('ServerlessDetails|Kubernetes Pods') }}</h4>
|
||||
<div v-if="podCount > 0">
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
<script>
|
||||
import Timeago from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import Url from './url.vue';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Timeago,
|
||||
Url,
|
||||
},
|
||||
props: {
|
||||
func: {
|
||||
|
@ -16,13 +19,18 @@ export default {
|
|||
return this.func.name;
|
||||
},
|
||||
description() {
|
||||
return this.func.description;
|
||||
const desc = this.func.description.split('\n');
|
||||
if (desc.length > 1) {
|
||||
return desc[1];
|
||||
}
|
||||
|
||||
return desc[0];
|
||||
},
|
||||
detailUrl() {
|
||||
return this.func.detail_url;
|
||||
},
|
||||
environment() {
|
||||
return this.func.environment_scope;
|
||||
targetUrl() {
|
||||
return this.func.url;
|
||||
},
|
||||
image() {
|
||||
return this.func.image;
|
||||
|
@ -31,25 +39,34 @@ export default {
|
|||
return this.func.created_at;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
checkClass(element) {
|
||||
if (element.closest('.no-expand') === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
openDetails(e) {
|
||||
if (this.checkClass(e.target)) {
|
||||
visitUrl(this.detailUrl);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gl-responsive-table-row">
|
||||
<div class="table-section section-20 section-wrap">
|
||||
<a :href="detailUrl">{{ name }}</a>
|
||||
<li :id="name" class="group-row">
|
||||
<div class="group-row-contents" role="button" @click="openDetails">
|
||||
<p class="float-right text-right">
|
||||
<span>{{ image }}</span
|
||||
><br />
|
||||
<timeago :time="timestamp" />
|
||||
</p>
|
||||
<b>{{ name }}</b>
|
||||
<div v-for="line in description.split('\n')" :key="line">{{ line }}</div>
|
||||
<url :uri="targetUrl" class="prepend-top-8 no-expand" />
|
||||
</div>
|
||||
<div class="table-section section-10">{{ environment }}</div>
|
||||
<div class="table-section section-40 section-wrap">
|
||||
<span class="line-break">{{ description }}</span>
|
||||
</div>
|
||||
<div class="table-section section-20">{{ image }}</div>
|
||||
<div class="table-section section-10"><timeago :time="timestamp" /></div>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.line-break {
|
||||
white-space: pre;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
<script>
|
||||
import { GlSkeletonLoading } from '@gitlab/ui';
|
||||
import FunctionRow from './function_row.vue';
|
||||
import EnvironmentRow from './environment_row.vue';
|
||||
import EmptyState from './empty_state.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EnvironmentRow,
|
||||
FunctionRow,
|
||||
EmptyState,
|
||||
GlSkeletonLoading,
|
||||
},
|
||||
props: {
|
||||
functions: {
|
||||
type: Array,
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => [],
|
||||
default: () => ({}),
|
||||
},
|
||||
installed: {
|
||||
type: Boolean,
|
||||
|
@ -45,33 +47,21 @@ export default {
|
|||
<section id="serverless-functions">
|
||||
<div v-if="installed">
|
||||
<div v-if="hasFunctionData">
|
||||
<div class="ci-table js-services-list function-element">
|
||||
<div class="gl-responsive-table-row table-row-header" role="row">
|
||||
<div class="table-section section-20" role="rowheader">
|
||||
{{ s__('Serverless|Function') }}
|
||||
</div>
|
||||
<div class="table-section section-10" role="rowheader">
|
||||
{{ s__('Serverless|Cluster Env') }}
|
||||
</div>
|
||||
<div class="table-section section-40" role="rowheader">
|
||||
{{ s__('Serverless|Description') }}
|
||||
</div>
|
||||
<div class="table-section section-20" role="rowheader">
|
||||
{{ s__('Serverless|Runtime') }}
|
||||
</div>
|
||||
<div class="table-section section-10" role="rowheader">
|
||||
{{ s__('Serverless|Last Update') }}
|
||||
</div>
|
||||
<template v-if="loadingData">
|
||||
<div v-for="j in 3" :key="j" class="gl-responsive-table-row"><gl-skeleton-loading /></div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="groups-list-tree-container">
|
||||
<ul class="content-list group-list-tree">
|
||||
<environment-row
|
||||
v-for="(env, index) in functions"
|
||||
:key="index"
|
||||
:env="env"
|
||||
:env-name="index"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
<template v-if="loadingData">
|
||||
<div v-for="j in 3" :key="j" class="gl-responsive-table-row">
|
||||
<gl-skeleton-loading />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<function-row v-for="f in functions" :key="f.name" :func="f" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else class="empty-state js-empty-state">
|
||||
<div class="text-content">
|
||||
|
@ -111,16 +101,3 @@ export default {
|
|||
<empty-state v-else :clusters-path="clustersPath" :help-path="helpPath" />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.top-area {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.function-element {
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
border-bottom-color: rgb(229, 229, 229);
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
</style>
|
||||
|
|
38
app/assets/javascripts/serverless/components/url.vue
Normal file
38
app/assets/javascripts/serverless/components/url.vue
Normal file
|
@ -0,0 +1,38 @@
|
|||
<script>
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
import ClipboardButton from '../../vue_shared/components/clipboard_button.vue';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Icon,
|
||||
GlButton,
|
||||
ClipboardButton,
|
||||
},
|
||||
props: {
|
||||
uri: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="clipboard-group">
|
||||
<div class="url-text-field label label-monospace">{{ uri }}</div>
|
||||
<clipboard-button
|
||||
:text="uri"
|
||||
:title="s__('ServerlessURL|Copy URL to clipboard')"
|
||||
class="input-group-text js-clipboard-btn"
|
||||
/>
|
||||
<gl-button
|
||||
:href="uri"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer nofollow"
|
||||
class="input-group-text btn btn-default"
|
||||
>
|
||||
<icon name="external-link" />
|
||||
</gl-button>
|
||||
</div>
|
||||
</template>
|
|
@ -1,7 +1,7 @@
|
|||
export default class ServerlessStore {
|
||||
constructor(knativeInstalled = false, clustersPath, helpPath) {
|
||||
this.state = {
|
||||
functions: [],
|
||||
functions: {},
|
||||
hasFunctionData: true,
|
||||
loadingData: true,
|
||||
installed: knativeInstalled,
|
||||
|
@ -10,8 +10,13 @@ export default class ServerlessStore {
|
|||
};
|
||||
}
|
||||
|
||||
updateFunctionsFromServer(functions = []) {
|
||||
this.state.functions = functions;
|
||||
updateFunctionsFromServer(upstreamFunctions = []) {
|
||||
this.state.functions = upstreamFunctions.reduce((rv, func) => {
|
||||
const envs = rv;
|
||||
envs[func.environment_scope] = (rv[func.environment_scope] || []).concat([func]);
|
||||
|
||||
return envs;
|
||||
}, {});
|
||||
}
|
||||
|
||||
updateLoadingState(loadingData) {
|
||||
|
|
3
app/assets/stylesheets/pages/serverless.scss
Normal file
3
app/assets/stylesheets/pages/serverless.scss
Normal file
|
@ -0,0 +1,3 @@
|
|||
.url-text-field {
|
||||
cursor: text;
|
||||
}
|
5
changelogs/unreleased/knative-list.yml
Normal file
5
changelogs/unreleased/knative-list.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Modified Knative list view to provide more details
|
||||
merge_request: 24072
|
||||
author: Chris Baumbauer
|
||||
type: changed
|
|
@ -6360,9 +6360,6 @@ msgstr ""
|
|||
msgid "Serverless"
|
||||
msgstr ""
|
||||
|
||||
msgid "ServerlessDetails|Copy URL to clipboard"
|
||||
msgstr ""
|
||||
|
||||
msgid "ServerlessDetails|Kubernetes Pods"
|
||||
msgstr ""
|
||||
|
||||
|
@ -6375,21 +6372,15 @@ msgstr ""
|
|||
msgid "ServerlessDetails|pods in use"
|
||||
msgstr ""
|
||||
|
||||
msgid "ServerlessURL|Copy URL to clipboard"
|
||||
msgstr ""
|
||||
|
||||
msgid "Serverless| In order to start using functions as a service, you must first install Knative on your Kubernetes cluster."
|
||||
msgstr ""
|
||||
|
||||
msgid "Serverless|An error occurred while retrieving serverless components"
|
||||
msgstr ""
|
||||
|
||||
msgid "Serverless|Cluster Env"
|
||||
msgstr ""
|
||||
|
||||
msgid "Serverless|Description"
|
||||
msgstr ""
|
||||
|
||||
msgid "Serverless|Function"
|
||||
msgstr ""
|
||||
|
||||
msgid "Serverless|Getting started with serverless"
|
||||
msgstr ""
|
||||
|
||||
|
@ -6399,18 +6390,12 @@ msgstr ""
|
|||
msgid "Serverless|Install Knative"
|
||||
msgstr ""
|
||||
|
||||
msgid "Serverless|Last Update"
|
||||
msgstr ""
|
||||
|
||||
msgid "Serverless|Learn more about Serverless"
|
||||
msgstr ""
|
||||
|
||||
msgid "Serverless|No functions available"
|
||||
msgstr ""
|
||||
|
||||
msgid "Serverless|Runtime"
|
||||
msgstr ""
|
||||
|
||||
msgid "Serverless|There is currently no function data available from Knative. This could be for a variety of reasons including:"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
33
spec/javascripts/serverless/components/function_row_spec.js
Normal file
33
spec/javascripts/serverless/components/function_row_spec.js
Normal file
|
@ -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();
|
||||
});
|
||||
});
|
68
spec/javascripts/serverless/components/functions_spec.js
Normal file
68
spec/javascripts/serverless/components/functions_spec.js
Normal file
|
@ -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);
|
||||
});
|
||||
});
|
28
spec/javascripts/serverless/components/url_spec.js
Normal file
28
spec/javascripts/serverless/components/url_spec.js
Normal file
|
@ -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();
|
||||
});
|
||||
});
|
79
spec/javascripts/serverless/mock_data.js
Normal file
79
spec/javascripts/serverless/mock_data.js
Normal file
|
@ -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',
|
||||
};
|
36
spec/javascripts/serverless/stores/serverless_store_spec.js
Normal file
36
spec/javascripts/serverless/stores/serverless_store_spec.js
Normal file
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue