Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-05-13 06:08:02 +00:00
parent 2fc7740f3c
commit 2c15256184
27 changed files with 526 additions and 53 deletions

View file

@ -249,15 +249,22 @@ export default {
createFlash(s__('Pipelines|Something went wrong while cleaning runners cache.'));
});
},
resetRequestData() {
this.requestData = { page: this.page, scope: this.scope };
},
filterPipelines(filters) {
this.resetRequestData();
filters.forEach(filter => {
this.requestData[filter.type] = filter.value.data;
// do not add Any for username query param, so we
// can fetch all trigger authors
if (filter.value.data !== ANY_TRIGGER_AUTHOR) {
this.requestData[filter.type] = filter.value.data;
}
});
// set query params back to default if filtering by Any author
// or input is cleared on submit
if (this.requestData.username === ANY_TRIGGER_AUTHOR || filters.length === 0) {
this.requestData = { page: this.page, scope: this.scope };
if (filters.length === 0) {
this.resetRequestData();
}
this.updateContent(this.requestData);

View file

@ -2,8 +2,10 @@
import { GlFilteredSearch } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import PipelineTriggerAuthorToken from './tokens/pipeline_trigger_author_token.vue';
import PipelineBranchNameToken from './tokens/pipeline_branch_name_token.vue';
import Api from '~/api';
import createFlash from '~/flash';
import { FETCH_AUTHOR_ERROR_MESSAGE, FETCH_BRANCH_ERROR_MESSAGE } from '../constants';
export default {
components: {
@ -22,6 +24,7 @@ export default {
data() {
return {
projectUsers: null,
projectBranches: null,
};
},
computed: {
@ -31,11 +34,21 @@ export default {
type: 'username',
icon: 'user',
title: s__('Pipeline|Trigger author'),
dataType: 'username',
unique: true,
token: PipelineTriggerAuthorToken,
operators: [{ value: '=', description: __('is'), default: 'true' }],
triggerAuthors: this.projectUsers,
projectId: this.projectId,
},
{
type: 'ref',
icon: 'branch',
title: s__('Pipeline|Branch name'),
unique: true,
token: PipelineBranchNameToken,
operators: [{ value: '=', description: __('is'), default: 'true' }],
branches: this.projectBranches,
projectId: this.projectId,
},
];
},
@ -46,7 +59,16 @@ export default {
this.projectUsers = users;
})
.catch(err => {
createFlash(__('There was a problem fetching project users.'));
createFlash(FETCH_AUTHOR_ERROR_MESSAGE);
throw err;
});
Api.branches(this.projectId)
.then(({ data }) => {
this.projectBranches = data.map(branch => branch.name);
})
.catch(err => {
createFlash(FETCH_BRANCH_ERROR_MESSAGE);
throw err;
});
},

View file

@ -0,0 +1,70 @@
<script>
import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
import Api from '~/api';
import { FETCH_BRANCH_ERROR_MESSAGE, FILTER_PIPELINES_SEARCH_DELAY } from '../../constants';
import createFlash from '~/flash';
import { debounce } from 'lodash';
export default {
components: {
GlFilteredSearchToken,
GlFilteredSearchSuggestion,
GlLoadingIcon,
},
props: {
config: {
type: Object,
required: true,
},
value: {
type: Object,
required: true,
},
},
data() {
return {
branches: this.config.branches,
loading: true,
};
},
methods: {
fetchBranchBySearchTerm(searchTerm) {
Api.branches(this.config.projectId, searchTerm)
.then(res => {
this.branches = res.data.map(branch => branch.name);
this.loading = false;
})
.catch(err => {
createFlash(FETCH_BRANCH_ERROR_MESSAGE);
this.loading = false;
throw err;
});
},
searchBranches: debounce(function debounceSearch({ data }) {
this.fetchBranchBySearchTerm(data);
}, FILTER_PIPELINES_SEARCH_DELAY),
},
};
</script>
<template>
<gl-filtered-search-token
:config="config"
v-bind="{ ...$props, ...$attrs }"
v-on="$listeners"
@input="searchBranches"
>
<template #suggestions>
<gl-loading-icon v-if="loading" />
<template v-else>
<gl-filtered-search-suggestion
v-for="(branch, index) in branches"
:key="index"
:value="branch"
>
{{ branch }}
</gl-filtered-search-suggestion>
</template>
</template>
</gl-filtered-search-token>
</template>

View file

@ -4,8 +4,16 @@ import {
GlAvatar,
GlFilteredSearchSuggestion,
GlDropdownDivider,
GlLoadingIcon,
} from '@gitlab/ui';
import { ANY_TRIGGER_AUTHOR } from '../../constants';
import Api from '~/api';
import createFlash from '~/flash';
import { debounce } from 'lodash';
import {
ANY_TRIGGER_AUTHOR,
FETCH_AUTHOR_ERROR_MESSAGE,
FILTER_PIPELINES_SEARCH_DELAY,
} from '../../constants';
export default {
anyTriggerAuthor: ANY_TRIGGER_AUTHOR,
@ -14,6 +22,7 @@ export default {
GlAvatar,
GlFilteredSearchSuggestion,
GlDropdownDivider,
GlLoadingIcon,
},
props: {
config: {
@ -25,26 +34,49 @@ export default {
required: true,
},
},
data() {
return {
users: this.config.triggerAuthors,
loading: true,
};
},
computed: {
currentValue() {
return this.value.data.toLowerCase();
},
filteredTriggerAuthors() {
return this.config.triggerAuthors.filter(user => {
return user.username.toLowerCase().includes(this.currentValue);
});
},
activeUser() {
return this.config.triggerAuthors.find(user => {
return this.users.find(user => {
return user.username.toLowerCase() === this.currentValue;
});
},
},
methods: {
fetchAuthorBySearchTerm(searchTerm) {
Api.projectUsers(this.config.projectId, searchTerm)
.then(res => {
this.users = res;
this.loading = false;
})
.catch(err => {
createFlash(FETCH_AUTHOR_ERROR_MESSAGE);
this.loading = false;
throw err;
});
},
searchAuthors: debounce(function debounceSearch({ data }) {
this.fetchAuthorBySearchTerm(data);
}, FILTER_PIPELINES_SEARCH_DELAY),
},
};
</script>
<template>
<gl-filtered-search-token :config="config" v-bind="{ ...$props, ...$attrs }" v-on="$listeners">
<gl-filtered-search-token
:config="config"
v-bind="{ ...$props, ...$attrs }"
v-on="$listeners"
@input="searchAuthors"
>
<template #view="{inputValue}">
<gl-avatar
v-if="activeUser"
@ -60,19 +92,23 @@ export default {
$options.anyTriggerAuthor
}}</gl-filtered-search-suggestion>
<gl-dropdown-divider />
<gl-filtered-search-suggestion
v-for="user in filteredTriggerAuthors"
:key="user.username"
:value="user.username"
>
<div class="d-flex">
<gl-avatar :size="32" :src="user.avatar_url" />
<div>
<div>{{ user.name }}</div>
<div>@{{ user.username }}</div>
<gl-loading-icon v-if="loading" />
<template v-else>
<gl-filtered-search-suggestion
v-for="user in users"
:key="user.username"
:value="user.username"
>
<div class="d-flex">
<gl-avatar :size="32" :src="user.avatar_url" />
<div>
<div>{{ user.name }}</div>
<div>@{{ user.username }}</div>
</div>
</div>
</div>
</gl-filtered-search-suggestion>
</gl-filtered-search-suggestion>
</template>
</template>
</gl-filtered-search-token>
</template>

View file

@ -1,6 +1,9 @@
import { __ } from '~/locale';
export const CANCEL_REQUEST = 'CANCEL_REQUEST';
export const PIPELINES_TABLE = 'PIPELINES_TABLE';
export const LAYOUT_CHANGE_DELAY = 300;
export const FILTER_PIPELINES_SEARCH_DELAY = 200;
export const ANY_TRIGGER_AUTHOR = 'Any';
export const TestStatus = {
@ -8,3 +11,6 @@ export const TestStatus = {
SKIPPED: 'skipped',
SUCCESS: 'success',
};
export const FETCH_AUTHOR_ERROR_MESSAGE = __('There was a problem fetching project users.');
export const FETCH_BRANCH_ERROR_MESSAGE = __('There was a problem fetching project branches.');

View file

@ -19,7 +19,7 @@ export default class PipelinesService {
}
getPipelines(data = {}) {
const { scope, page, username } = data;
const { scope, page, username, ref } = data;
const { CancelToken } = axios;
const queryParams = { scope, page };
@ -28,6 +28,10 @@ export default class PipelinesService {
queryParams.username = username;
}
if (ref) {
queryParams.ref = ref;
}
this.cancelationSource = CancelToken.source();
return axios.get(this.endpoint, {

View file

@ -35,13 +35,18 @@ export default {
},
onChangeWithFilter(params) {
const { username } = this.requestData;
const { username, ref } = this.requestData;
const paramsData = params;
if (username) {
return { ...params, username };
paramsData.username = username;
}
return params;
if (ref) {
paramsData.ref = ref;
}
return paramsData;
},
updateInternalState(parameters) {

View file

@ -12,7 +12,7 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action :authorize_update_pipeline!, only: [:retry, :cancel]
before_action do
push_frontend_feature_flag(:junit_pipeline_view)
push_frontend_feature_flag(:filter_pipelines_search)
push_frontend_feature_flag(:filter_pipelines_search, default_enabled: true)
push_frontend_feature_flag(:dag_pipeline_tab)
end
before_action :ensure_pipeline, only: [:show]
@ -269,7 +269,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end
def index_params
params.permit(:scope, :username)
params.permit(:scope, :username, :ref)
end
end

View file

@ -17,6 +17,17 @@ module ClustersHelper
end
end
def provider_icon(provider = nil)
case provider
when 'aws'
image_tag 'illustrations/logos/amazon_eks.svg', alt: s_('ClusterIntegration|Amazon EKS'), class: 'gl-h-full'
when 'gcp'
image_tag 'illustrations/logos/google_gke.svg', alt: s_('ClusterIntegration|Google GKE'), class: 'gl-h-full'
else
image_tag 'illustrations/logos/kubernetes.svg', alt: _('Kubernetes Cluster'), class: 'gl-h-full'
end
end
def render_gcp_signup_offer
return if Gitlab::CurrentSettings.current_application_settings.hide_third_party_offers?
return unless show_gcp_signup_offer?

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
# Placeholder class for model that is implemented in EE
# It reserves '&' as a reference prefix, but the table does not exists in CE
# It reserves '&' as a reference prefix, but the table does not exist in FOSS
class Epic < ApplicationRecord
include IgnorableColumns

View file

@ -2,7 +2,8 @@
.card-body.gl-responsive-table-row
.table-section.section-60
.table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Kubernetes cluster")
.table-mobile-content
.table-mobile-content.gl-display-flex.gl-align-items-center.gl-justify-content-end.gl-justify-content-md-start
.gl-w-6.gl-h-6.gl-mr-3.gl-display-flex.gl-align-items-center= provider_icon(cluster.provider_type)
= cluster.item_link(clusterable, html_options: { data: { qa_selector: 'cluster', qa_cluster_name: cluster.name } })
- if cluster.status_name == :creating
.spinner.ml-2.align-middle.has-tooltip{ title: s_("ClusterIntegration|Cluster being created") }

View file

@ -0,0 +1,5 @@
---
title: Added provider icon to cluster index display
merge_request: 31134
author:
type: added

View file

@ -0,0 +1,5 @@
---
title: Filter pipelines by trigger author and branch name
merge_request: 31386
author:
type: added

View file

@ -314,7 +314,7 @@ It is important to note that selective synchronization:
Selective synchronization restrictions are implemented on the **secondary** nodes,
not the **primary** node.
### Git operations on unreplicated respositories
### Git operations on unreplicated repositories
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2562) in GitLab 12.10 for HTTP(S) and in GitLab 13.0 for SSH.

View file

@ -195,12 +195,12 @@ authentication](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/configurati
gitlab_workhorse['enable'] = false
grafana['enable'] = false
# If you run a seperate monitoring node you can disable these services
# If you run a separate monitoring node you can disable these services
alertmanager['enable'] = false
prometheus['enable'] = false
# If you don't run a seperate monitoring node you can
# Enable Prometheus access & disable these extra services
# If you don't run a separate monitoring node you can
# enable Prometheus access & disable these extra services.
# This makes Prometheus listen on all interfaces. You must use firewalls to restrict access to this address/port.
# prometheus['listen_address'] = '0.0.0.0:9090'
# prometheus['monitor_kubernetes'] = false

View file

@ -21,6 +21,15 @@ Pipeline schedules can be used to also run [pipelines](index.md) at specific int
In addition to using the GitLab UI, pipeline schedules can be maintained using the
[Pipeline schedules API](../../api/pipeline_schedules.md).
## Prerequisites
In order for a scheduled pipeline to be created successfully:
- The schedule owner must have [permissions](../../user/permissions.md) to merge into the target branch.
- The pipeline configuration must be valid.
Otherwise the pipeline is not created.
## Configuring pipeline schedules
To schedule a pipeline for project:

View file

@ -180,7 +180,7 @@ using environment variables.
| `CLAIR_VULNERABILITIES_DB_URL` | (**DEPRECATED - use `CLAIR_DB_CONNECTION_STRING` instead**) This variable is explicitly set in the [services section](https://gitlab.com/gitlab-org/gitlab/-/blob/898c5da43504eba87b749625da50098d345b60d6/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml#L23) of the `Container-Scanning.gitlab-ci.yml` file and defaults to `clair-vulnerabilities-db`. This value represents the address that the [PostgreSQL server hosting the vulnerabilities definitions](https://hub.docker.com/r/arminc/clair-db) is running on and **shouldn't be changed** unless you're running the image locally as described in the [Running the standalone Container Scanning Tool](#running-the-standalone-container-scanning-tool) section. | `clair-vulnerabilities-db` |
| `CLAIR_DB_CONNECTION_STRING` | This variable represents the [connection string](https://www.postgresql.org/docs/9.3/libpq-connect.html#AEN39692) to the [PostgreSQL server hosting the vulnerabilities definitions](https://hub.docker.com/r/arminc/clair-db) database and **shouldn't be changed** unless you're running the image locally as described in the [Running the standalone Container Scanning Tool](#running-the-standalone-container-scanning-tool) section. The host value for the connection string must match the [alias](https://gitlab.com/gitlab-org/gitlab/-/blob/898c5da43504eba87b749625da50098d345b60d6/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml#L23) value of the `Container-Scanning.gitlab-ci.yml` template file, which defaults to `clair-vulnerabilities-db`. | `postgresql://postgres:password@clair-vulnerabilities-db:5432/postgres?sslmode=disable&statement_timeout=60000` |
| `CI_APPLICATION_REPOSITORY` | Docker repository URL for the image to be scanned. | `$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG` |
| `CI_APPLICATION_TAG` | Docker respository tag for the image to be scanned. | `$CI_COMMIT_SHA` |
| `CI_APPLICATION_TAG` | Docker repository tag for the image to be scanned. | `$CI_COMMIT_SHA` |
| `CLAIR_DB_IMAGE` | The Docker image name and tag for the [PostgreSQL server hosting the vulnerabilities definitions](https://hub.docker.com/r/arminc/clair-db). It can be useful to override this value with a specific version, for example, to provide a consistent set of vulnerabilities for integration testing purposes, or to refer to a locally hosted vulnerabilities database for an on-premise offline installation. | `arminc/clair-db:latest` |
| `CLAIR_DB_IMAGE_TAG` | (**DEPRECATED - use `CLAIR_DB_IMAGE` instead**) The Docker image tag for the [PostgreSQL server hosting the vulnerabilities definitions](https://hub.docker.com/r/arminc/clair-db). It can be useful to override this value with a specific version, for example, to provide a consistent set of vulnerabilities for integration testing purposes. | `latest` |
| `DOCKERFILE_PATH` | The path to the `Dockerfile` to be used for generating remediations. By default, the scanner will look for a file named `Dockerfile` in the root directory of the project, so this variable should only be configured if your `Dockerfile` is in a non-standard location, such as a subdirectory. See [Solutions for vulnerabilities](#solutions-for-vulnerabilities-auto-remediation) for more details. | `Dockerfile` |

View file

@ -18,6 +18,11 @@ measuring the accessibility of web sites, and has built a simple
This job outputs accessibility violations, warnings, and notices for each page
analyzed to a file called `accessibility`.
[Since GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/39425), in addition to the report artifact that is created, GitLab will also show the
Accessibility Report in the merge request widget area:
![Accessibility Merge Request Widget](img/accessibility_mr_widget_v13_0.png)
## Configure Accessibility Testing
This example shows how to run [pa11y](https://pa11y.org/)

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

View file

@ -15237,6 +15237,9 @@ msgstr ""
msgid "Pipelines|parent"
msgstr ""
msgid "Pipeline|Branch name"
msgstr ""
msgid "Pipeline|Commit"
msgstr ""
@ -21292,6 +21295,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
msgid "There was a problem fetching project branches."
msgstr ""
msgid "There was a problem fetching project users."
msgstr ""

View file

@ -195,6 +195,26 @@ describe Projects::PipelinesController do
end
end
context 'filter by ref' do
let!(:pipeline) { create(:ci_pipeline, :running, project: project, ref: 'branch-1') }
context 'when pipelines with the ref exists' do
it 'returns matched pipelines' do
get_pipelines_index_json(ref: 'branch-1')
check_pipeline_response(returned: 1, all: 1, running: 1, pending: 0, finished: 0)
end
end
context 'when no pipeline with the ref exists' do
it 'returns empty list' do
get_pipelines_index_json(ref: 'invalid-ref')
check_pipeline_response(returned: 0, all: 0, running: 0, pending: 0, finished: 0)
end
end
end
def get_pipelines_index_json(params = {})
get :index, params: {
namespace_id: project.namespace,

View file

@ -3,7 +3,13 @@ import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import PipelinesFilteredSearch from '~/pipelines/components/pipelines_filtered_search.vue';
import { users, mockSearch, pipelineWithStages } from '../mock_data';
import {
users,
mockSearch,
pipelineWithStages,
branches,
mockBranchesAfterMap,
} from '../mock_data';
import { GlFilteredSearch } from '@gitlab/ui';
describe('Pipelines filtered search', () => {
@ -30,6 +36,7 @@ describe('Pipelines filtered search', () => {
mock = new MockAdapter(axios);
jest.spyOn(Api, 'projectUsers').mockResolvedValue(users);
jest.spyOn(Api, 'branches').mockResolvedValue({ data: branches });
createComponent();
});
@ -52,9 +59,19 @@ describe('Pipelines filtered search', () => {
type: 'username',
icon: 'user',
title: 'Trigger author',
dataType: 'username',
unique: true,
triggerAuthors: users,
projectId: '21',
operators: [expect.objectContaining({ value: '=' })],
});
expect(getSearchToken('ref')).toMatchObject({
type: 'ref',
icon: 'branch',
title: 'Branch name',
unique: true,
branches: mockBranchesAfterMap,
projectId: '21',
operators: [expect.objectContaining({ value: '=' })],
});
});
@ -65,6 +82,12 @@ describe('Pipelines filtered search', () => {
expect(wrapper.vm.projectUsers).toEqual(users);
});
it('fetches and sets branches', () => {
expect(Api.branches).toHaveBeenCalled();
expect(wrapper.vm.projectBranches).toEqual(mockBranchesAfterMap);
});
it('emits filterPipelines on submit with correct filter', () => {
findFilteredSearch().vm.$emit('submit', mockSearch);

View file

@ -479,4 +479,90 @@ export const users = [
},
];
export const mockSearch = { type: 'username', value: { data: 'root', operator: '=' } };
export const branches = [
{
name: 'branch-1',
commit: {
id: '21fb056cc47dcf706670e6de635b1b326490ebdc',
short_id: '21fb056c',
created_at: '2020-05-07T10:58:28.000-04:00',
parent_ids: null,
title: 'Add new file',
message: 'Add new file',
author_name: 'Administrator',
author_email: 'admin@example.com',
authored_date: '2020-05-07T10:58:28.000-04:00',
committer_name: 'Administrator',
committer_email: 'admin@example.com',
committed_date: '2020-05-07T10:58:28.000-04:00',
web_url:
'http://192.168.1.22:3000/root/dag-pipeline/-/commit/21fb056cc47dcf706670e6de635b1b326490ebdc',
},
merged: false,
protected: false,
developers_can_push: false,
developers_can_merge: false,
can_push: true,
default: false,
web_url: 'http://192.168.1.22:3000/root/dag-pipeline/-/tree/branch-1',
},
{
name: 'branch-10',
commit: {
id: '66673b07efef254dab7d537f0433a40e61cf84fe',
short_id: '66673b07',
created_at: '2020-03-16T11:04:46.000-04:00',
parent_ids: null,
title: 'Update .gitlab-ci.yml',
message: 'Update .gitlab-ci.yml',
author_name: 'Administrator',
author_email: 'admin@example.com',
authored_date: '2020-03-16T11:04:46.000-04:00',
committer_name: 'Administrator',
committer_email: 'admin@example.com',
committed_date: '2020-03-16T11:04:46.000-04:00',
web_url:
'http://192.168.1.22:3000/root/dag-pipeline/-/commit/66673b07efef254dab7d537f0433a40e61cf84fe',
},
merged: false,
protected: false,
developers_can_push: false,
developers_can_merge: false,
can_push: true,
default: false,
web_url: 'http://192.168.1.22:3000/root/dag-pipeline/-/tree/branch-10',
},
{
name: 'branch-11',
commit: {
id: '66673b07efef254dab7d537f0433a40e61cf84fe',
short_id: '66673b07',
created_at: '2020-03-16T11:04:46.000-04:00',
parent_ids: null,
title: 'Update .gitlab-ci.yml',
message: 'Update .gitlab-ci.yml',
author_name: 'Administrator',
author_email: 'admin@example.com',
authored_date: '2020-03-16T11:04:46.000-04:00',
committer_name: 'Administrator',
committer_email: 'admin@example.com',
committed_date: '2020-03-16T11:04:46.000-04:00',
web_url:
'http://192.168.1.22:3000/root/dag-pipeline/-/commit/66673b07efef254dab7d537f0433a40e61cf84fe',
},
merged: false,
protected: false,
developers_can_push: false,
developers_can_merge: false,
can_push: true,
default: false,
web_url: 'http://192.168.1.22:3000/root/dag-pipeline/-/tree/branch-11',
},
];
export const mockSearch = [
{ type: 'username', value: { data: 'root', operator: '=' } },
{ type: 'ref', value: { data: 'master', operator: '=' } },
];
export const mockBranchesAfterMap = ['branch-1', 'branch-10', 'branch-11'];

View file

@ -5,7 +5,7 @@ import axios from '~/lib/utils/axios_utils';
import waitForPromises from 'helpers/wait_for_promises';
import PipelinesComponent from '~/pipelines/components/pipelines.vue';
import Store from '~/pipelines/stores/pipelines_store';
import { pipelineWithStages, stageReply, users, mockSearch } from './mock_data';
import { pipelineWithStages, stageReply, users, mockSearch, branches } from './mock_data';
import { GlFilteredSearch } from '@gitlab/ui';
describe('Pipelines', () => {
@ -63,7 +63,9 @@ describe('Pipelines', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
pipelines = getJSONFixture(jsonFixtureName);
jest.spyOn(Api, 'projectUsers').mockResolvedValue(users);
jest.spyOn(Api, 'branches').mockResolvedValue({ data: branches });
});
afterEach(() => {
@ -674,9 +676,9 @@ describe('Pipelines', () => {
it('updates request data and query params on filter submit', () => {
const updateContentMock = jest.spyOn(wrapper.vm, 'updateContent');
const expectedQueryParams = { page: '1', scope: 'all', username: 'root' };
const expectedQueryParams = { page: '1', scope: 'all', username: 'root', ref: 'master' };
findFilteredSearch().vm.$emit('submit', [mockSearch]);
findFilteredSearch().vm.$emit('submit', mockSearch);
expect(wrapper.vm.requestData).toEqual(expectedQueryParams);
expect(updateContentMock).toHaveBeenCalledWith(expectedQueryParams);

View file

@ -0,0 +1,89 @@
import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import PipelineBranchNameToken from '~/pipelines/components/tokens/pipeline_branch_name_token.vue';
import { branches } from '../mock_data';
describe('Pipeline Branch Name Token', () => {
let wrapper;
const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const stubs = {
GlFilteredSearchToken: {
template: `<div><slot name="suggestions"></slot></div>`,
},
};
const defaultProps = {
config: {
type: 'ref',
icon: 'branch',
title: 'Branch name',
dataType: 'ref',
unique: true,
branches,
projectId: '21',
},
value: {
data: '',
},
};
const createComponent = (options, data) => {
wrapper = shallowMount(PipelineBranchNameToken, {
propsData: {
...defaultProps,
},
data() {
return {
...data,
};
},
...options,
});
};
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('passes config correctly', () => {
expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config);
});
describe('displays loading icon correctly', () => {
it('shows loading icon', () => {
createComponent({ stubs }, { loading: true });
expect(findLoadingIcon().exists()).toBe(true);
});
it('does not show loading icon', () => {
createComponent({ stubs }, { loading: false });
expect(findLoadingIcon().exists()).toBe(false);
});
});
describe('shows branches correctly', () => {
it('renders all trigger authors', () => {
createComponent({ stubs }, { branches, loading: false });
expect(findAllFilteredSearchSuggestions()).toHaveLength(branches.length);
});
it('renders only the branch searched for', () => {
const mockBranches = ['master'];
createComponent({ stubs }, { branches: mockBranches, loading: false });
expect(findAllFilteredSearchSuggestions()).toHaveLength(mockBranches.length);
});
});
});

View file

@ -1,4 +1,4 @@
import { GlFilteredSearchToken, GlFilteredSearchSuggestion } from '@gitlab/ui';
import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import PipelineTriggerAuthorToken from '~/pipelines/components/tokens/pipeline_trigger_author_token.vue';
import { users } from '../mock_data';
@ -8,6 +8,7 @@ describe('Pipeline Trigger Author Token', () => {
const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const stubs = {
GlFilteredSearchToken: {
@ -24,20 +25,27 @@ describe('Pipeline Trigger Author Token', () => {
unique: true,
triggerAuthors: users,
},
value: {
data: '',
},
};
const createComponent = (props = {}, options) => {
const createComponent = (options, data) => {
wrapper = shallowMount(PipelineTriggerAuthorToken, {
propsData: {
...props,
...defaultProps,
},
data() {
return {
...data,
};
},
...options,
});
};
beforeEach(() => {
createComponent({ value: { data: '' } });
createComponent();
});
afterEach(() => {
@ -49,14 +57,41 @@ describe('Pipeline Trigger Author Token', () => {
expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config);
});
describe('displays loading icon correctly', () => {
it('shows loading icon', () => {
createComponent({ stubs }, { loading: true });
expect(findLoadingIcon().exists()).toBe(true);
});
it('does not show loading icon', () => {
createComponent({ stubs }, { loading: false });
expect(findLoadingIcon().exists()).toBe(false);
});
});
describe('shows trigger authors correctly', () => {
beforeEach(() => {});
it('renders all trigger authors', () => {
createComponent({ value: { data: '' } }, { stubs });
expect(findAllFilteredSearchSuggestions()).toHaveLength(7);
createComponent({ stubs }, { users, loading: false });
// should have length of all users plus the static 'Any' option
expect(findAllFilteredSearchSuggestions()).toHaveLength(users.length + 1);
});
it('renders only the trigger author searched for', () => {
createComponent({ value: { data: 'root' } }, { stubs });
createComponent(
{ stubs },
{
users: [
{ name: 'Arnold', username: 'admin', state: 'active', avatar_url: 'avatar-link' },
],
loading: false,
},
);
expect(findAllFilteredSearchSuggestions()).toHaveLength(2);
});
});

View file

@ -59,6 +59,32 @@ describe ClustersHelper do
end
end
describe '#provider_icon' do
it 'will return GCP logo with gcp argument' do
logo = helper.provider_icon('gcp')
expect(logo).to match(%r(img alt="Google GKE" data-src="|/illustrations/logos/google_gke|svg))
end
it 'will return AWS logo with aws argument' do
logo = helper.provider_icon('aws')
expect(logo).to match(%r(img alt="Amazon EKS" data-src="|/illustrations/logos/amazon_eks|svg))
end
it 'will return default logo with unknown provider' do
logo = helper.provider_icon('unknown')
expect(logo).to match(%r(img alt="Kubernetes Cluster" data-src="|/illustrations/logos/kubernetes|svg))
end
it 'will return default logo when provider is empty' do
logo = helper.provider_icon
expect(logo).to match(%r(img alt="Kubernetes Cluster" data-src="|/illustrations/logos/kubernetes|svg))
end
end
describe '#cluster_type_label' do
subject { helper.cluster_type_label(cluster_type) }