Commit d90b6ee0 authored by Stan Hu's avatar Stan Hu

Merge branch 'ce-to-ee-2018-09-14' into 'master'

CE upstream - 2018-09-14 09:21 UTC

See merge request gitlab-org/gitlab-ee!7374
parents c6d2ac22 d2abdaaf
...@@ -11,8 +11,10 @@ export default () => ({ ...@@ -11,8 +11,10 @@ export default () => ({
endpoint: '', endpoint: '',
basePath: '', basePath: '',
commit: null, commit: null,
startVersion: null,
diffFiles: [], diffFiles: [],
mergeRequestDiffs: [], mergeRequestDiffs: [],
mergeRequestDiff: null,
diffLineCommentForms: {}, diffLineCommentForms: {},
diffViewType: viewTypeFromQueryString || viewTypeFromCookie || defaultViewType, diffViewType: viewTypeFromQueryString || viewTypeFromCookie || defaultViewType,
}); });
...@@ -3,10 +3,10 @@ import * as getters from '../getters'; ...@@ -3,10 +3,10 @@ import * as getters from '../getters';
import mutations from '../mutations'; import mutations from '../mutations';
import createState from './diff_state'; import createState from './diff_state';
export default { export default () => ({
namespaced: true, namespaced: true,
state: createState(), state: createState(),
getters, getters,
actions, actions,
mutations, mutations,
}; });
...@@ -3,7 +3,7 @@ import { ...@@ -3,7 +3,7 @@ import {
getParameterByName, getParameterByName,
getUrlParamsArray, getUrlParamsArray,
} from '~/lib/utils/common_utils'; } from '~/lib/utils/common_utils';
import IssuesFilteredSearchTokenKeys from '~/filtered_search/issues_filtered_search_token_keys'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { visitUrl } from '../lib/utils/url_utility'; import { visitUrl } from '../lib/utils/url_utility';
import Flash from '../flash'; import Flash from '../flash';
import FilteredSearchContainer from './container'; import FilteredSearchContainer from './container';
...@@ -23,7 +23,7 @@ export default class FilteredSearchManager { ...@@ -23,7 +23,7 @@ export default class FilteredSearchManager {
isGroup = false, isGroup = false,
isGroupAncestor = true, isGroupAncestor = true,
isGroupDecendent = false, isGroupDecendent = false,
filteredSearchTokenKeys = IssuesFilteredSearchTokenKeys, filteredSearchTokenKeys = IssuableFilteredSearchTokenKeys,
stateFiltersSelector = '.issues-state-filters', stateFiltersSelector = '.issues-state-filters',
}) { }) {
this.isGroup = isGroup; this.isGroup = isGroup;
......
...@@ -71,7 +71,7 @@ export const conditions = [{ ...@@ -71,7 +71,7 @@ export const conditions = [{
value: 'none', value: 'none',
}]; }];
const IssuesFilteredSearchTokenKeys = const IssuableFilteredSearchTokenKeys =
new FilteredSearchTokenKeys(tokenKeys, alternativeTokenKeys, conditions); new FilteredSearchTokenKeys(tokenKeys, alternativeTokenKeys, conditions);
export default IssuesFilteredSearchTokenKeys; export default IssuableFilteredSearchTokenKeys;
...@@ -9,7 +9,7 @@ Vue.use(Vuex); ...@@ -9,7 +9,7 @@ Vue.use(Vuex);
export default new Vuex.Store({ export default new Vuex.Store({
modules: { modules: {
page: mrPageModule, page: mrPageModule,
notes: notesModule, notes: notesModule(),
diffs: diffsModule, diffs: diffsModule(),
}, },
}); });
import Vue from 'vue'; import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import * as actions from './actions'; import notesModule from './modules';
import * as getters from './getters';
import mutations from './mutations';
import module from './modules';
Vue.use(Vuex); Vue.use(Vuex);
export default () => export default () =>
new Vuex.Store({ new Vuex.Store(notesModule());
state: module.state,
actions,
getters,
mutations,
});
...@@ -2,7 +2,7 @@ import * as actions from '../actions'; ...@@ -2,7 +2,7 @@ import * as actions from '../actions';
import * as getters from '../getters'; import * as getters from '../getters';
import mutations from '../mutations'; import mutations from '../mutations';
export default { export default () => ({
state: { state: {
discussions: [], discussions: [],
targetNoteHash: null, targetNoteHash: null,
...@@ -24,4 +24,4 @@ export default { ...@@ -24,4 +24,4 @@ export default {
actions, actions,
getters, getters,
mutations, mutations,
}; });
import projectSelect from '~/project_select'; import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search'; import initFilteredSearch from '~/pages/search/init_filtered_search';
import IssuesFilteredSearchTokenKeys from '~/filtered_search/issues_filtered_search_token_keys'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants'; import { FILTERED_SEARCH } from '~/pages/constants';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({ initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS, page: FILTERED_SEARCH.MERGE_REQUESTS,
isGroupDecendent: true, isGroupDecendent: true,
filteredSearchTokenKeys: IssuesFilteredSearchTokenKeys, filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
}); });
projectSelect(); projectSelect();
}); });
...@@ -2,14 +2,14 @@ import IssuableIndex from '~/issuable_index'; ...@@ -2,14 +2,14 @@ import IssuableIndex from '~/issuable_index';
import ShortcutsNavigation from '~/shortcuts_navigation'; import ShortcutsNavigation from '~/shortcuts_navigation';
import UsersSelect from '~/users_select'; import UsersSelect from '~/users_select';
import initFilteredSearch from '~/pages/search/init_filtered_search'; import initFilteredSearch from '~/pages/search/init_filtered_search';
import IssuesFilteredSearchTokenKeys from '~/filtered_search/issues_filtered_search_token_keys'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants'; import { FILTERED_SEARCH } from '~/pages/constants';
import { ISSUABLE_INDEX } from '~/pages/projects/constants'; import { ISSUABLE_INDEX } from '~/pages/projects/constants';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({ initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS, page: FILTERED_SEARCH.MERGE_REQUESTS,
filteredSearchTokenKeys: IssuesFilteredSearchTokenKeys, filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
}); });
new IssuableIndex(ISSUABLE_INDEX.MERGE_REQUEST); // eslint-disable-line no-new new IssuableIndex(ISSUABLE_INDEX.MERGE_REQUEST); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new new ShortcutsNavigation(); // eslint-disable-line no-new
......
class Admin::RunnersController < Admin::ApplicationController class Admin::RunnersController < Admin::ApplicationController
before_action :runner, except: :index before_action :runner, except: :index
# rubocop: disable CodeReuse/ActiveRecord
def index def index
finder = Admin::RunnersFinder.new(params: params) finder = Admin::RunnersFinder.new(params: params)
@runners = finder.execute @runners = finder.execute
@active_runners_cnt = Ci::Runner.online.count @active_runners_count = Ci::Runner.online.count
@sort = finder.sort_key @sort = finder.sort_key
end end
# rubocop: enable CodeReuse/ActiveRecord
def show def show
assign_builds_and_projects assign_builds_and_projects
......
...@@ -22,7 +22,7 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController ...@@ -22,7 +22,7 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController
private private
def group_milestones def group_milestones
groups = GroupsFinder.new(current_user, all_available: true).execute groups = GroupsFinder.new(current_user, all_available: false).execute
DashboardGroupMilestone.build_collection(groups) DashboardGroupMilestone.build_collection(groups)
end end
......
...@@ -43,8 +43,7 @@ class Admin::RunnersFinder < UnionFinder ...@@ -43,8 +43,7 @@ class Admin::RunnersFinder < UnionFinder
end end
def sort! def sort!
sort = sort_key == 'contacted_asc' ? { contacted_at: :asc } : { created_at: :desc } @runners = @runners.order_by(sort_key)
@runners = @runners.order(sort)
end end
def paginate! def paginate!
......
...@@ -32,6 +32,12 @@ module Ci ...@@ -32,6 +32,12 @@ module Ci
scope :active, -> { where(active: true) } scope :active, -> { where(active: true) }
scope :paused, -> { where(active: false) } scope :paused, -> { where(active: false) }
scope :online, -> { where('contacted_at > ?', contact_time_deadline) } scope :online, -> { where('contacted_at > ?', contact_time_deadline) }
# The following query using negation is cheaper than using `contacted_at <= ?`
# because there are less runners online than have been created. The
# resulting query is quickly finding online ones and then uses the regular
# indexed search and rejects the ones that are in the previous set. If we
# did `contacted_at <= ?` the query would effectively have to do a seq
# scan.
scope :offline, -> { where.not(id: online) } scope :offline, -> { where.not(id: online) }
scope :ordered, -> { order(id: :desc) } scope :ordered, -> { order(id: :desc) }
...@@ -67,6 +73,9 @@ module Ci ...@@ -67,6 +73,9 @@ module Ci
.project_type .project_type
end end
scope :order_contacted_at_asc, -> { order(contacted_at: :asc) }
scope :order_created_at_desc, -> { order(created_at: :desc) }
validate :tag_constraints validate :tag_constraints
validates :access_level, presence: true validates :access_level, presence: true
validates :runner_type, presence: true validates :runner_type, presence: true
...@@ -119,6 +128,14 @@ module Ci ...@@ -119,6 +128,14 @@ module Ci
ONLINE_CONTACT_TIMEOUT.ago ONLINE_CONTACT_TIMEOUT.ago
end end
def self.order_by(order)
if order == 'contacted_asc'
order_contacted_at_asc
else
order_created_at_desc
end
end
def set_default_values def set_default_values
self.token = SecureRandom.hex(15) if self.token.blank? self.token = SecureRandom.hex(15) if self.token.blank?
end end
......
...@@ -73,19 +73,10 @@ module Clusters ...@@ -73,19 +73,10 @@ module Clusters
"clientSecret" => oauth_application.secret, "clientSecret" => oauth_application.secret,
"callbackUrl" => callback_url "callbackUrl" => callback_url
} }
},
"singleuser" => {
"extraEnv" => {
"GITLAB_PROJECT_ID" => project_id
}
} }
} }
end end
def project_id
cluster&.project&.id
end
def gitlab_url def gitlab_url
Gitlab.config.gitlab.url Gitlab.config.gitlab.url
end end
......
...@@ -13,7 +13,7 @@ class DashboardGroupMilestone < GlobalMilestone ...@@ -13,7 +13,7 @@ class DashboardGroupMilestone < GlobalMilestone
end end
def self.build_collection(groups) def self.build_collection(groups)
MilestonesFinder.new(group_ids: groups.pluck(:id)).execute.map { |m| new(m) } # rubocop: disable CodeReuse/Finder MilestonesFinder.new(group_ids: groups.select(:id)).execute.map { |m| new(m) } # rubocop: disable CodeReuse/Finder
end end
override :group_milestone? override :group_milestone?
......
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
.bs-callout .bs-callout
%p %p
= _('Runners currently online: %{active_runners_cnt}') % { active_runners_cnt: @active_runners_cnt } = _('Runners currently online: %{active_runners_count}') % { active_runners_count: @active_runners_count }
.row-content-block.second-block .row-content-block.second-block
= form_tag admin_runners_path, id: 'runners-search', method: :get, class: 'filter-form js-filter-form' do = form_tag admin_runners_path, id: 'runners-search', method: :get, class: 'filter-form js-filter-form' do
...@@ -68,13 +68,13 @@ ...@@ -68,13 +68,13 @@
#js-dropdown-hint.filtered-search-input-dropdown-menu.dropdown-menu.hint-dropdown #js-dropdown-hint.filtered-search-input-dropdown-menu.dropdown-menu.hint-dropdown
%ul{ data: { dropdown: true } } %ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { action: 'submit' } } %li.filter-dropdown-item{ data: { action: 'submit' } }
%button.btn.btn-link = button_tag class: %w[btn btn-link] do
= icon('search') = icon('search')
%span %span
= _('Press Enter or click to search') = _('Press Enter or click to search')
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item %li.filter-dropdown-item
%button.btn.btn-link = button_tag class: %w[btn btn-link] do
-# Encapsulate static class name `{{icon}}` inside #{} to bypass -# Encapsulate static class name `{{icon}}` inside #{} to bypass
-# haml lint's ClassAttributeWithStaticValue -# haml lint's ClassAttributeWithStaticValue
%i.fa{ class: "#{'{{icon}}'}" } %i.fa{ class: "#{'{{icon}}'}" }
...@@ -86,9 +86,9 @@ ...@@ -86,9 +86,9 @@
%ul{ data: { dropdown: true } } %ul{ data: { dropdown: true } }
- Ci::Runner::AVAILABLE_STATUSES.each do |status| - Ci::Runner::AVAILABLE_STATUSES.each do |status|
%li.filter-dropdown-item{ data: { value: status } } %li.filter-dropdown-item{ data: { value: status } }
%button.btn.btn-link = button_tag class: %w[btn btn-link] do
= status.titleize = status.titleize
%button.clear-search.hidden{ type: 'button' } = button_tag class: %w[clear-search hidden] do
= icon('times') = icon('times')
.filter-dropdown-container .filter-dropdown-container
= render 'sort_dropdown' = render 'sort_dropdown'
......
---
title: Filter group milestones based on user membership.
merge_request: 21660
author:
type: fixed
...@@ -3,7 +3,7 @@ import { ...@@ -3,7 +3,7 @@ import {
tokenKeys, tokenKeys,
alternativeTokenKeys, alternativeTokenKeys,
conditions, conditions,
} from '~/filtered_search/issues_filtered_search_token_keys'; } from '~/filtered_search/issuable_filtered_search_token_keys';
const weightTokenKey = { const weightTokenKey = {
key: 'weight', key: 'weight',
...@@ -27,6 +27,11 @@ const weightConditions = [ ...@@ -27,6 +27,11 @@ const weightConditions = [
}, },
]; ];
/**
* Filter tokens for issues in EE.
*
* @type {FilteredSearchTokenKeys}
*/
const IssuesFilteredSearchTokenKeysEE = new FilteredSearchTokenKeys( const IssuesFilteredSearchTokenKeysEE = new FilteredSearchTokenKeys(
[...tokenKeys, weightTokenKey], [...tokenKeys, weightTokenKey],
alternativeTokenKeys, alternativeTokenKeys,
......
...@@ -14,7 +14,7 @@ module API ...@@ -14,7 +14,7 @@ module API
use :pagination use :pagination
end end
get do get do
runners = filter_runners(current_user.ci_owned_runners, params[:scope], only: Ci::Runner::AVAILABLE_STATUSES) runners = filter_runners(current_user.ci_owned_runners, params[:scope], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
present paginate(runners), with: Entities::Runner present paginate(runners), with: Entities::Runner
end end
...@@ -160,12 +160,10 @@ module API ...@@ -160,12 +160,10 @@ module API
end end
helpers do helpers do
def filter_runners(runners, scope, only: nil) def filter_runners(runners, scope, allowed_scopes: ::Ci::Runner::AVAILABLE_SCOPES)
return runners unless scope.present? return runners unless scope.present?
available_scopes = only || ::Ci::Runner::AVAILABLE_SCOPES unless allowed_scopes.include?(scope)
unless available_scopes.include?(scope)
render_api_error!('Scope contains invalid value', 400) render_api_error!('Scope contains invalid value', 400)
end end
......
...@@ -6480,7 +6480,7 @@ msgstr "" ...@@ -6480,7 +6480,7 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, even on your local machine." msgid "Runners can be placed on separate users, servers, even on your local machine."
msgstr "" msgstr ""
msgid "Runners currently online: %{active_runners_cnt}" msgid "Runners currently online: %{active_runners_count}"
msgstr "" msgstr ""
msgid "Runners page" msgid "Runners page"
......
...@@ -3,9 +3,11 @@ require 'spec_helper' ...@@ -3,9 +3,11 @@ require 'spec_helper'
describe Dashboard::MilestonesController do describe Dashboard::MilestonesController do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:group) { create(:group) } let(:group) { create(:group) }
let(:public_group) { create(:group, :public) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project_milestone) { create(:milestone, project: project) } let(:project_milestone) { create(:milestone, project: project) }
let(:group_milestone) { create(:milestone, group: group) } let(:group_milestone) { create(:milestone, group: group) }
let!(:public_milestone) { create(:milestone, group: public_group) }
let(:milestone) do let(:milestone) do
DashboardMilestone.build( DashboardMilestone.build(
[project], [project],
...@@ -43,13 +45,13 @@ describe Dashboard::MilestonesController do ...@@ -43,13 +45,13 @@ describe Dashboard::MilestonesController do
end end
describe "#index" do describe "#index" do
it 'should contain group and project milestones' do it 'returns group and project milestones to which the user belongs' do
get :index, format: :json get :index, format: :json
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(json_response.size).to eq(2) expect(json_response.size).to eq(2)
expect(json_response.map { |i| i["first_milestone"]["id"] }).to include(group_milestone.id, project_milestone.id) expect(json_response.map { |i| i["first_milestone"]["id"] }).to match_array([group_milestone.id, project_milestone.id])
expect(json_response.map { |i| i["group_name"] }).to include(group.name) expect(json_response.map { |i| i["group_name"] }.compact).to match_array(group.name)
end end
end end
end end
...@@ -17,8 +17,9 @@ describe 'Dashboard > Milestones' do ...@@ -17,8 +17,9 @@ describe 'Dashboard > Milestones' do
let(:project) { create(:project, namespace: user.namespace) } let(:project) { create(:project, namespace: user.namespace) }
let!(:milestone) { create(:milestone, project: project) } let!(:milestone) { create(:milestone, project: project) }
let!(:milestone2) { create(:milestone, group: group) } let!(:milestone2) { create(:milestone, group: group) }
before do before do
project.add_maintainer(user) group.add_developer(user)
sign_in(user) sign_in(user)
visit dashboard_milestones_path visit dashboard_milestones_path
end end
......
// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 import Vue from 'vue';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { TEST_HOST } from 'spec/test_constants';
import App from '~/diffs/components/app.vue';
import createDiffsStore from '../create_diffs_store';
describe('diffs/components/app', () => {
const oldMrTabs = window.mrTabs;
const Component = Vue.extend(App);
let vm;
beforeEach(() => {
// setup globals (needed for component to mount :/)
window.mrTabs = jasmine.createSpyObj('mrTabs', ['resetViewContainer']);
// setup component
const store = createDiffsStore();
store.state.diffs.isLoading = false;
vm = mountComponentWithStore(Component, {
store,
props: {
endpoint: `${TEST_HOST}/diff/endpoint`,
projectPath: 'namespace/project',
currentUser: {},
},
});
});
afterEach(() => {
// reset globals
window.mrTabs = oldMrTabs;
// reset component
vm.$destroy();
});
it('shows comments message, with commit', done => {
vm.$store.state.diffs.commit = {};
vm.$nextTick()
.then(() => {
expect(vm.$el).toContainText('Only comments from the following commit are shown below');
})
.then(done)
.catch(done.fail);
});
it('shows comments message, with old mergeRequestDiff', done => {
vm.$store.state.diffs.mergeRequestDiff = { latest: false };
vm.$nextTick()
.then(() => {
expect(vm.$el).toContainText("Not all comments are displayed because you're viewing an old version of the diff.");
})
.then(done)
.catch(done.fail);
});
it('shows comments message, with startVersion', done => {
vm.$store.state.diffs.startVersion = 'test';
vm.$nextTick()
.then(() => {
expect(vm.$el).toContainText("Not all comments are displayed because you're comparing two versions of the diff.");
})
.then(done)
.catch(done.fail);
});
});
...@@ -8,7 +8,7 @@ describe('ChangedFiles', () => { ...@@ -8,7 +8,7 @@ describe('ChangedFiles', () => {
const Component = Vue.extend(changedFiles); const Component = Vue.extend(changedFiles);
const store = new Vuex.Store({ const store = new Vuex.Store({
modules: { modules: {
diffs: diffsModule, diffs: diffsModule(),
}, },
}); });
......
...@@ -16,8 +16,8 @@ describe('diff_file_header', () => { ...@@ -16,8 +16,8 @@ describe('diff_file_header', () => {
const store = new Vuex.Store({ const store = new Vuex.Store({
modules: { modules: {
diffs: diffsModule, diffs: diffsModule(),
notes: notesModule, notes: notesModule(),
}, },
}); });
...@@ -450,13 +450,14 @@ describe('diff_file_header', () => { ...@@ -450,13 +450,14 @@ describe('diff_file_header', () => {
propsCopy.diffFile.deletedFile = true; propsCopy.diffFile.deletedFile = true;
const discussionGetter = () => [diffDiscussionMock]; const discussionGetter = () => [diffDiscussionMock];
notesModule.getters.discussions = discussionGetter; const notesModuleMock = notesModule();
notesModuleMock.getters.discussions = discussionGetter;
vm = mountComponentWithStore(Component, { vm = mountComponentWithStore(Component, {
props: propsCopy, props: propsCopy,
store: new Vuex.Store({ store: new Vuex.Store({
modules: { modules: {
diffs: diffsModule, diffs: diffsModule(),
notes: notesModule, notes: notesModuleMock,
}, },
}), }),
}); });
......
import Vue from 'vue';
import Vuex from 'vuex';
import diffsModule from '~/diffs/store/modules';
import notesModule from '~/notes/stores/modules';
Vue.use(Vuex);
export default function createDiffsStore() {
return new Vuex.Store({
modules: {
diffs: diffsModule(),
notes: notesModule(),
},
});
}
import Vue from 'vue'; import Vue from 'vue';
import eventHub from '~/filtered_search/event_hub'; import eventHub from '~/filtered_search/event_hub';
import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content.vue'; import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content.vue';
import IssuesFilteredSearchTokenKeys from '~/filtered_search/issues_filtered_search_token_keys'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
const createComponent = (propsData) => { const createComponent = (propsData) => {
const Component = Vue.extend(RecentSearchesDropdownContent); const Component = Vue.extend(RecentSearchesDropdownContent);
...@@ -18,14 +18,14 @@ const trimMarkupWhitespace = text => text.replace(/(\n|\s)+/gm, ' ').trim(); ...@@ -18,14 +18,14 @@ const trimMarkupWhitespace = text => text.replace(/(\n|\s)+/gm, ' ').trim();
describe('RecentSearchesDropdownContent', () => { describe('RecentSearchesDropdownContent', () => {
const propsDataWithoutItems = { const propsDataWithoutItems = {
items: [], items: [],
allowedKeys: IssuesFilteredSearchTokenKeys.getKeys(), allowedKeys: IssuableFilteredSearchTokenKeys.getKeys(),
}; };
const propsDataWithItems = { const propsDataWithItems = {
items: [ items: [
'foo', 'foo',
'author:@root label:~foo bar', 'author:@root label:~foo bar',
], ],
allowedKeys: IssuesFilteredSearchTokenKeys.getKeys(), allowedKeys: IssuableFilteredSearchTokenKeys.getKeys(),
}; };
let vm; let vm;
......
import DropdownUtils from '~/filtered_search/dropdown_utils'; import DropdownUtils from '~/filtered_search/dropdown_utils';
import DropdownUser from '~/filtered_search/dropdown_user'; import DropdownUser from '~/filtered_search/dropdown_user';
import FilteredSearchTokenizer from '~/filtered_search/filtered_search_tokenizer'; import FilteredSearchTokenizer from '~/filtered_search/filtered_search_tokenizer';
import IssuesFilteredSearchTokenKeys from '~/filtered_search/issues_filtered_search_token_keys'; import IssuableFilteredTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
describe('Dropdown User', () => { describe('Dropdown User', () => {
describe('getSearchInput', () => { describe('getSearchInput', () => {
...@@ -14,7 +14,7 @@ describe('Dropdown User', () => { ...@@ -14,7 +14,7 @@ describe('Dropdown User', () => {
spyOn(DropdownUtils, 'getSearchInput').and.callFake(() => {}); spyOn(DropdownUtils, 'getSearchInput').and.callFake(() => {});
dropdownUser = new DropdownUser({ dropdownUser = new DropdownUser({
tokenKeys: IssuesFilteredSearchTokenKeys, tokenKeys: IssuableFilteredTokenKeys,
}); });
}); });
......
import DropdownUtils from '~/filtered_search/dropdown_utils'; import DropdownUtils from '~/filtered_search/dropdown_utils';
import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager'; import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager';
import IssuesFilteredSearchTokenKeys from '~/filtered_search/issues_filtered_search_token_keys'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper'; import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper';
describe('Dropdown Utils', () => { describe('Dropdown Utils', () => {
...@@ -137,7 +137,7 @@ describe('Dropdown Utils', () => { ...@@ -137,7 +137,7 @@ describe('Dropdown Utils', () => {
`); `);
input = document.getElementById('test'); input = document.getElementById('test');
allowedKeys = IssuesFilteredSearchTokenKeys.getKeys(); allowedKeys = IssuableFilteredSearchTokenKeys.getKeys();
}); });
function config() { function config() {
......
import RecentSearchesService from '~/filtered_search/services/recent_searches_service'; import RecentSearchesService from '~/filtered_search/services/recent_searches_service';
import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error'; import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error';
import RecentSearchesRoot from '~/filtered_search/recent_searches_root'; import RecentSearchesRoot from '~/filtered_search/recent_searches_root';
import IssuesFilteredSearchTokenKeys from '~/filtered_search/issues_filtered_search_token_keys'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import '~/lib/utils/common_utils'; import '~/lib/utils/common_utils';
import DropdownUtils from '~/filtered_search/dropdown_utils'; import DropdownUtils from '~/filtered_search/dropdown_utils';
import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens'; import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens';
...@@ -86,7 +86,7 @@ describe('Filtered Search Manager', function () { ...@@ -86,7 +86,7 @@ describe('Filtered Search Manager', function () {
expect(RecentSearchesService.isAvailable).toHaveBeenCalled(); expect(RecentSearchesService.isAvailable).toHaveBeenCalled();
expect(RecentSearchesStoreSpy).toHaveBeenCalledWith({ expect(RecentSearchesStoreSpy).toHaveBeenCalledWith({
isLocalStorageAvailable, isLocalStorageAvailable,
allowedKeys: IssuesFilteredSearchTokenKeys.getKeys(), allowedKeys: IssuableFilteredSearchTokenKeys.getKeys(),
}); });
}); });
}); });
......
import IssuesFilteredSearchTokenKeys from '~/filtered_search/issues_filtered_search_token_keys'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import FilteredSearchTokenizer from '~/filtered_search/filtered_search_tokenizer'; import FilteredSearchTokenizer from '~/filtered_search/filtered_search_tokenizer';
describe('Filtered Search Tokenizer', () => { describe('Filtered Search Tokenizer', () => {
const allowedKeys = IssuesFilteredSearchTokenKeys.getKeys(); const allowedKeys = IssuableFilteredSearchTokenKeys.getKeys();
describe('processTokens', () => { describe('processTokens', () => {
it('returns for input containing only search value', () => { it('returns for input containing only search value', () => {
......
...@@ -797,4 +797,22 @@ describe Ci::Runner do ...@@ -797,4 +797,22 @@ describe Ci::Runner do
expect { subject.destroy }.to change { described_class.count }.by(-1) expect { subject.destroy }.to change { described_class.count }.by(-1)
end end
end end
describe '.order_by' do
it 'supports ordering by the contact date' do
runner1 = create(:ci_runner, contacted_at: 1.year.ago)
runner2 = create(:ci_runner, contacted_at: 1.month.ago)
runners = described_class.order_by('contacted_asc')
expect(runners).to eq([runner1, runner2])
end
it 'supports ordering by the creation date' do
runner1 = create(:ci_runner, created_at: 1.year.ago)
runner2 = create(:ci_runner, created_at: 1.month.ago)
runners = described_class.order_by('created_asc')
expect(runners).to eq([runner2, runner1])
end
end
end end
...@@ -108,17 +108,8 @@ describe Clusters::Applications::Jupyter do ...@@ -108,17 +108,8 @@ describe Clusters::Applications::Jupyter do
expect(values).to include('rbac') expect(values).to include('rbac')
expect(values).to include('proxy') expect(values).to include('proxy')
expect(values).to include('auth') expect(values).to include('auth')
expect(values).to include('singleuser')
expect(values).to match(/clientId: '?#{application.oauth_application.uid}/) expect(values).to match(/clientId: '?#{application.oauth_application.uid}/)
expect(values).to match(/callbackUrl: '?#{application.callback_url}/) expect(values).to match(/callbackUrl: '?#{application.callback_url}/)
end end
context 'when cluster belongs to a project' do
let(:project) { application.cluster.first_project }
it 'sets GitLab project id' do
expect(values).to match(/GITLAB_PROJECT_ID: '?#{project.id}/)
end
end
end end
end end
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