Commit cc6b394a authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 0a850868
...@@ -3,6 +3,8 @@ import DropdownSearchInput from '~/vue_shared/components/dropdown/dropdown_searc ...@@ -3,6 +3,8 @@ import DropdownSearchInput from '~/vue_shared/components/dropdown/dropdown_searc
import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue'; import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue'; import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
const findItem = (items, valueProp, value) => items.find(item => item[valueProp] === value);
export default { export default {
components: { components: {
DropdownButton, DropdownButton,
...@@ -26,7 +28,7 @@ export default { ...@@ -26,7 +28,7 @@ export default {
default: '', default: '',
}, },
value: { value: {
type: Object, type: [Object, String],
required: false, required: false,
default: () => null, default: () => null,
}, },
...@@ -93,8 +95,8 @@ export default { ...@@ -93,8 +95,8 @@ export default {
}, },
data() { data() {
return { return {
selectedItem: findItem(this.items, this.value),
searchQuery: '', searchQuery: '',
selectedItem: null,
}; };
}, },
computed: { computed: {
...@@ -127,10 +129,15 @@ export default { ...@@ -127,10 +129,15 @@ export default {
return (this.selectedItem && this.selectedItem[this.valueProperty]) || ''; return (this.selectedItem && this.selectedItem[this.valueProperty]) || '';
}, },
}, },
watch: {
value(value) {
this.selectedItem = findItem(this.items, this.valueProperty, value);
},
},
methods: { methods: {
select(item) { select(item) {
this.selectedItem = item; this.selectedItem = item;
this.$emit('input', item); this.$emit('input', item[this.valueProperty]);
}, },
}, },
}; };
......
...@@ -3,12 +3,15 @@ import { createNamespacedHelpers, mapState, mapActions } from 'vuex'; ...@@ -3,12 +3,15 @@ import { createNamespacedHelpers, mapState, mapActions } from 'vuex';
import { sprintf, s__ } from '~/locale'; import { sprintf, s__ } from '~/locale';
import ClusterFormDropdown from './cluster_form_dropdown.vue'; import ClusterFormDropdown from './cluster_form_dropdown.vue';
import RegionDropdown from './region_dropdown.vue'; import RegionDropdown from './region_dropdown.vue';
import RoleNameDropdown from './role_name_dropdown.vue';
import SecurityGroupDropdown from './security_group_dropdown.vue'; import SecurityGroupDropdown from './security_group_dropdown.vue';
const { mapState: mapRolesState, mapActions: mapRolesActions } = createNamespacedHelpers('roles');
const { mapState: mapRegionsState, mapActions: mapRegionsActions } = createNamespacedHelpers( const { mapState: mapRegionsState, mapActions: mapRegionsActions } = createNamespacedHelpers(
'regions', 'regions',
); );
const { mapState: mapKeyPairsState, mapActions: mapKeyPairsActions } = createNamespacedHelpers(
'keyPairs',
);
const { mapState: mapVpcsState, mapActions: mapVpcActions } = createNamespacedHelpers('vpcs'); const { mapState: mapVpcsState, mapActions: mapVpcActions } = createNamespacedHelpers('vpcs');
const { mapState: mapSubnetsState, mapActions: mapSubnetActions } = createNamespacedHelpers( const { mapState: mapSubnetsState, mapActions: mapSubnetActions } = createNamespacedHelpers(
'subnets', 'subnets',
...@@ -18,16 +21,31 @@ export default { ...@@ -18,16 +21,31 @@ export default {
components: { components: {
ClusterFormDropdown, ClusterFormDropdown,
RegionDropdown, RegionDropdown,
RoleNameDropdown,
SecurityGroupDropdown, SecurityGroupDropdown,
}, },
computed: { computed: {
...mapState(['selectedRegion', 'selectedVpc', 'selectedSubnet']), ...mapState([
'selectedRegion',
'selectedKeyPair',
'selectedVpc',
'selectedSubnet',
'selectedRole',
]),
...mapRolesState({
roles: 'items',
isLoadingRoles: 'isLoadingItems',
loadingRolesError: 'loadingItemsError',
}),
...mapRegionsState({ ...mapRegionsState({
regions: 'items', regions: 'items',
isLoadingRegions: 'isLoadingItems', isLoadingRegions: 'isLoadingItems',
loadingRegionsError: 'loadingItemsError', loadingRegionsError: 'loadingItemsError',
}), }),
...mapKeyPairsState({
keyPairs: 'items',
isLoadingKeyPairs: 'isLoadingItems',
loadingKeyPairsError: 'loadingItemsError',
}),
...mapVpcsState({ ...mapVpcsState({
vpcs: 'items', vpcs: 'items',
isLoadingVpcs: 'isLoadingItems', isLoadingVpcs: 'isLoadingItems',
...@@ -41,9 +59,38 @@ export default { ...@@ -41,9 +59,38 @@ export default {
vpcDropdownDisabled() { vpcDropdownDisabled() {
return !this.selectedRegion; return !this.selectedRegion;
}, },
keyPairDropdownDisabled() {
return !this.selectedRegion;
},
subnetDropdownDisabled() { subnetDropdownDisabled() {
return !this.selectedVpc; return !this.selectedVpc;
}, },
roleDropdownHelpText() {
return sprintf(
s__(
'ClusterIntegration|Select the IAM Role to allow Amazon EKS and the Kubernetes control plane to manage AWS resources on your behalf. To use a new role name, first create one on %{startLink}Amazon Web Services%{endLink}.',
),
{
startLink:
'<a href="https://console.aws.amazon.com/iam/home?#roles" target="_blank" rel="noopener noreferrer">',
endLink: '</a>',
},
false,
);
},
keyPairDropdownHelpText() {
return sprintf(
s__(
'ClusterIntegration|Select the key pair name that will be used to create EC2 nodes. To use a new key pair name, first create one on %{startLink}Amazon Web Services%{endLink}.',
),
{
startLink:
'<a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html#having-ec2-create-your-key-pair" target="_blank" rel="noopener noreferrer">',
endLink: '</a>',
},
false,
);
},
vpcDropdownHelpText() { vpcDropdownHelpText() {
return sprintf( return sprintf(
s__( s__(
...@@ -73,15 +120,19 @@ export default { ...@@ -73,15 +120,19 @@ export default {
}, },
mounted() { mounted() {
this.fetchRegions(); this.fetchRegions();
this.fetchRoles();
}, },
methods: { methods: {
...mapActions(['setRegion', 'setVpc', 'setSubnet']), ...mapActions(['setRegion', 'setVpc', 'setSubnet', 'setRole', 'setKeyPair']),
...mapRegionsActions({ fetchRegions: 'fetchItems' }), ...mapRegionsActions({ fetchRegions: 'fetchItems' }),
...mapVpcActions({ fetchVpcs: 'fetchItems' }), ...mapVpcActions({ fetchVpcs: 'fetchItems' }),
...mapSubnetActions({ fetchSubnets: 'fetchItems' }), ...mapSubnetActions({ fetchSubnets: 'fetchItems' }),
setRegionAndFetchVpcs(region) { ...mapRolesActions({ fetchRoles: 'fetchItems' }),
...mapKeyPairsActions({ fetchKeyPairs: 'fetchItems' }),
setRegionAndFetchVpcsAndKeyPairs(region) {
this.setRegion({ region }); this.setRegion({ region });
this.fetchVpcs({ region }); this.fetchVpcs({ region });
this.fetchKeyPairs({ region });
}, },
setVpcAndFetchSubnets(vpc) { setVpcAndFetchSubnets(vpc) {
this.setVpc({ vpc }); this.setVpc({ vpc });
...@@ -93,27 +144,57 @@ export default { ...@@ -93,27 +144,57 @@ export default {
<template> <template>
<form name="eks-cluster-configuration-form"> <form name="eks-cluster-configuration-form">
<div class="form-group"> <div class="form-group">
<label class="label-bold" name="role" for="eks-role">{{ <label class="label-bold" for="eks-role">{{ s__('ClusterIntegration|Role name') }}</label>
s__('ClusterIntegration|Role name') <cluster-form-dropdown
}}</label> field-id="eks-role"
<role-name-dropdown /> field-name="eks-role"
:input="selectedRole"
:items="roles"
:loading="isLoadingRoles"
:loading-text="s__('ClusterIntegration|Loading IAM Roles')"
:placeholder="s__('ClusterIntergation|Select role name')"
:search-field-placeholder="s__('ClusterIntegration|Search IAM Roles')"
:empty-text="s__('ClusterIntegration|No IAM Roles found')"
:has-errors="Boolean(loadingRolesError)"
:error-message="s__('ClusterIntegration|Could not load IAM roles')"
@input="setRole({ role: $event })"
/>
<p class="form-text text-muted" v-html="roleDropdownHelpText"></p>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="label-bold" name="role" for="eks-role">{{ <label class="label-bold" for="eks-role">{{ s__('ClusterIntegration|Region') }}</label>
s__('ClusterIntegration|Region')
}}</label>
<region-dropdown <region-dropdown
:value="selectedRegion" :value="selectedRegion"
:regions="regions" :regions="regions"
:error="loadingRegionsError" :error="loadingRegionsError"
:loading="isLoadingRegions" :loading="isLoadingRegions"
@input="setRegionAndFetchVpcs($event)" @input="setRegionAndFetchVpcsAndKeyPairs($event)"
/> />
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="label-bold" name="eks-vpc" for="eks-vpc">{{ <label class="label-bold" for="eks-key-pair">{{
s__('ClusterIntegration|VPC') s__('ClusterIntegration|Key pair name')
}}</label> }}</label>
<cluster-form-dropdown
field-id="eks-key-pair"
field-name="eks-key-pair"
:input="selectedKeyPair"
:items="keyPairs"
:disabled="keyPairDropdownDisabled"
:disabled-text="s__('ClusterIntegration|Select a region to choose a Key Pair')"
:loading="isLoadingKeyPairs"
:loading-text="s__('ClusterIntegration|Loading Key Pairs')"
:placeholder="s__('ClusterIntergation|Select key pair')"
:search-field-placeholder="s__('ClusterIntegration|Search Key Pairs')"
:empty-text="s__('ClusterIntegration|No Key Pairs found')"
:has-errors="Boolean(loadingKeyPairsError)"
:error-message="s__('ClusterIntegration|Could not load Key Pairs')"
@input="setKeyPair({ keyPair: $event })"
/>
<p class="form-text text-muted" v-html="keyPairDropdownHelpText"></p>
</div>
<div class="form-group">
<label class="label-bold" for="eks-vpc">{{ s__('ClusterIntegration|VPC') }}</label>
<cluster-form-dropdown <cluster-form-dropdown
field-id="eks-vpc" field-id="eks-vpc"
field-name="eks-vpc" field-name="eks-vpc"
...@@ -126,16 +207,14 @@ export default { ...@@ -126,16 +207,14 @@ export default {
:placeholder="s__('ClusterIntergation|Select a VPC')" :placeholder="s__('ClusterIntergation|Select a VPC')"
:search-field-placeholder="s__('ClusterIntegration|Search VPCs')" :search-field-placeholder="s__('ClusterIntegration|Search VPCs')"
:empty-text="s__('ClusterIntegration|No VPCs found')" :empty-text="s__('ClusterIntegration|No VPCs found')"
:has-errors="loadingVpcsError" :has-errors="Boolean(loadingVpcsError)"
:error-message="s__('ClusterIntegration|Could not load VPCs for the selected region')" :error-message="s__('ClusterIntegration|Could not load VPCs for the selected region')"
@input="setVpcAndFetchSubnets($event)" @input="setVpcAndFetchSubnets($event)"
/> />
<p class="form-text text-muted" v-html="vpcDropdownHelpText"></p> <p class="form-text text-muted" v-html="vpcDropdownHelpText"></p>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="label-bold" name="eks-subnet" for="eks-subnet">{{ <label class="label-bold" for="eks-role">{{ s__('ClusterIntegration|Subnet') }}</label>
s__('ClusterIntegration|Subnet')
}}</label>
<cluster-form-dropdown <cluster-form-dropdown
field-id="eks-subnet" field-id="eks-subnet"
field-name="eks-subnet" field-name="eks-subnet"
...@@ -148,7 +227,7 @@ export default { ...@@ -148,7 +227,7 @@ export default {
:placeholder="s__('ClusterIntergation|Select a subnet')" :placeholder="s__('ClusterIntergation|Select a subnet')"
:search-field-placeholder="s__('ClusterIntegration|Search subnets')" :search-field-placeholder="s__('ClusterIntegration|Search subnets')"
:empty-text="s__('ClusterIntegration|No subnet found')" :empty-text="s__('ClusterIntegration|No subnet found')"
:has-errors="loadingSubnetsError" :has-errors="Boolean(loadingSubnetsError)"
:error-message="s__('ClusterIntegration|Could not load subnets for the selected VPC')" :error-message="s__('ClusterIntegration|Could not load subnets for the selected VPC')"
@input="setSubnet({ subnet: $event })" @input="setSubnet({ subnet: $event })"
/> />
......
<script>
import { sprintf, s__ } from '~/locale';
import ClusterFormDropdown from './cluster_form_dropdown.vue';
export default {
components: {
ClusterFormDropdown,
},
props: {
roles: {
type: Array,
required: false,
default: () => [],
},
loading: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
helpText() {
return sprintf(
s__(
'ClusterIntegration|Select the IAM Role to allow Amazon EKS and the Kubernetes control plane to manage AWS resources on your behalf. To use a new role name, first create one on %{startLink}Amazon Web Services%{endLink}.',
),
{
startLink:
'<a href="https://console.aws.amazon.com/iam/home?#roles" target="_blank" rel="noopener noreferrer">',
endLink: '</a>',
},
false,
);
},
},
};
</script>
<template>
<div>
<cluster-form-dropdown
field-id="eks-role-name"
field-name="eks-role-name"
:items="roles"
:loading="loading"
:loading-text="s__('ClusterIntegration|Loading IAM Roles')"
:placeholder="s__('ClusterIntergation|Select role name')"
:search-field-placeholder="s__('ClusterIntegration|Search IAM Roles')"
:empty-text="s__('ClusterIntegration|No IAM Roles found')"
/>
<p class="form-text text-muted" v-html="helpText"></p>
</div>
</template>
import EC2 from 'aws-sdk/clients/ec2'; import EC2 from 'aws-sdk/clients/ec2';
import IAM from 'aws-sdk/clients/iam';
export const fetchRoles = () =>
new Promise((resolve, reject) => {
const iam = new IAM();
iam
.listRoles()
.on('success', ({ data: { Roles: roles } }) => {
const transformedRoles = roles.map(({ RoleName: name }) => ({ name }));
resolve(transformedRoles);
})
.on('error', error => {
reject(error);
})
.send();
});
export const fetchKeyPairs = () =>
new Promise((resolve, reject) => {
const ec2 = new EC2();
ec2
.describeKeyPairs()
.on('success', ({ data: { KeyPairs: keyPairs } }) => {
const transformedKeyPairs = keyPairs.map(({ RegionName: name }) => ({ name }));
resolve(transformedKeyPairs);
})
.on('error', error => {
reject(error);
})
.send();
});
export const fetchRegions = () => export const fetchRegions = () =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
......
...@@ -4,6 +4,10 @@ export const setRegion = ({ commit }, payload) => { ...@@ -4,6 +4,10 @@ export const setRegion = ({ commit }, payload) => {
commit(types.SET_REGION, payload); commit(types.SET_REGION, payload);
}; };
export const setKeyPair = ({ commit }, payload) => {
commit(types.SET_KEY_PAIR, payload);
};
export const setVpc = ({ commit }, payload) => { export const setVpc = ({ commit }, payload) => {
commit(types.SET_VPC, payload); commit(types.SET_VPC, payload);
}; };
...@@ -12,4 +16,8 @@ export const setSubnet = ({ commit }, payload) => { ...@@ -12,4 +16,8 @@ export const setSubnet = ({ commit }, payload) => {
commit(types.SET_SUBNET, payload); commit(types.SET_SUBNET, payload);
}; };
export const setRole = ({ commit }, payload) => {
commit(types.SET_ROLE, payload);
};
export default () => {}; export default () => {};
...@@ -15,10 +15,18 @@ const createStore = () => ...@@ -15,10 +15,18 @@ const createStore = () =>
mutations, mutations,
state: state(), state: state(),
modules: { modules: {
roles: {
namespaced: true,
...clusterDropdownStore(awsServices.fetchRoles),
},
regions: { regions: {
namespaced: true, namespaced: true,
...clusterDropdownStore(awsServices.fetchRegions), ...clusterDropdownStore(awsServices.fetchRegions),
}, },
keyPairs: {
namespaced: true,
...clusterDropdownStore(awsServices.fetchKeyPairs),
},
vpcs: { vpcs: {
namespaced: true, namespaced: true,
...clusterDropdownStore(awsServices.fetchVpcs), ...clusterDropdownStore(awsServices.fetchVpcs),
......
export const SET_REGION = 'SET_REGION'; export const SET_REGION = 'SET_REGION';
export const SET_VPC = 'SET_VPC'; export const SET_VPC = 'SET_VPC';
export const SET_KEY_PAIR = 'SET_KEY_PAIR';
export const SET_SUBNET = 'SET_SUBNET'; export const SET_SUBNET = 'SET_SUBNET';
export const SET_ROLE = 'SET_ROLE';
...@@ -4,10 +4,16 @@ export default { ...@@ -4,10 +4,16 @@ export default {
[types.SET_REGION](state, { region }) { [types.SET_REGION](state, { region }) {
state.selectedRegion = region; state.selectedRegion = region;
}, },
[types.SET_KEY_PAIR](state, { keyPair }) {
state.selectedKeyPair = keyPair;
},
[types.SET_VPC](state, { vpc }) { [types.SET_VPC](state, { vpc }) {
state.selectedVpc = vpc; state.selectedVpc = vpc;
}, },
[types.SET_SUBNET](state, { subnet }) { [types.SET_SUBNET](state, { subnet }) {
state.selectedSubnet = subnet; state.selectedSubnet = subnet;
}, },
[types.SET_ROLE](state, { role }) {
state.selectedRole = role;
},
}; };
...@@ -4,6 +4,7 @@ export default () => ({ ...@@ -4,6 +4,7 @@ export default () => ({
selectedRegion: '', selectedRegion: '',
selectedRole: '', selectedRole: '',
selectedKeyPair: '',
selectedVpc: '', selectedVpc: '',
selectedSubnet: '', selectedSubnet: '',
selectedSecurityGroup: '', selectedSecurityGroup: '',
......
...@@ -19,18 +19,13 @@ export default { ...@@ -19,18 +19,13 @@ export default {
updated() { updated() {
this.$nextTick(() => { this.$nextTick(() => {
this.handleScrollDown(); this.handleScrollDown();
this.handleCollapsibleRows();
}); });
}, },
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
this.handleScrollDown(); this.handleScrollDown();
this.handleCollapsibleRows();
}); });
}, },
destroyed() {
this.removeEventListener();
},
methods: { methods: {
...mapActions(['scrollBottom']), ...mapActions(['scrollBottom']),
/** /**
...@@ -47,53 +42,6 @@ export default { ...@@ -47,53 +42,6 @@ export default {
}, 0); }, 0);
} }
}, },
removeEventListener() {
this.$el.querySelectorAll('.js-section-start').forEach(el => {
const titleSection = el.nextSibling;
titleSection.removeEventListener(
'click',
this.handleHeaderClick.bind(this, el, el.dataset.section),
);
el.removeEventListener('click', this.handleSectionClick);
});
},
/**
* The collapsible rows are sent in HTML from the backend
* We need tos add a onclick handler for the divs that match `.js-section-start`
*
*/
handleCollapsibleRows() {
this.$el.querySelectorAll('.js-section-start').forEach(el => {
const titleSection = el.nextSibling;
titleSection.addEventListener(
'click',
this.handleHeaderClick.bind(this, el, el.dataset.section),
);
el.addEventListener('click', this.handleSectionClick);
});
},
handleHeaderClick(arrowElement, section) {
this.updateToggleSection(arrowElement, section);
},
updateToggleSection(arrow, section) {
// toggle the arrow class
arrow.classList.toggle('fa-caret-right');
arrow.classList.toggle('fa-caret-down');
// hide the sections
const sibilings = this.$el.querySelectorAll(`.js-s-${section}:not(.js-section-header)`);
sibilings.forEach(row => row.classList.toggle('hidden'));
},
/**
* On click, we toggle the hidden class of
* all the rows that match the `data-section` selector
*/
handleSectionClick(evt) {
const clickedArrow = evt.currentTarget;
this.updateToggleSection(clickedArrow, clickedArrow.dataset.section);
},
}, },
}; };
</script> </script>
......
...@@ -124,26 +124,6 @@ ...@@ -124,26 +124,6 @@
float: left; float: left;
padding-left: $gl-padding-8; padding-left: $gl-padding-8;
} }
.section-start {
display: inline;
}
.section-start,
.section-header {
&:hover {
cursor: pointer;
&::after {
content: '';
background-color: rgba($white-light, 0.2);
left: 0;
right: 0;
position: absolute;
height: $job-log-highlight-height;
}
}
}
} }
.build-header { .build-header {
......
...@@ -59,18 +59,20 @@ class SnippetsFinder < UnionFinder ...@@ -59,18 +59,20 @@ class SnippetsFinder < UnionFinder
end end
def execute def execute
base = base = init_collection
base.with_optional_visibility(visibility_from_scope).fresh
end
private
def init_collection
if project if project
snippets_for_a_single_project snippets_for_a_single_project
else else
snippets_for_multiple_projects snippets_for_multiple_projects
end end
base.with_optional_visibility(visibility_from_scope).fresh
end end
private
# Produces a query that retrieves snippets from multiple projects. # Produces a query that retrieves snippets from multiple projects.
# #
# The resulting query will, depending on the user's permissions, include the # The resulting query will, depending on the user's permissions, include the
...@@ -115,7 +117,7 @@ class SnippetsFinder < UnionFinder ...@@ -115,7 +117,7 @@ class SnippetsFinder < UnionFinder
# This method requires that `current_user` returns a `User` instead of `nil`, # This method requires that `current_user` returns a `User` instead of `nil`,
# and is optimised for this specific scenario. # and is optimised for this specific scenario.
def snippets_of_authorized_projects def snippets_of_authorized_projects
base = author ? snippets_for_author : Snippet.all base = author ? author.snippets : Snippet.all
base base
.only_include_projects_with_snippets_enabled(include_private: true) .only_include_projects_with_snippets_enabled(include_private: true)
...@@ -157,3 +159,5 @@ class SnippetsFinder < UnionFinder ...@@ -157,3 +159,5 @@ class SnippetsFinder < UnionFinder
end end
end end
end end
SnippetsFinder.prepend_if_ee('EE::SnippetsFinder')
...@@ -21,7 +21,7 @@ module NotificationBranchSelection ...@@ -21,7 +21,7 @@ module NotificationBranchSelection
end end
is_default_branch = ref == project.default_branch is_default_branch = ref == project.default_branch
is_protected_branch = project.protected_branches.exists?(name: ref) is_protected_branch = ProtectedBranch.protected?(project, ref)
case branches_to_be_notified case branches_to_be_notified
when "all" when "all"
......
---
title: Specify sort order explicitly for Group and Project audit events
merge_request: 17739
author:
type: fixed
---
title: Removes Collapsible Sections from Job Log
merge_request:
author:
type: fixed
---
title: Fix protected branch detection used by notification service
merge_request: 18221
author:
type: fixed
---
title: Narrow snippet search scope in GitLab.com
merge_request: 17625
author:
type: performance
---
title: Upgrade to Gitaly v1.67.0
merge_request: 18326
author:
type: changed
...@@ -115,16 +115,6 @@ curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded token>" --da ...@@ -115,16 +115,6 @@ curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded token>" --da
- GitLab-shell - GitLab-shell
## Get merge requests for a ref [NOT USED]
```
GET /internal/merge_request_urls
```
**Deprecated**: This used to be called from GitLab shell to fetch the
merge requests for a change to output them after a push, but this is
now handled in the `/internal/post_receive` call.
## Authorized Keys Check ## Authorized Keys Check
This endpoint is called by the GitLab-shell authorized keys This endpoint is called by the GitLab-shell authorized keys
......
...@@ -112,10 +112,6 @@ module API ...@@ -112,10 +112,6 @@ module API
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
get "/merge_request_urls" do
merge_request_urls
end
# #
# Get a ssh key using the fingerprint # Get a ssh key using the fingerprint
# #
......
...@@ -45,7 +45,7 @@ module Gitlab ...@@ -45,7 +45,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def snippets def snippets
SnippetsFinder.new(current_user) SnippetsFinder.new(current_user, finder_params)
.execute .execute
.includes(:author) .includes(:author)
.reorder(updated_at: :desc) .reorder(updated_at: :desc)
...@@ -67,5 +67,11 @@ module Gitlab ...@@ -67,5 +67,11 @@ module Gitlab
def paginated_objects(relation, page) def paginated_objects(relation, page)
relation.page(page).per(per_page) relation.page(page).per(per_page)
end end
def finder_params
{}
end
end end
end end
Gitlab::SnippetSearchResults.prepend_if_ee('::EE::Gitlab::SnippetSearchResults')
...@@ -3417,6 +3417,12 @@ msgstr "" ...@@ -3417,6 +3417,12 @@ msgstr ""
msgid "ClusterIntegration|Copy Service Token" msgid "ClusterIntegration|Copy Service Token"
msgstr "" msgstr ""
msgid "ClusterIntegration|Could not load IAM roles"
msgstr ""
msgid "ClusterIntegration|Could not load Key Pairs"
msgstr ""
msgid "ClusterIntegration|Could not load VPCs for the selected region" msgid "ClusterIntegration|Could not load VPCs for the selected region"
msgstr "" msgstr ""
...@@ -3549,6 +3555,9 @@ msgstr "" ...@@ -3549,6 +3555,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group." msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
msgstr "" msgstr ""
msgid "ClusterIntegration|Key pair name"
msgstr ""
msgid "ClusterIntegration|Knative" msgid "ClusterIntegration|Knative"
msgstr "" msgstr ""
...@@ -3609,6 +3618,9 @@ msgstr "" ...@@ -3609,6 +3618,9 @@ msgstr ""
msgid "ClusterIntegration|Loading IAM Roles" msgid "ClusterIntegration|Loading IAM Roles"
msgstr "" msgstr ""
msgid "ClusterIntegration|Loading Key Pairs"
msgstr ""
msgid "ClusterIntegration|Loading Regions" msgid "ClusterIntegration|Loading Regions"
msgstr "" msgstr ""
...@@ -3630,6 +3642,9 @@ msgstr "" ...@@ -3630,6 +3642,9 @@ msgstr ""
msgid "ClusterIntegration|No IAM Roles found" msgid "ClusterIntegration|No IAM Roles found"
msgstr "" msgstr ""
msgid "ClusterIntegration|No Key Pairs found"
msgstr ""
msgid "ClusterIntegration|No VPCs found" msgid "ClusterIntegration|No VPCs found"
msgstr "" msgstr ""
...@@ -3717,6 +3732,9 @@ msgstr "" ...@@ -3717,6 +3732,9 @@ msgstr ""
msgid "ClusterIntegration|Search IAM Roles" msgid "ClusterIntegration|Search IAM Roles"
msgstr "" msgstr ""
msgid "ClusterIntegration|Search Key Pairs"
msgstr ""
msgid "ClusterIntegration|Search VPCs" msgid "ClusterIntegration|Search VPCs"
msgstr "" msgstr ""
...@@ -3744,6 +3762,9 @@ msgstr "" ...@@ -3744,6 +3762,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%{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%{endLink}."
msgstr "" msgstr ""
msgid "ClusterIntegration|Select a region to choose a Key Pair"
msgstr ""
msgid "ClusterIntegration|Select a region to choose a VPC" msgid "ClusterIntegration|Select a region to choose a VPC"
msgstr "" msgstr ""
...@@ -3762,6 +3783,9 @@ msgstr "" ...@@ -3762,6 +3783,9 @@ msgstr ""
msgid "ClusterIntegration|Select the IAM Role to allow Amazon EKS and the Kubernetes control plane to manage AWS resources on your behalf. To use a new role name, first create one on %{startLink}Amazon Web Services%{endLink}." msgid "ClusterIntegration|Select the IAM Role to allow Amazon EKS and the Kubernetes control plane to manage AWS resources on your behalf. To use a new role name, first create one on %{startLink}Amazon Web Services%{endLink}."
msgstr "" msgstr ""
msgid "ClusterIntegration|Select the key pair name that will be used to create EC2 nodes. To use a new key pair name, first create one on %{startLink}Amazon Web Services%{endLink}."
msgstr ""
msgid "ClusterIntegration|Select zone" msgid "ClusterIntegration|Select zone"
msgstr "" msgstr ""
...@@ -3906,6 +3930,9 @@ msgstr "" ...@@ -3906,6 +3930,9 @@ msgstr ""
msgid "ClusterIntergation|Select a subnet" msgid "ClusterIntergation|Select a subnet"
msgstr "" msgstr ""
msgid "ClusterIntergation|Select key pair"
msgstr ""
msgid "ClusterIntergation|Select role name" msgid "ClusterIntergation|Select role name"
msgstr "" msgstr ""
...@@ -14014,9 +14041,15 @@ msgstr "" ...@@ -14014,9 +14041,15 @@ msgstr ""
msgid "SearchResults|Showing %{count} %{scope} for \"%{term}\"" msgid "SearchResults|Showing %{count} %{scope} for \"%{term}\""
msgstr "" msgstr ""
msgid "SearchResults|Showing %{count} %{scope} for \"%{term}\" in your personal and project snippets"
msgstr ""
msgid "SearchResults|Showing %{from} - %{to} of %{count} %{scope} for \"%{term}\"" msgid "SearchResults|Showing %{from} - %{to} of %{count} %{scope} for \"%{term}\""
msgstr "" msgstr ""
msgid "SearchResults|Showing %{from} - %{to} of %{count} %{scope} for \"%{term}\" in your personal and project snippets"
msgstr ""
msgid "SearchResults|We couldn't find any %{scope} matching %{term}" msgid "SearchResults|We couldn't find any %{scope} matching %{term}"
msgstr "" msgstr ""
...@@ -14207,6 +14240,12 @@ msgstr "" ...@@ -14207,6 +14240,12 @@ msgstr ""
msgid "SecurityDashboard|Project" msgid "SecurityDashboard|Project"
msgstr "" msgstr ""
msgid "SecurityDashboard|Projects added"
msgstr ""
msgid "SecurityDashboard|Remove project from dashboard"
msgstr ""
msgid "SecurityDashboard|Report type" msgid "SecurityDashboard|Report type"
msgstr "" msgstr ""
...@@ -14216,6 +14255,9 @@ msgstr "" ...@@ -14216,6 +14255,9 @@ msgstr ""
msgid "SecurityDashboard|Security Dashboard" msgid "SecurityDashboard|Security Dashboard"
msgstr "" msgstr ""
msgid "SecurityDashboard|Select a project to add by using the project search field above."
msgstr ""
msgid "SecurityDashboard|Severity" msgid "SecurityDashboard|Severity"
msgstr "" msgstr ""
......
...@@ -259,7 +259,6 @@ module QA ...@@ -259,7 +259,6 @@ module QA
module Milestone module Milestone
autoload :New, 'qa/page/project/milestone/new' autoload :New, 'qa/page/project/milestone/new'
autoload :Index, 'qa/page/project/milestone/index' autoload :Index, 'qa/page/project/milestone/index'
autoload :Show, 'qa/page/project/milestone/show'
end end
module Operations module Operations
......
# frozen_string_literal: true
module QA
module Page
module Project
module Milestone
class Show < Page::Base
end
end
end
end
end
QA::Page::Project::Milestone::Show.prepend_if_ee('QA::EE::Page::Project::Milestone::Show')
...@@ -38,66 +38,6 @@ describe 'User browses a job', :js do ...@@ -38,66 +38,6 @@ describe 'User browses a job', :js do
expect(page).to have_content('Job has been erased') expect(page).to have_content('Job has been erased')
end end
shared_examples 'has collapsible sections' do
it 'collapses the section clicked' do
wait_for_requests
text_to_hide = "Cloning into '/nolith/ci-tests'"
text_to_show = 'Waiting for pod'
expect(page).to have_content(text_to_hide)
expect(page).to have_content(text_to_show)
first('.js-section-start[data-section="get-sources"]').click
expect(page).not_to have_content(text_to_hide)
expect(page).to have_content(text_to_show)
end
it 'collapses the section header clicked' do
wait_for_requests
text_to_hide = "Cloning into '/nolith/ci-tests'"
text_to_show = 'Waiting for pod'
expect(page).to have_content(text_to_hide)
expect(page).to have_content(text_to_show)
first('.js-section-header.js-s-get-sources').click
expect(page).not_to have_content(text_to_hide)
expect(page).to have_content(text_to_show)
end
end
context 'when job trace contains sections' do
let!(:build) { create(:ci_build, :success, :trace_with_sections, :coverage, pipeline: pipeline) }
it_behaves_like 'has collapsible sections'
end
context 'when job trace contains duplicate sections' do
let!(:build) { create(:ci_build, :success, :trace_with_duplicate_sections, :coverage, pipeline: pipeline) }
it_behaves_like 'has collapsible sections'
end
context 'when job trace contains sections' do
let!(:build) { create(:ci_build, :success, :trace_with_duplicate_sections, :coverage, pipeline: pipeline) }
it 'collapses a section' do
wait_for_requests
text_to_hide = "Cloning into '/nolith/ci-tests'"
text_to_show = 'Waiting for pod'
expect(page).to have_content(text_to_hide)
expect(page).to have_content(text_to_show)
first('.js-section-start[data-section="get-sources"]').click
expect(page).not_to have_content(text_to_hide)
expect(page).to have_content(text_to_show)
end
end
context 'with a failed job' do context 'with a failed job' do
let!(:build) { create(:ci_build, :failed, :trace_artifact, pipeline: pipeline) } let!(:build) { create(:ci_build, :failed, :trace_artifact, pipeline: pipeline) }
......
...@@ -150,6 +150,26 @@ describe SnippetsFinder do ...@@ -150,6 +150,26 @@ describe SnippetsFinder do
expect(snippets).to contain_exactly(private_project_snippet, internal_project_snippet, public_project_snippet) expect(snippets).to contain_exactly(private_project_snippet, internal_project_snippet, public_project_snippet)
end end
context 'filter by author' do
let!(:other_user) { create(:user) }
let!(:other_private_project_snippet) { create(:project_snippet, :private, project: project, author: other_user) }
let!(:other_internal_project_snippet) { create(:project_snippet, :internal, project: project, author: other_user) }
let!(:other_public_project_snippet) { create(:project_snippet, :public, project: project, author: other_user) }
it 'returns all snippets for project members' do
project.add_developer(user)
snippets = described_class.new(user, author: other_user).execute
expect(snippets)
.to contain_exactly(
other_private_project_snippet,
other_internal_project_snippet,
other_public_project_snippet
)
end
end
end end
context 'when the user cannot read cross project' do context 'when the user cannot read cross project' do
......
...@@ -7,12 +7,22 @@ import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidde ...@@ -7,12 +7,22 @@ import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidde
describe('ClusterFormDropdown', () => { describe('ClusterFormDropdown', () => {
let vm; let vm;
const firstItem = { name: 'item 1', value: 1 };
const secondItem = { name: 'item 2', value: 2 };
const items = [firstItem, secondItem, { name: 'item 3', value: 3 }];
beforeEach(() => { beforeEach(() => {
vm = shallowMount(ClusterFormDropdown); vm = shallowMount(ClusterFormDropdown);
}); });
afterEach(() => vm.destroy()); afterEach(() => vm.destroy());
describe('when initial value is provided', () => {
it('sets selectedItem to initial value', () => {
vm.setProps({ items, value: secondItem.value });
expect(vm.find(DropdownButton).props('toggleText')).toEqual(secondItem.name);
});
});
describe('when no item is selected', () => { describe('when no item is selected', () => {
it('displays placeholder text', () => { it('displays placeholder text', () => {
const placeholder = 'placeholder'; const placeholder = 'placeholder';
...@@ -24,18 +34,19 @@ describe('ClusterFormDropdown', () => { ...@@ -24,18 +34,19 @@ describe('ClusterFormDropdown', () => {
}); });
describe('when an item is selected', () => { describe('when an item is selected', () => {
const selectedItem = { name: 'Name', value: 'value' };
beforeEach(() => { beforeEach(() => {
vm.setData({ selectedItem }); vm.setProps({ items });
vm.findAll('.js-dropdown-item')
.at(1)
.trigger('click');
}); });
it('displays selected item label', () => { it('displays selected item label', () => {
expect(vm.find(DropdownButton).props('toggleText')).toEqual(selectedItem.name); expect(vm.find(DropdownButton).props('toggleText')).toEqual(secondItem.name);
}); });
it('sets selected value to dropdown hidden input', () => { it('sets selected value to dropdown hidden input', () => {
expect(vm.find(DropdownHiddenInput).props('value')).toEqual(selectedItem.value); expect(vm.find(DropdownHiddenInput).props('value')).toEqual(secondItem.value);
}); });
}); });
...@@ -124,9 +135,7 @@ describe('ClusterFormDropdown', () => { ...@@ -124,9 +135,7 @@ describe('ClusterFormDropdown', () => {
}); });
it('it filters results by search query', () => { it('it filters results by search query', () => {
const secondItem = { name: 'second item' }; const searchQuery = secondItem.name;
const items = [{ name: 'first item' }, secondItem];
const searchQuery = 'second';
vm.setProps({ items }); vm.setProps({ items });
vm.setData({ searchQuery }); vm.setData({ searchQuery });
......
...@@ -14,12 +14,16 @@ describe('EksClusterConfigurationForm', () => { ...@@ -14,12 +14,16 @@ describe('EksClusterConfigurationForm', () => {
let store; let store;
let actions; let actions;
let state; let state;
let rolesState;
let regionsState; let regionsState;
let vpcsState; let vpcsState;
let subnetsState; let subnetsState;
let keyPairsState;
let vpcsActions; let vpcsActions;
let rolesActions;
let regionsActions; let regionsActions;
let subnetsActions; let subnetsActions;
let keyPairsActions;
let vm; let vm;
beforeEach(() => { beforeEach(() => {
...@@ -28,16 +32,27 @@ describe('EksClusterConfigurationForm', () => { ...@@ -28,16 +32,27 @@ describe('EksClusterConfigurationForm', () => {
setRegion: jest.fn(), setRegion: jest.fn(),
setVpc: jest.fn(), setVpc: jest.fn(),
setSubnet: jest.fn(), setSubnet: jest.fn(),
setRole: jest.fn(),
setKeyPair: jest.fn(),
}; };
regionsActions = { regionsActions = {
fetchItems: jest.fn(), fetchItems: jest.fn(),
}; };
keyPairsActions = {
fetchItems: jest.fn(),
};
vpcsActions = { vpcsActions = {
fetchItems: jest.fn(), fetchItems: jest.fn(),
}; };
subnetsActions = { subnetsActions = {
fetchItems: jest.fn(), fetchItems: jest.fn(),
}; };
rolesActions = {
fetchItems: jest.fn(),
};
rolesState = {
...clusterDropdownStoreState(),
};
regionsState = { regionsState = {
...clusterDropdownStoreState(), ...clusterDropdownStoreState(),
}; };
...@@ -47,6 +62,9 @@ describe('EksClusterConfigurationForm', () => { ...@@ -47,6 +62,9 @@ describe('EksClusterConfigurationForm', () => {
subnetsState = { subnetsState = {
...clusterDropdownStoreState(), ...clusterDropdownStoreState(),
}; };
keyPairsState = {
...clusterDropdownStoreState(),
};
store = new Vuex.Store({ store = new Vuex.Store({
state, state,
actions, actions,
...@@ -66,6 +84,16 @@ describe('EksClusterConfigurationForm', () => { ...@@ -66,6 +84,16 @@ describe('EksClusterConfigurationForm', () => {
state: subnetsState, state: subnetsState,
actions: subnetsActions, actions: subnetsActions,
}, },
roles: {
namespaced: true,
state: rolesState,
actions: rolesActions,
},
keyPairs: {
namespaced: true,
state: keyPairsState,
actions: keyPairsActions,
},
}, },
}); });
}); });
...@@ -82,13 +110,37 @@ describe('EksClusterConfigurationForm', () => { ...@@ -82,13 +110,37 @@ describe('EksClusterConfigurationForm', () => {
}); });
const findRegionDropdown = () => vm.find(RegionDropdown); const findRegionDropdown = () => vm.find(RegionDropdown);
const findKeyPairDropdown = () => vm.find('[field-id="eks-key-pair"]');
const findVpcDropdown = () => vm.find('[field-id="eks-vpc"]'); const findVpcDropdown = () => vm.find('[field-id="eks-vpc"]');
const findSubnetDropdown = () => vm.find('[field-id="eks-subnet"]'); const findSubnetDropdown = () => vm.find('[field-id="eks-subnet"]');
const findRoleDropdown = () => vm.find('[field-id="eks-role"]');
describe('when mounted', () => { describe('when mounted', () => {
it('fetches available regions', () => { it('fetches available regions', () => {
expect(regionsActions.fetchItems).toHaveBeenCalled(); expect(regionsActions.fetchItems).toHaveBeenCalled();
}); });
it('fetches available roles', () => {
expect(rolesActions.fetchItems).toHaveBeenCalled();
});
});
it('sets isLoadingRoles to RoleDropdown loading property', () => {
rolesState.isLoadingItems = true;
return Vue.nextTick().then(() => {
expect(findRoleDropdown().props('loading')).toBe(rolesState.isLoadingItems);
});
});
it('sets roles to RoleDropdown items property', () => {
expect(findRoleDropdown().props('items')).toBe(rolesState.items);
});
it('sets RoleDropdown hasErrors to true when loading roles failed', () => {
rolesState.loadingItemsError = new Error();
expect(findRoleDropdown().props('hasErrors')).toEqual(true);
}); });
it('sets isLoadingRegions to RegionDropdown loading property', () => { it('sets isLoadingRegions to RegionDropdown loading property', () => {
...@@ -107,6 +159,36 @@ describe('EksClusterConfigurationForm', () => { ...@@ -107,6 +159,36 @@ describe('EksClusterConfigurationForm', () => {
expect(findRegionDropdown().props('error')).toBe(regionsState.loadingItemsError); expect(findRegionDropdown().props('error')).toBe(regionsState.loadingItemsError);
}); });
it('disables KeyPairDropdown when no region is selected', () => {
expect(findKeyPairDropdown().props('disabled')).toBe(true);
});
it('enables KeyPairDropdown when no region is selected', () => {
state.selectedRegion = { name: 'west-1 ' };
return Vue.nextTick().then(() => {
expect(findKeyPairDropdown().props('disabled')).toBe(false);
});
});
it('sets isLoadingKeyPairs to KeyPairDropdown loading property', () => {
keyPairsState.isLoadingItems = true;
return Vue.nextTick().then(() => {
expect(findKeyPairDropdown().props('loading')).toBe(keyPairsState.isLoadingItems);
});
});
it('sets keyPairs to KeyPairDropdown items property', () => {
expect(findKeyPairDropdown().props('items')).toBe(keyPairsState.items);
});
it('sets KeyPairDropdown hasErrors to true when loading key pairs fails', () => {
keyPairsState.loadingItemsError = new Error();
expect(findKeyPairDropdown().props('hasErrors')).toEqual(true);
});
it('disables VpcDropdown when no region is selected', () => { it('disables VpcDropdown when no region is selected', () => {
expect(findVpcDropdown().props('disabled')).toBe(true); expect(findVpcDropdown().props('disabled')).toBe(true);
}); });
...@@ -131,8 +213,10 @@ describe('EksClusterConfigurationForm', () => { ...@@ -131,8 +213,10 @@ describe('EksClusterConfigurationForm', () => {
expect(findVpcDropdown().props('items')).toBe(vpcsState.items); expect(findVpcDropdown().props('items')).toBe(vpcsState.items);
}); });
it('sets loadingVpcsError to VpcDropdown hasErrors property', () => { it('sets VpcDropdown hasErrors to true when loading vpcs fails', () => {
expect(findVpcDropdown().props('hasErrors')).toBe(vpcsState.loadingItemsError); vpcsState.loadingItemsError = new Error();
expect(findVpcDropdown().props('hasErrors')).toEqual(true);
}); });
it('disables SubnetDropdown when no vpc is selected', () => { it('disables SubnetDropdown when no vpc is selected', () => {
...@@ -159,8 +243,10 @@ describe('EksClusterConfigurationForm', () => { ...@@ -159,8 +243,10 @@ describe('EksClusterConfigurationForm', () => {
expect(findSubnetDropdown().props('items')).toBe(subnetsState.items); expect(findSubnetDropdown().props('items')).toBe(subnetsState.items);
}); });
it('sets loadingSubnetsError to SubnetDropdown hasErrors property', () => { it('sets SubnetDropdown hasErrors to true when loading subnets fails', () => {
expect(findSubnetDropdown().props('hasErrors')).toBe(subnetsState.loadingItemsError); subnetsState.loadingItemsError = new Error();
expect(findSubnetDropdown().props('hasErrors')).toEqual(true);
}); });
describe('when region is selected', () => { describe('when region is selected', () => {
...@@ -177,6 +263,14 @@ describe('EksClusterConfigurationForm', () => { ...@@ -177,6 +263,14 @@ describe('EksClusterConfigurationForm', () => {
it('fetches available vpcs', () => { it('fetches available vpcs', () => {
expect(vpcsActions.fetchItems).toHaveBeenCalledWith(expect.anything(), { region }, undefined); expect(vpcsActions.fetchItems).toHaveBeenCalledWith(expect.anything(), { region }, undefined);
}); });
it('fetches available key pairs', () => {
expect(keyPairsActions.fetchItems).toHaveBeenCalledWith(
expect.anything(),
{ region },
undefined,
);
});
}); });
describe('when vpc is selected', () => { describe('when vpc is selected', () => {
...@@ -206,4 +300,28 @@ describe('EksClusterConfigurationForm', () => { ...@@ -206,4 +300,28 @@ describe('EksClusterConfigurationForm', () => {
expect(actions.setSubnet).toHaveBeenCalledWith(expect.anything(), { subnet }, undefined); expect(actions.setSubnet).toHaveBeenCalledWith(expect.anything(), { subnet }, undefined);
}); });
}); });
describe('when role is selected', () => {
const role = { name: 'admin' };
beforeEach(() => {
findRoleDropdown().vm.$emit('input', role);
});
it('dispatches setRole action', () => {
expect(actions.setRole).toHaveBeenCalledWith(expect.anything(), { role }, undefined);
});
});
describe('when key pair is selected', () => {
const keyPair = { name: 'key pair' };
beforeEach(() => {
findKeyPairDropdown().vm.$emit('input', keyPair);
});
it('dispatches setKeyPair action', () => {
expect(actions.setKeyPair).toHaveBeenCalledWith(expect.anything(), { keyPair }, undefined);
});
});
}); });
import { shallowMount } from '@vue/test-utils';
import ClusterFormDropdown from '~/create_cluster/eks_cluster/components/cluster_form_dropdown.vue';
import RoleNameDropdown from '~/create_cluster/eks_cluster/components/role_name_dropdown.vue';
describe('RoleNameDropdown', () => {
let vm;
beforeEach(() => {
vm = shallowMount(RoleNameDropdown);
});
afterEach(() => vm.destroy());
it('renders a cluster-form-dropdown', () => {
expect(vm.find(ClusterFormDropdown).exists()).toBe(true);
});
it('sets roles to cluster-form-dropdown items property', () => {
const roles = [{ name: 'basic' }];
vm.setProps({ roles });
expect(vm.find(ClusterFormDropdown).props('items')).toEqual(roles);
});
it('sets a loading text', () => {
expect(vm.find(ClusterFormDropdown).props('loadingText')).toEqual('Loading IAM Roles');
});
it('sets a placeholder', () => {
expect(vm.find(ClusterFormDropdown).props('placeholder')).toEqual('Select role name');
});
it('sets an empty results text', () => {
expect(vm.find(ClusterFormDropdown).props('emptyText')).toEqual('No IAM Roles found');
});
it('sets a search field placeholder', () => {
expect(vm.find(ClusterFormDropdown).props('searchFieldPlaceholder')).toEqual(
'Search IAM Roles',
);
});
});
...@@ -2,22 +2,34 @@ import testAction from 'helpers/vuex_action_helper'; ...@@ -2,22 +2,34 @@ import testAction from 'helpers/vuex_action_helper';
import createState from '~/create_cluster/eks_cluster/store/state'; import createState from '~/create_cluster/eks_cluster/store/state';
import * as actions from '~/create_cluster/eks_cluster/store/actions'; import * as actions from '~/create_cluster/eks_cluster/store/actions';
import { SET_REGION, SET_VPC, SET_SUBNET } from '~/create_cluster/eks_cluster/store/mutation_types'; import {
SET_REGION,
SET_VPC,
SET_KEY_PAIR,
SET_SUBNET,
SET_ROLE,
} from '~/create_cluster/eks_cluster/store/mutation_types';
describe('EKS Cluster Store Actions', () => { describe('EKS Cluster Store Actions', () => {
let region; let region;
let vpc; let vpc;
let subnet; let subnet;
let role;
let keyPair;
beforeEach(() => { beforeEach(() => {
region = { name: 'regions-1' }; region = { name: 'regions-1' };
vpc = { name: 'vpc-1' }; vpc = { name: 'vpc-1' };
subnet = { name: 'subnet-1' }; subnet = { name: 'subnet-1' };
role = { name: 'role-1' };
keyPair = { name: 'key-pair-1' };
}); });
it.each` it.each`
action | mutation | payload | payloadDescription action | mutation | payload | payloadDescription
${'setRole'} | ${SET_ROLE} | ${{ role }} | ${'role'}
${'setRegion'} | ${SET_REGION} | ${{ region }} | ${'region'} ${'setRegion'} | ${SET_REGION} | ${{ region }} | ${'region'}
${'setKeyPair'} | ${SET_KEY_PAIR} | ${{ keyPair }} | ${'key pair'}
${'setVpc'} | ${SET_VPC} | ${{ vpc }} | ${'vpc'} ${'setVpc'} | ${SET_VPC} | ${{ vpc }} | ${'vpc'}
${'setSubnet'} | ${SET_SUBNET} | ${{ subnet }} | ${'subnet'} ${'setSubnet'} | ${SET_SUBNET} | ${{ subnet }} | ${'subnet'}
`(`$action commits $mutation with $payloadDescription payload`, data => { `(`$action commits $mutation with $payloadDescription payload`, data => {
......
import { SET_REGION, SET_VPC, SET_SUBNET } from '~/create_cluster/eks_cluster/store/mutation_types'; import {
SET_REGION,
SET_VPC,
SET_KEY_PAIR,
SET_SUBNET,
SET_ROLE,
} from '~/create_cluster/eks_cluster/store/mutation_types';
import createState from '~/create_cluster/eks_cluster/store/state'; import createState from '~/create_cluster/eks_cluster/store/state';
import mutations from '~/create_cluster/eks_cluster/store/mutations'; import mutations from '~/create_cluster/eks_cluster/store/mutations';
...@@ -7,18 +13,24 @@ describe('Create EKS cluster store mutations', () => { ...@@ -7,18 +13,24 @@ describe('Create EKS cluster store mutations', () => {
let region; let region;
let vpc; let vpc;
let subnet; let subnet;
let role;
let keyPair;
beforeEach(() => { beforeEach(() => {
region = { name: 'regions-1' }; region = { name: 'regions-1' };
vpc = { name: 'vpc-1' }; vpc = { name: 'vpc-1' };
subnet = { name: 'subnet-1' }; subnet = { name: 'subnet-1' };
role = { name: 'role-1' };
keyPair = { name: 'key pair' };
state = createState(); state = createState();
}); });
it.each` it.each`
mutation | mutatedProperty | payload | expectedValue | expectedValueDescription mutation | mutatedProperty | payload | expectedValue | expectedValueDescription
${SET_ROLE} | ${'selectedRole'} | ${{ role }} | ${role} | ${'selected role payload'}
${SET_REGION} | ${'selectedRegion'} | ${{ region }} | ${region} | ${'selected region payload'} ${SET_REGION} | ${'selectedRegion'} | ${{ region }} | ${region} | ${'selected region payload'}
${SET_KEY_PAIR} | ${'selectedKeyPair'} | ${{ keyPair }} | ${keyPair} | ${'selected key pair payload'}
${SET_VPC} | ${'selectedVpc'} | ${{ vpc }} | ${vpc} | ${'selected vpc payload'} ${SET_VPC} | ${'selectedVpc'} | ${{ vpc }} | ${vpc} | ${'selected vpc payload'}
${SET_SUBNET} | ${'selectedSubnet'} | ${{ subnet }} | ${subnet} | ${'selected sybnet payload'} ${SET_SUBNET} | ${'selectedSubnet'} | ${{ subnet }} | ${subnet} | ${'selected sybnet payload'}
`(`$mutation sets $mutatedProperty to $expectedValueDescription`, data => { `(`$mutation sets $mutatedProperty to $expectedValueDescription`, data => {
......
...@@ -3,7 +3,6 @@ import component from '~/jobs/components/job_log.vue'; ...@@ -3,7 +3,6 @@ import component from '~/jobs/components/job_log.vue';
import createStore from '~/jobs/store'; import createStore from '~/jobs/store';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore } from '../store/helpers'; import { resetStore } from '../store/helpers';
import { logWithCollapsibleSections } from '../mock_data';
describe('Job Log', () => { describe('Job Log', () => {
const Component = Vue.extend(component); const Component = Vue.extend(component);
...@@ -63,60 +62,4 @@ describe('Job Log', () => { ...@@ -63,60 +62,4 @@ describe('Job Log', () => {
expect(vm.$el.querySelector('.js-log-animation')).toBeNull(); expect(vm.$el.querySelector('.js-log-animation')).toBeNull();
}); });
}); });
describe('Collapsible sections', () => {
beforeEach(() => {
vm = mountComponentWithStore(Component, {
props: {
trace: logWithCollapsibleSections.html,
isComplete: true,
},
store,
});
});
it('renders open arrow', () => {
expect(vm.$el.querySelector('.fa-caret-down')).not.toBeNull();
});
it('toggles hidden class to the sibilings rows when arrow is clicked', done => {
vm.$nextTick()
.then(() => {
const { section } = vm.$el.querySelector('.js-section-start').dataset;
vm.$el.querySelector('.js-section-start').click();
vm.$el.querySelectorAll(`.js-s-${section}:not(.js-section-header)`).forEach(el => {
expect(el.classList.contains('hidden')).toEqual(true);
});
vm.$el.querySelector('.js-section-start').click();
vm.$el.querySelectorAll(`.js-s-${section}:not(.js-section-header)`).forEach(el => {
expect(el.classList.contains('hidden')).toEqual(false);
});
})
.then(done)
.catch(done.fail);
});
it('toggles hidden class to the sibilings rows when header section is clicked', done => {
vm.$nextTick()
.then(() => {
const { section } = vm.$el.querySelector('.js-section-header').dataset;
vm.$el.querySelector('.js-section-header').click();
vm.$el.querySelectorAll(`.js-s-${section}:not(.js-section-header)`).forEach(el => {
expect(el.classList.contains('hidden')).toEqual(true);
});
vm.$el.querySelector('.js-section-header').click();
vm.$el.querySelectorAll(`.js-s-${section}:not(.js-section-header)`).forEach(el => {
expect(el.classList.contains('hidden')).toEqual(false);
});
})
.then(done)
.catch(done.fail);
});
});
}); });
...@@ -4,9 +4,11 @@ import ProjectSelector from '~/vue_shared/components/project_selector/project_se ...@@ -4,9 +4,11 @@ import ProjectSelector from '~/vue_shared/components/project_selector/project_se
import ProjectListItem from '~/vue_shared/components/project_selector/project_list_item.vue'; import ProjectListItem from '~/vue_shared/components/project_selector/project_list_item.vue';
import { GlSearchBoxByType } from '@gitlab/ui'; import { GlSearchBoxByType } from '@gitlab/ui';
import { mount } from '@vue/test-utils'; import { mount, createLocalVue } from '@vue/test-utils';
import { trimText } from 'spec/helpers/text_helper'; import { trimText } from 'spec/helpers/text_helper';
const localVue = createLocalVue();
describe('ProjectSelector component', () => { describe('ProjectSelector component', () => {
let wrapper; let wrapper;
let vm; let vm;
...@@ -22,6 +24,7 @@ describe('ProjectSelector component', () => { ...@@ -22,6 +24,7 @@ describe('ProjectSelector component', () => {
jasmine.clock().install(); jasmine.clock().install();
wrapper = mount(Vue.extend(ProjectSelector), { wrapper = mount(Vue.extend(ProjectSelector), {
localVue,
propsData: { propsData: {
projectSearchResults: searchResults, projectSearchResults: searchResults,
selectedProjects: selected, selectedProjects: selected,
......
...@@ -770,47 +770,6 @@ describe API::Internal::Base do ...@@ -770,47 +770,6 @@ describe API::Internal::Base do
end end
end end
describe 'GET /internal/merge_request_urls' do
let(:repo_name) { "#{project.full_path}" }
let(:changes) { URI.escape("#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/new_branch") }
before do
project.add_developer(user)
end
it 'returns link to create new merge request' do
get api("/internal/merge_request_urls?project=#{repo_name}&changes=#{changes}"), params: { secret_token: secret_token }
expect(json_response).to match [{
"branch_name" => "new_branch",
"url" => "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch",
"new_merge_request" => true
}]
end
it 'returns empty array if printing_merge_request_link_enabled is false' do
project.update!(printing_merge_request_link_enabled: false)
get api("/internal/merge_request_urls?project=#{repo_name}&changes=#{changes}"), params: { secret_token: secret_token }
expect(json_response).to eq([])
end
context 'with a gl_repository parameter' do
let(:gl_repository) { "project-#{project.id}" }
it 'returns link to create new merge request' do
get api("/internal/merge_request_urls?gl_repository=#{gl_repository}&changes=#{changes}"), params: { secret_token: secret_token }
expect(json_response).to match [{
"branch_name" => "new_branch",
"url" => "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch",
"new_merge_request" => true
}]
end
end
end
# TODO: Uncomment when the end-point is reenabled # TODO: Uncomment when the end-point is reenabled
# describe 'POST /notify_post_receive' do # describe 'POST /notify_post_receive' do
# let(:valid_params) do # let(:valid_params) do
...@@ -951,6 +910,19 @@ describe API::Internal::Base do ...@@ -951,6 +910,19 @@ describe API::Internal::Base do
expect(json_response['messages']).to include(build_basic_message(message)) expect(json_response['messages']).to include(build_basic_message(message))
end end
it 'returns the link to an existing merge request when it exists' do
merge_request = create(:merge_request, source_project: project, source_branch: branch_name, target_branch: 'master')
post api('/internal/post_receive'), params: valid_params
message = <<~MESSAGE.strip
View merge request for feature:
#{project_merge_request_url(project, merge_request)}
MESSAGE
expect(json_response['messages']).to include(build_basic_message(message))
end
it 'returns no merge request messages if printing_merge_request_link_enabled is false' do it 'returns no merge request messages if printing_merge_request_link_enabled is false' do
project.update!(printing_merge_request_link_enabled: false) project.update!(printing_merge_request_link_enabled: false)
......
...@@ -369,6 +369,48 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name| ...@@ -369,6 +369,48 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
end end
end end
context 'on a protected branch with protected branches defined using wildcards' do
before do
create(:protected_branch, project: project, name: '*-stable')
end
let(:data) do
Gitlab::DataBuilder::Push.build(
project: project,
user: user,
ref: '1-stable'
)
end
context 'pushing tags' do
let(:data) do
Gitlab::DataBuilder::Push.build(
project: project,
user: user,
ref: "#{Gitlab::Git::TAG_REF_PREFIX}test"
)
end
it_behaves_like "triggered #{service_name} service", event_type: "push"
end
context 'notification enabled only for default branch' do
it_behaves_like "untriggered #{service_name} service", event_type: "push", branches_to_be_notified: "default"
end
context 'notification enabled only for protected branches' do
it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "protected"
end
context 'notification enabled only for default and protected branches' do
it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "default_and_protected"
end
context 'notification enabled for all branches' do
it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "all"
end
end
context 'on a neither protected nor default branch' do context 'on a neither protected nor default branch' do
let(:data) do let(:data) do
Gitlab::DataBuilder::Push.build( Gitlab::DataBuilder::Push.build(
...@@ -570,6 +612,36 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name| ...@@ -570,6 +612,36 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
end end
end end
context 'on a protected branch with protected branches defined usin wildcards' do
before do
create(:protected_branch, project: project, name: '*-stable')
end
let(:pipeline) do
create(:ci_pipeline,
project: project, status: :failed,
sha: project.commit.sha, ref: '1-stable')
end
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
context 'notification enabled only for default branch' do
it_behaves_like "untriggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "default"
end
context 'notification enabled only for protected branches' do
it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "protected"
end
context 'notification enabled only for default and protected branches' do
it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "default_and_protected"
end
context 'notification enabled for all branches' do
it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "all"
end
end
context 'on a neither protected nor default branch' do context 'on a neither protected nor default branch' do
let(:pipeline) do let(:pipeline) do
create(:ci_pipeline, create(:ci_pipeline,
......
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