Commit aacc1cef authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch 'gke-network-and-subnetwork-dropdowns' into 'master'

GKE network and subnetwork dropdowns

This MR implements two dropdown fields used by the GKE cluster configuration form: network and subnetwork. This MR only contains the dropdown component implementations but it does not integrate the dropdowns in the form. The purpose is to keep the size of the MR manageable. 

To test the dropdowns, please use the following integration MR https://gitlab.com/gitlab-org/gitlab/merge_requests/22344

### Screenshots

![Screen_Shot_2020-01-09_at_2.55.28_PM](/uploads/a823f1675816d929c8bfacdc1cd5afb6/Screen_Shot_2020-01-09_at_2.55.28_PM.png)

See merge request gitlab-org/gitlab!22717
parents 97906995 ade45ebb
<script>
import { createNamespacedHelpers, mapState, mapGetters, mapActions } from 'vuex';
import ClusterFormDropdown from '~/create_cluster/components/cluster_form_dropdown.vue';
const { mapState: mapDropdownState } = createNamespacedHelpers('networks');
const { mapActions: mapSubnetworkActions } = createNamespacedHelpers('subnetworks');
export default {
components: {
ClusterFormDropdown,
},
props: {
fieldName: {
type: String,
required: true,
},
},
computed: {
...mapState(['selectedNetwork']),
...mapDropdownState(['items', 'isLoadingItems', 'loadingItemsError']),
...mapGetters(['hasZone', 'projectId', 'region']),
},
methods: {
...mapActions(['setNetwork', 'setSubnetwork']),
...mapSubnetworkActions({ fetchSubnetworks: 'fetchItems' }),
setNetworkAndFetchSubnetworks(network) {
const { projectId: project, region } = this;
this.setSubnetwork('');
this.setNetwork(network);
this.fetchSubnetworks({ project, region, network: network.selfLink });
},
},
};
</script>
<template>
<cluster-form-dropdown
:field-name="fieldName"
:value="selectedNetwork"
:items="items"
:disabled="!hasZone"
:loading="isLoadingItems"
:has-errors="Boolean(loadingItemsError)"
:loading-text="s__('ClusterIntegration|Loading networks')"
:placeholder="s__('ClusterIntergation|Select a network')"
:search-field-placeholder="s__('ClusterIntegration|Search networks')"
:empty-text="s__('ClusterIntegration|No networks found')"
:error-message="s__('ClusterIntegration|Could not load networks')"
:disabled-text="s__('ClusterIntegration|Select a zone to choose a network')"
@input="setNetworkAndFetchSubnetworks"
/>
</template>
<script>
import { createNamespacedHelpers, mapState, mapGetters, mapActions } from 'vuex';
import ClusterFormDropdown from '~/create_cluster/components/cluster_form_dropdown.vue';
const { mapState: mapDropdownState } = createNamespacedHelpers('subnetworks');
export default {
components: {
ClusterFormDropdown,
},
props: {
fieldName: {
type: String,
required: true,
},
},
computed: {
...mapState(['selectedSubnetwork']),
...mapDropdownState(['items', 'isLoadingItems', 'loadingItemsError']),
...mapGetters(['hasNetwork']),
},
methods: {
...mapActions(['setSubnetwork']),
},
};
</script>
<template>
<cluster-form-dropdown
:field-name="fieldName"
:value="selectedSubnetwork"
:items="items"
:disabled="!hasNetwork"
:loading="isLoadingItems"
:has-errors="Boolean(loadingItemsError)"
:loading-text="s__('ClusterIntegration|Loading subnetworks')"
:placeholder="s__('ClusterIntergation|Select a subnetwork')"
:search-field-placeholder="s__('ClusterIntegration|Search subnetworks')"
:empty-text="s__('ClusterIntegration|No subnetworks found')"
:error-message="s__('ClusterIntegration|Could not load subnetworks')"
:disabled-text="s__('ClusterIntegration|Select a network to choose a subnetwork')"
@input="setSubnetwork"
/>
</template>
...@@ -3854,6 +3854,9 @@ msgstr "" ...@@ -3854,6 +3854,9 @@ msgstr ""
msgid "ClusterIntegration|Could not load instance types" msgid "ClusterIntegration|Could not load instance types"
msgstr "" msgstr ""
msgid "ClusterIntegration|Could not load networks"
msgstr ""
msgid "ClusterIntegration|Could not load regions from your AWS account" msgid "ClusterIntegration|Could not load regions from your AWS account"
msgstr "" msgstr ""
...@@ -3863,6 +3866,9 @@ msgstr "" ...@@ -3863,6 +3866,9 @@ msgstr ""
msgid "ClusterIntegration|Could not load subnets for the selected VPC" msgid "ClusterIntegration|Could not load subnets for the selected VPC"
msgstr "" msgstr ""
msgid "ClusterIntegration|Could not load subnetworks"
msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster" msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr "" msgstr ""
...@@ -4127,12 +4133,18 @@ msgstr "" ...@@ -4127,12 +4133,18 @@ msgstr ""
msgid "ClusterIntegration|Loading instance types" msgid "ClusterIntegration|Loading instance types"
msgstr "" msgstr ""
msgid "ClusterIntegration|Loading networks"
msgstr ""
msgid "ClusterIntegration|Loading security groups" msgid "ClusterIntegration|Loading security groups"
msgstr "" msgstr ""
msgid "ClusterIntegration|Loading subnets" msgid "ClusterIntegration|Loading subnets"
msgstr "" msgstr ""
msgid "ClusterIntegration|Loading subnetworks"
msgstr ""
msgid "ClusterIntegration|Machine type" msgid "ClusterIntegration|Machine type"
msgstr "" msgstr ""
...@@ -4157,6 +4169,9 @@ msgstr "" ...@@ -4157,6 +4169,9 @@ msgstr ""
msgid "ClusterIntegration|No machine types matched your search" msgid "ClusterIntegration|No machine types matched your search"
msgstr "" msgstr ""
msgid "ClusterIntegration|No networks found"
msgstr ""
msgid "ClusterIntegration|No projects found" msgid "ClusterIntegration|No projects found"
msgstr "" msgstr ""
...@@ -4172,6 +4187,9 @@ msgstr "" ...@@ -4172,6 +4187,9 @@ msgstr ""
msgid "ClusterIntegration|No subnet found" msgid "ClusterIntegration|No subnet found"
msgstr "" msgstr ""
msgid "ClusterIntegration|No subnetworks found"
msgstr ""
msgid "ClusterIntegration|No zones matched your search" msgid "ClusterIntegration|No zones matched your search"
msgstr "" msgstr ""
...@@ -4268,6 +4286,9 @@ msgstr "" ...@@ -4268,6 +4286,9 @@ msgstr ""
msgid "ClusterIntegration|Search machine types" msgid "ClusterIntegration|Search machine types"
msgstr "" msgstr ""
msgid "ClusterIntegration|Search networks"
msgstr ""
msgid "ClusterIntegration|Search projects" msgid "ClusterIntegration|Search projects"
msgstr "" msgstr ""
...@@ -4280,6 +4301,9 @@ msgstr "" ...@@ -4280,6 +4301,9 @@ msgstr ""
msgid "ClusterIntegration|Search subnets" msgid "ClusterIntegration|Search subnets"
msgstr "" msgstr ""
msgid "ClusterIntegration|Search subnetworks"
msgstr ""
msgid "ClusterIntegration|Search zones" msgid "ClusterIntegration|Search zones"
msgstr "" msgstr ""
...@@ -4298,6 +4322,9 @@ msgstr "" ...@@ -4298,6 +4322,9 @@ msgstr ""
msgid "ClusterIntegration|Select a VPC to use for your EKS Cluster resources. To use a new VPC, first create one on %{startLink}Amazon Web Services %{externalLinkIcon} %{endLink}." msgid "ClusterIntegration|Select a VPC to use for your EKS Cluster resources. To use a new VPC, first create one on %{startLink}Amazon Web Services %{externalLinkIcon} %{endLink}."
msgstr "" msgstr ""
msgid "ClusterIntegration|Select a network to choose a subnetwork"
msgstr ""
msgid "ClusterIntegration|Select a region to choose a Key Pair" msgid "ClusterIntegration|Select a region to choose a Key Pair"
msgstr "" msgstr ""
...@@ -4307,6 +4334,9 @@ msgstr "" ...@@ -4307,6 +4334,9 @@ msgstr ""
msgid "ClusterIntegration|Select a stack to install Crossplane." msgid "ClusterIntegration|Select a stack to install Crossplane."
msgstr "" msgstr ""
msgid "ClusterIntegration|Select a zone to choose a network"
msgstr ""
msgid "ClusterIntegration|Select machine type" msgid "ClusterIntegration|Select machine type"
msgstr "" msgstr ""
...@@ -4484,6 +4514,9 @@ msgstr "" ...@@ -4484,6 +4514,9 @@ msgstr ""
msgid "ClusterIntergation|Select a VPC" msgid "ClusterIntergation|Select a VPC"
msgstr "" msgstr ""
msgid "ClusterIntergation|Select a network"
msgstr ""
msgid "ClusterIntergation|Select a region" msgid "ClusterIntergation|Select a region"
msgstr "" msgstr ""
...@@ -4493,6 +4526,9 @@ msgstr "" ...@@ -4493,6 +4526,9 @@ msgstr ""
msgid "ClusterIntergation|Select a subnet" msgid "ClusterIntergation|Select a subnet"
msgstr "" msgstr ""
msgid "ClusterIntergation|Select a subnetwork"
msgstr ""
msgid "ClusterIntergation|Select an instance type" msgid "ClusterIntergation|Select an instance type"
msgstr "" msgstr ""
......
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import GkeNetworkDropdown from '~/create_cluster/gke_cluster/components/gke_network_dropdown.vue';
import ClusterFormDropdown from '~/create_cluster/components/cluster_form_dropdown.vue';
import createClusterDropdownState from '~/create_cluster/store/cluster_dropdown/state';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('GkeNetworkDropdown', () => {
let wrapper;
let store;
const defaultProps = { fieldName: 'field-name' };
const selectedNetwork = { selfLink: '123456' };
const projectId = '6789';
const region = 'east-1';
const setNetwork = jest.fn();
const setSubnetwork = jest.fn();
const fetchSubnetworks = jest.fn();
const buildStore = ({ clusterDropdownState } = {}) =>
new Vuex.Store({
state: {
selectedNetwork,
},
actions: {
setNetwork,
setSubnetwork,
},
getters: {
hasZone: () => false,
region: () => region,
projectId: () => projectId,
},
modules: {
networks: {
namespaced: true,
state: {
...createClusterDropdownState(),
...(clusterDropdownState || {}),
},
},
subnetworks: {
namespaced: true,
actions: {
fetchItems: fetchSubnetworks,
},
},
},
});
const buildWrapper = (propsData = defaultProps) =>
shallowMount(GkeNetworkDropdown, {
propsData,
store,
localVue,
});
afterEach(() => {
wrapper.destroy();
});
it('sets correct field-name', () => {
const fieldName = 'field-name';
store = buildStore();
wrapper = buildWrapper({ fieldName });
expect(wrapper.find(ClusterFormDropdown).props('fieldName')).toBe(fieldName);
});
it('sets selected network as the dropdown value', () => {
store = buildStore();
wrapper = buildWrapper();
expect(wrapper.find(ClusterFormDropdown).props('value')).toBe(selectedNetwork);
});
it('maps networks store items to the dropdown items property', () => {
const items = [{ name: 'network' }];
store = buildStore({ clusterDropdownState: { items } });
wrapper = buildWrapper();
expect(wrapper.find(ClusterFormDropdown).props('items')).toBe(items);
});
describe('when network dropdown store is loading items', () => {
it('sets network dropdown as loading', () => {
store = buildStore({ clusterDropdownState: { isLoadingItems: true } });
wrapper = buildWrapper();
expect(wrapper.find(ClusterFormDropdown).props('loading')).toBe(true);
});
});
describe('when there is no selected zone', () => {
it('disables the network dropdown', () => {
store = buildStore();
wrapper = buildWrapper();
expect(wrapper.find(ClusterFormDropdown).props('disabled')).toBe(true);
});
});
describe('when an error occurs while loading networks', () => {
it('sets the network dropdown as having errors', () => {
store = buildStore({ clusterDropdownState: { loadingItemsError: new Error() } });
wrapper = buildWrapper();
expect(wrapper.find(ClusterFormDropdown).props('hasErrors')).toBe(true);
});
});
describe('when dropdown emits input event', () => {
beforeEach(() => {
store = buildStore();
wrapper = buildWrapper();
wrapper.find(ClusterFormDropdown).vm.$emit('input', selectedNetwork);
});
it('cleans selected subnetwork', () => {
expect(setSubnetwork).toHaveBeenCalledWith(expect.anything(), '', undefined);
});
it('dispatches the setNetwork action', () => {
expect(setNetwork).toHaveBeenCalledWith(expect.anything(), selectedNetwork, undefined);
});
it('fetches subnetworks for the selected project, region, and network', () => {
expect(fetchSubnetworks).toHaveBeenCalledWith(
expect.anything(),
{
project: projectId,
region,
network: selectedNetwork.selfLink,
},
undefined,
);
});
});
});
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import GkeSubnetworkDropdown from '~/create_cluster/gke_cluster/components/gke_subnetwork_dropdown.vue';
import ClusterFormDropdown from '~/create_cluster/components/cluster_form_dropdown.vue';
import createClusterDropdownState from '~/create_cluster/store/cluster_dropdown/state';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('GkeSubnetworkDropdown', () => {
let wrapper;
let store;
const defaultProps = { fieldName: 'field-name' };
const selectedSubnetwork = '123456';
const setSubnetwork = jest.fn();
const buildStore = ({ clusterDropdownState } = {}) =>
new Vuex.Store({
state: {
selectedSubnetwork,
},
actions: {
setSubnetwork,
},
getters: {
hasNetwork: () => false,
},
modules: {
subnetworks: {
namespaced: true,
state: {
...createClusterDropdownState(),
...(clusterDropdownState || {}),
},
},
},
});
const buildWrapper = (propsData = defaultProps) =>
shallowMount(GkeSubnetworkDropdown, {
propsData,
store,
localVue,
});
afterEach(() => {
wrapper.destroy();
});
it('sets correct field-name', () => {
const fieldName = 'field-name';
store = buildStore();
wrapper = buildWrapper({ fieldName });
expect(wrapper.find(ClusterFormDropdown).props('fieldName')).toBe(fieldName);
});
it('sets selected subnetwork as the dropdown value', () => {
store = buildStore();
wrapper = buildWrapper();
expect(wrapper.find(ClusterFormDropdown).props('value')).toBe(selectedSubnetwork);
});
it('maps subnetworks store items to the dropdown items property', () => {
const items = [{ name: 'subnetwork' }];
store = buildStore({ clusterDropdownState: { items } });
wrapper = buildWrapper();
expect(wrapper.find(ClusterFormDropdown).props('items')).toBe(items);
});
describe('when subnetwork dropdown store is loading items', () => {
it('sets subnetwork dropdown as loading', () => {
store = buildStore({ clusterDropdownState: { isLoadingItems: true } });
wrapper = buildWrapper();
expect(wrapper.find(ClusterFormDropdown).props('loading')).toBe(true);
});
});
describe('when there is no selected network', () => {
it('disables the subnetwork dropdown', () => {
store = buildStore();
wrapper = buildWrapper();
expect(wrapper.find(ClusterFormDropdown).props('disabled')).toBe(true);
});
});
describe('when an error occurs while loading subnetworks', () => {
it('sets the subnetwork dropdown as having errors', () => {
store = buildStore({ clusterDropdownState: { loadingItemsError: new Error() } });
wrapper = buildWrapper();
expect(wrapper.find(ClusterFormDropdown).props('hasErrors')).toBe(true);
});
});
describe('when dropdown emits input event', () => {
it('dispatches the setSubnetwork action', () => {
store = buildStore();
wrapper = buildWrapper();
wrapper.find(ClusterFormDropdown).vm.$emit('input', selectedSubnetwork);
expect(setSubnetwork).toHaveBeenCalledWith(expect.anything(), selectedSubnetwork, undefined);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment