Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/jobs/components/job_app.vue2
-rw-r--r--app/assets/javascripts/jobs/components/job_log_json.vue10
-rw-r--r--app/controllers/application_controller.rb1
-rw-r--r--app/services/projects/after_import_service.rb9
-rw-r--r--app/views/admin/application_settings/_protected_paths.html.haml2
-rw-r--r--changelogs/unreleased/mc-fixes-wrong-link-on-protected-paths-admin-ui.yml5
-rw-r--r--doc/api/epics.md7
-rw-r--r--doc/development/fe_guide/development_process.md2
-rw-r--r--doc/development/fe_guide/graphql.md13
-rw-r--r--doc/development/fe_guide/img/graphiql_explorer_v12_4.pngbin0 -> 353541 bytes
-rw-r--r--lib/gitlab/experimentation.rb89
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/controllers/projects/discussions_controller_spec.rb2
-rw-r--r--spec/frontend/ide/components/branches/search_list_spec.js81
-rw-r--r--spec/frontend/ide/mock_data.js228
-rw-r--r--spec/javascripts/ide/components/branches/search_list_spec.js80
-rw-r--r--spec/javascripts/ide/mock_data.js229
-rw-r--r--spec/lib/gitlab/experimentation_spec.rb156
-rw-r--r--spec/services/projects/after_import_service_spec.rb2
19 files changed, 591 insertions, 330 deletions
diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue
index 36701a95673..b4b124d5db1 100644
--- a/app/assets/javascripts/jobs/components/job_app.vue
+++ b/app/assets/javascripts/jobs/components/job_app.vue
@@ -30,7 +30,7 @@ export default {
EnvironmentsBlock,
ErasedBlock,
Icon,
- Log: () => (isNewJobLogActive() ? import('./job_log_json.vue') : import('./job_log.vue')),
+ Log: () => (isNewJobLogActive() ? import('./log/log.vue') : import('./job_log.vue')),
LogTopBar,
StuckBlock,
UnmetPrerequisitesBlock,
diff --git a/app/assets/javascripts/jobs/components/job_log_json.vue b/app/assets/javascripts/jobs/components/job_log_json.vue
deleted file mode 100644
index 2198b20eb8f..00000000000
--- a/app/assets/javascripts/jobs/components/job_log_json.vue
+++ /dev/null
@@ -1,10 +0,0 @@
-<script>
-export default {
- name: 'JobLogJSON',
-};
-</script>
-<template>
- <pre>
- {{ __('This feature is in development. Please disable the `job_log_json` feature flag') }}
- </pre>
-</template>
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index ad242a078ad..01d80d77080 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -14,6 +14,7 @@ class ApplicationController < ActionController::Base
include SessionlessAuthentication
include ConfirmEmailWarning
include Gitlab::Tracking::ControllerConcern
+ include Gitlab::Experimentation::ControllerConcern
before_action :authenticate_user!
before_action :enforce_terms!, if: :should_enforce_terms?
diff --git a/app/services/projects/after_import_service.rb b/app/services/projects/after_import_service.rb
index e30da0f26df..6fc15db9b4c 100644
--- a/app/services/projects/after_import_service.rb
+++ b/app/services/projects/after_import_service.rb
@@ -9,9 +9,16 @@ module Projects
end
def execute
- Projects::HousekeepingService.new(@project).execute do
+ service = Projects::HousekeepingService.new(@project)
+
+ service.execute do
repository.delete_all_refs_except(RESERVED_REF_PREFIXES)
end
+
+ # Right now we don't actually have a way to know if a project
+ # import actually changed, so we increment the counter to avoid
+ # causing GC to run every time.
+ service.increment!
rescue Projects::HousekeepingService::LeaseTaken => e
Rails.logger.info( # rubocop:disable Gitlab/RailsLogger
"Could not perform housekeeping for project #{@project.full_path} (#{@project.id}): #{e}")
diff --git a/app/views/admin/application_settings/_protected_paths.html.haml b/app/views/admin/application_settings/_protected_paths.html.haml
index cfb04562b59..f4d40e10f36 100644
--- a/app/views/admin/application_settings/_protected_paths.html.haml
+++ b/app/views/admin/application_settings/_protected_paths.html.haml
@@ -4,7 +4,7 @@
%fieldset
- if omnibus_protected_paths_throttle?
.bs-callout.bs-callout-danger
- - relative_url_link = 'https://docs.gitlab.com/ee/user/admin_area/settings/protected_paths.html#migrating-from-omnibus'
+ - relative_url_link = 'https://docs.gitlab.com/ee/user/admin_area/settings/protected_paths.html#migrate-settings-from-gitlab-123-and-earlier'
- relative_url_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: relative_url_link }
= _("Omnibus Protected Paths throttle is active. From 12.4, Omnibus throttle is deprecated and will be removed in a future release. Please read the %{relative_url_link_start}Migrating Protected Paths documentation%{relative_url_link_end}.").html_safe % { relative_url_link_start: relative_url_link_start, relative_url_link_end: '</a>'.html_safe }
diff --git a/changelogs/unreleased/mc-fixes-wrong-link-on-protected-paths-admin-ui.yml b/changelogs/unreleased/mc-fixes-wrong-link-on-protected-paths-admin-ui.yml
new file mode 100644
index 00000000000..61e54f2f5e0
--- /dev/null
+++ b/changelogs/unreleased/mc-fixes-wrong-link-on-protected-paths-admin-ui.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes wrong link on Protected paths admin settings
+merge_request: 17945
+author:
+type: other
diff --git a/doc/api/epics.md b/doc/api/epics.md
index 7ef12766f78..c24df6a236f 100644
--- a/doc/api/epics.md
+++ b/doc/api/epics.md
@@ -88,6 +88,7 @@ Example response:
"due_date_from_milestones": "2018-07-31",
"created_at": "2018-07-17T13:36:22.770Z",
"updated_at": "2018-07-18T12:22:05.239Z",
+ "closed_at": "2018-08-18T12:22:05.239Z",
"labels": [],
"upvotes": 4,
"downvotes": 0
@@ -143,6 +144,7 @@ Example response:
"due_date_from_milestones": "2018-07-31",
"created_at": "2018-07-17T13:36:22.770Z",
"updated_at": "2018-07-18T12:22:05.239Z",
+ "closed_at": "2018-08-18T12:22:05.239Z",
"labels": [],
"upvotes": 4,
"downvotes": 0
@@ -209,6 +211,7 @@ Example response:
"due_date_from_milestones": "2018-07-31",
"created_at": "2018-07-17T13:36:22.770Z",
"updated_at": "2018-07-18T12:22:05.239Z",
+ "closed_at": "2018-08-18T12:22:05.239Z",
"labels": [],
"upvotes": 4,
"downvotes": 0
@@ -276,6 +279,7 @@ Example response:
"due_date_from_milestones": "2018-07-31",
"created_at": "2018-07-17T13:36:22.770Z",
"updated_at": "2018-07-18T12:22:05.239Z",
+ "closed_at": "2018-08-18T12:22:05.239Z",
"labels": [],
"upvotes": 4,
"downvotes": 0
@@ -358,7 +362,8 @@ Example response:
"start_date": null,
"end_date": null,
"created_at": "2018-01-21T06:21:13.165Z",
- "updated_at": "2018-01-22T12:41:41.166Z"
+ "updated_at": "2018-01-22T12:41:41.166Z",
+ "closed_at": "2018-08-18T12:22:05.239Z"
},
"target_url": "https://gitlab.example.com/groups/epics/5",
"body": "Vel voluptas atque dicta mollitia adipisci qui at.",
diff --git a/doc/development/fe_guide/development_process.md b/doc/development/fe_guide/development_process.md
index 41513e6d57e..21e0c869f13 100644
--- a/doc/development/fe_guide/development_process.md
+++ b/doc/development/fe_guide/development_process.md
@@ -80,7 +80,7 @@ With the purpose of being [respectful of others' time](https://about.gitlab.com/
1. Before writing code, ensure your vision of the architecture is aligned with
GitLab's architecture.
-1. Add a diagram to the issue and ask a frontend architect in the slack channel `#fe_architectural` about it.
+1. Add a diagram to the issue and ask a frontend maintainer in the slack channel `#frontend_maintainers` about it.
![Diagram of Issue Boards Architecture](img/boards_diagram.png)
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index 22e773e8ea3..fe4f6d7bec8 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -1,11 +1,18 @@
# GraphQL
+Our GraphQL API can be explored via GraphiQL at your instance's
+`/-/graphql-explorer` or at [GitLab.com](https://gitlab.com/-/graphql-explorer).
+
+You can check all existing queries and mutations on the right side
+of GraphiQL in its **Documentation explorer**. It's also possible to
+write queries and mutations directly on the left tab and check
+their execution by clicking **Execute query** button on the top left:
+
+![GraphiQL interface](img/graphiql_explorer_v12_4.png)
+
We use [Apollo] and [Vue Apollo][vue-apollo] for working with GraphQL
on the frontend.
-In order to use GraphQL, you need to enable the `graphql` feature flag,
-read more about [Feature Flags][feature-flags].
-
## Apollo Client
To save duplicated clients getting created in different apps, we have a
diff --git a/doc/development/fe_guide/img/graphiql_explorer_v12_4.png b/doc/development/fe_guide/img/graphiql_explorer_v12_4.png
new file mode 100644
index 00000000000..8981b37ba23
--- /dev/null
+++ b/doc/development/fe_guide/img/graphiql_explorer_v12_4.png
Binary files differ
diff --git a/lib/gitlab/experimentation.rb b/lib/gitlab/experimentation.rb
new file mode 100644
index 00000000000..678d47150e8
--- /dev/null
+++ b/lib/gitlab/experimentation.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+# == Experimentation
+#
+# Utility module used for A/B testing experimental features. Define your experiments in the `EXPERIMENTS` constant.
+# The feature_toggle and environment keys are optional. If the feature_toggle is not set, a feature with the name of
+# the experiment will be checked, with a default value of true. The enabled_ratio is required and should be
+# the ratio for the number of users for which this experiment is enabled. For example: a ratio of 0.1 will
+# enable the experiment for 10% of the users (determined by the `experimentation_subject_index`).
+#
+module Gitlab
+ module Experimentation
+ EXPERIMENTS = {
+ signup_flow: {
+ feature_toggle: :experimental_separate_sign_up_flow,
+ environment: ::Gitlab.dev_env_or_com?,
+ enabled_ratio: 0.1
+ }
+ }.freeze
+
+ # Controller concern that checks if an experimentation_subject_id cookie is present and sets it if absent.
+ # Used for A/B testing of experimental features. Exposes the `experiment_enabled?(experiment_name)` method
+ # to controllers and views.
+ #
+ module ControllerConcern
+ extend ActiveSupport::Concern
+
+ included do
+ before_action :set_experimentation_subject_id_cookie
+ helper_method :experiment_enabled?
+ end
+
+ def set_experimentation_subject_id_cookie
+ return if cookies[:experimentation_subject_id].present?
+
+ cookies.permanent.signed[:experimentation_subject_id] = {
+ value: SecureRandom.uuid,
+ domain: :all,
+ secure: ::Gitlab.config.gitlab.https
+ }
+ end
+
+ def experiment_enabled?(experiment)
+ Experimentation.enabled?(experiment, experimentation_subject_index)
+ end
+
+ private
+
+ def experimentation_subject_index
+ experimentation_subject_id = cookies.signed[:experimentation_subject_id]
+ return if experimentation_subject_id.blank?
+
+ experimentation_subject_id.delete('-').hex % 100
+ end
+ end
+
+ class << self
+ def enabled?(experiment_key, experimentation_subject_index)
+ return false unless EXPERIMENTS.key?(experiment_key)
+
+ experiment = Experiment.new(EXPERIMENTS[experiment_key].merge(key: experiment_key))
+
+ experiment.feature_toggle_enabled? &&
+ experiment.enabled_for_environment? &&
+ experiment.enabled_for_experimentation_subject?(experimentation_subject_index)
+ end
+ end
+
+ Experiment = Struct.new(:key, :feature_toggle, :environment, :enabled_ratio, keyword_init: true) do
+ def feature_toggle_enabled?
+ return Feature.enabled?(key, default_enabled: true) if feature_toggle.nil?
+
+ Feature.enabled?(feature_toggle)
+ end
+
+ def enabled_for_environment?
+ return true if environment.nil?
+
+ environment
+ end
+
+ def enabled_for_experimentation_subject?(experimentation_subject_index)
+ return false if enabled_ratio.nil? || experimentation_subject_index.blank?
+
+ experimentation_subject_index <= enabled_ratio * 100
+ end
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 8eb7d4d5a8d..8a00584894b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -16074,9 +16074,6 @@ msgstr ""
msgid "This environment has no deployments yet."
msgstr ""
-msgid "This feature is in development. Please disable the `job_log_json` feature flag"
-msgstr ""
-
msgid "This feature requires local storage to be enabled"
msgstr ""
diff --git a/spec/controllers/projects/discussions_controller_spec.rb b/spec/controllers/projects/discussions_controller_spec.rb
index e30b28a4bd5..6ed822bbb10 100644
--- a/spec/controllers/projects/discussions_controller_spec.rb
+++ b/spec/controllers/projects/discussions_controller_spec.rb
@@ -189,7 +189,7 @@ describe Projects::DiscussionsController do
context "when vue_mr_discussions cookie is present" do
before do
- allow(controller).to receive(:cookies).and_return({ vue_mr_discussions: 'true' })
+ cookies[:vue_mr_discussions] = 'true'
end
it "renders discussion with serializer" do
diff --git a/spec/frontend/ide/components/branches/search_list_spec.js b/spec/frontend/ide/components/branches/search_list_spec.js
new file mode 100644
index 00000000000..5cfe1c25c6b
--- /dev/null
+++ b/spec/frontend/ide/components/branches/search_list_spec.js
@@ -0,0 +1,81 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { __ } from '~/locale';
+import List from '~/ide/components/branches/search_list.vue';
+import Item from '~/ide/components/branches/item.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
+import { branches } from '../../mock_data';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('IDE branches search list', () => {
+ let wrapper;
+ const fetchBranchesMock = jest.fn();
+
+ const createComponent = (state, currentBranchId = 'branch') => {
+ const fakeStore = new Vuex.Store({
+ state: {
+ currentBranchId,
+ currentProjectId: 'project',
+ },
+ modules: {
+ branches: {
+ namespaced: true,
+ state: { isLoading: false, branches: [], ...state },
+ actions: {
+ fetchBranches: fetchBranchesMock,
+ },
+ },
+ },
+ });
+
+ wrapper = shallowMount(List, {
+ localVue,
+ store: fakeStore,
+ sync: false,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('calls fetch on mounted', () => {
+ createComponent();
+ expect(fetchBranchesMock).toHaveBeenCalled();
+ });
+
+ it('renders loading icon when `isLoading` is true', () => {
+ createComponent({ isLoading: true });
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ });
+
+ it('renders branches not found when search is not empty and branches list is empty', () => {
+ createComponent({ branches: [] });
+ wrapper.find('input[type="search"]').setValue('something');
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.text()).toContain(__('No branches found'));
+ });
+ });
+
+ describe('with branches', () => {
+ it('renders list', () => {
+ createComponent({ branches });
+ const items = wrapper.findAll(Item);
+
+ expect(items.length).toBe(branches.length);
+ });
+
+ it('renders check next to active branch', () => {
+ const activeBranch = 'regular';
+ createComponent({ branches }, activeBranch);
+ const items = wrapper.findAll(Item).filter(w => w.props('isActive'));
+
+ expect(items.length).toBe(1);
+ expect(items.at(0).props('item').name).toBe(activeBranch);
+ });
+ });
+});
diff --git a/spec/frontend/ide/mock_data.js b/spec/frontend/ide/mock_data.js
new file mode 100644
index 00000000000..80eb15fe5a6
--- /dev/null
+++ b/spec/frontend/ide/mock_data.js
@@ -0,0 +1,228 @@
+import { TEST_HOST } from 'spec/test_constants';
+
+export const projectData = {
+ id: 1,
+ name: 'abcproject',
+ web_url: '',
+ avatar_url: '',
+ path: '',
+ name_with_namespace: 'namespace/abcproject',
+ branches: {
+ master: {
+ treeId: 'abcproject/master',
+ can_push: true,
+ commit: {
+ id: '123',
+ },
+ },
+ },
+ mergeRequests: {},
+ merge_requests_enabled: true,
+ default_branch: 'master',
+};
+
+export const pipelines = [
+ {
+ id: 1,
+ ref: 'master',
+ sha: '123',
+ details: {
+ status: {
+ icon: 'status_failed',
+ group: 'failed',
+ text: 'Failed',
+ },
+ },
+ commit: { id: '123' },
+ },
+ {
+ id: 2,
+ ref: 'master',
+ sha: '213',
+ details: {
+ status: {
+ icon: 'status_failed',
+ group: 'failed',
+ text: 'Failed',
+ },
+ },
+ commit: { id: '213' },
+ },
+];
+
+export const stages = [
+ {
+ dropdown_path: `${TEST_HOST}/testing`,
+ name: 'build',
+ status: {
+ icon: 'status_failed',
+ group: 'failed',
+ text: 'failed',
+ },
+ },
+ {
+ dropdown_path: 'testing',
+ name: 'test',
+ status: {
+ icon: 'status_failed',
+ group: 'failed',
+ text: 'failed',
+ },
+ },
+];
+
+export const jobs = [
+ {
+ id: 1,
+ name: 'test',
+ path: 'testing',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ },
+ stage: 'test',
+ duration: 1,
+ started: new Date(),
+ },
+ {
+ id: 2,
+ name: 'test 2',
+ path: 'testing2',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ },
+ stage: 'test',
+ duration: 1,
+ started: new Date(),
+ },
+ {
+ id: 3,
+ name: 'test 3',
+ path: 'testing3',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ },
+ stage: 'test',
+ duration: 1,
+ started: new Date(),
+ },
+ {
+ id: 4,
+ name: 'test 4',
+ path: 'testing4',
+ status: {
+ icon: 'status_failed',
+ text: 'failed',
+ },
+ stage: 'build',
+ duration: 1,
+ started: new Date(),
+ },
+];
+
+export const fullPipelinesResponse = {
+ data: {
+ count: {
+ all: 2,
+ },
+ pipelines: [
+ {
+ id: '51',
+ path: 'test',
+ commit: {
+ id: '123',
+ },
+ details: {
+ status: {
+ icon: 'status_failed',
+ text: 'failed',
+ },
+ stages: [...stages],
+ },
+ },
+ {
+ id: '50',
+ commit: {
+ id: 'abc123def456ghi789jkl',
+ },
+ details: {
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ },
+ stages: [...stages],
+ },
+ },
+ ],
+ },
+};
+
+export const mergeRequests = [
+ {
+ id: 1,
+ iid: 1,
+ title: 'Test merge request',
+ project_id: 1,
+ web_url: `${TEST_HOST}/namespace/project-path/merge_requests/1`,
+ },
+];
+
+export const branches = [
+ {
+ id: 1,
+ name: 'master',
+ commit: {
+ message: 'Update master branch',
+ committed_date: '2018-08-01T00:20:05Z',
+ },
+ can_push: true,
+ protected: true,
+ default: true,
+ },
+ {
+ id: 2,
+ name: 'protected/no-access',
+ commit: {
+ message: 'Update some stuff',
+ committed_date: '2018-08-02T00:00:05Z',
+ },
+ can_push: false,
+ protected: true,
+ default: false,
+ },
+ {
+ id: 3,
+ name: 'protected/access',
+ commit: {
+ message: 'Update some stuff',
+ committed_date: '2018-08-02T00:00:05Z',
+ },
+ can_push: true,
+ protected: true,
+ default: false,
+ },
+ {
+ id: 4,
+ name: 'regular',
+ commit: {
+ message: 'Update some more stuff',
+ committed_date: '2018-06-30T00:20:05Z',
+ },
+ can_push: true,
+ protected: false,
+ default: false,
+ },
+ {
+ id: 5,
+ name: 'regular/no-access',
+ commit: {
+ message: 'Update some more stuff',
+ committed_date: '2018-06-30T00:20:05Z',
+ },
+ can_push: false,
+ protected: false,
+ default: false,
+ },
+];
diff --git a/spec/javascripts/ide/components/branches/search_list_spec.js b/spec/javascripts/ide/components/branches/search_list_spec.js
deleted file mode 100644
index 72a3c2d5dcd..00000000000
--- a/spec/javascripts/ide/components/branches/search_list_spec.js
+++ /dev/null
@@ -1,80 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import * as types from '~/ide/stores/modules/branches/mutation_types';
-import List from '~/ide/components/branches/search_list.vue';
-import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
-import { branches as testBranches } from '../../mock_data';
-import { resetStore } from '../../helpers';
-
-describe('IDE branches search list', () => {
- const Component = Vue.extend(List);
- let vm;
-
- beforeEach(() => {
- vm = createComponentWithStore(Component, store, {});
-
- spyOn(vm, 'fetchBranches');
-
- vm.$mount();
- });
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(store);
- });
-
- it('calls fetch on mounted', () => {
- expect(vm.fetchBranches).toHaveBeenCalledWith({
- search: '',
- });
- });
-
- it('renders loading icon', done => {
- vm.$store.state.branches.isLoading = true;
-
- vm.$nextTick()
- .then(() => {
- expect(vm.$el).toContainElement('.loading-container');
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('renders branches not found when search is not empty', done => {
- vm.search = 'testing';
-
- vm.$nextTick(() => {
- expect(vm.$el).toContainText('No branches found');
-
- done();
- });
- });
-
- describe('with branches', () => {
- const currentBranch = testBranches[1];
-
- beforeEach(done => {
- vm.$store.state.currentBranchId = currentBranch.name;
- vm.$store.commit(`branches/${types.RECEIVE_BRANCHES_SUCCESS}`, testBranches);
-
- vm.$nextTick(done);
- });
-
- it('renders list', () => {
- const elementText = Array.from(vm.$el.querySelectorAll('li strong')).map(x =>
- x.textContent.trim(),
- );
-
- expect(elementText).toEqual(testBranches.map(x => x.name));
- });
-
- it('renders check next to active branch', () => {
- const checkedText = Array.from(vm.$el.querySelectorAll('li'))
- .filter(x => x.querySelector('.ide-search-list-current-icon svg'))
- .map(x => x.querySelector('strong').textContent.trim());
-
- expect(checkedText).toEqual([currentBranch.name]);
- });
- });
-});
diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js
index 1c2e082489e..27f0ad01f54 100644
--- a/spec/javascripts/ide/mock_data.js
+++ b/spec/javascripts/ide/mock_data.js
@@ -1,228 +1 @@
-import { TEST_HOST } from '../test_constants';
-
-export const projectData = {
- id: 1,
- name: 'abcproject',
- web_url: '',
- avatar_url: '',
- path: '',
- name_with_namespace: 'namespace/abcproject',
- branches: {
- master: {
- treeId: 'abcproject/master',
- can_push: true,
- commit: {
- id: '123',
- },
- },
- },
- mergeRequests: {},
- merge_requests_enabled: true,
- default_branch: 'master',
-};
-
-export const pipelines = [
- {
- id: 1,
- ref: 'master',
- sha: '123',
- details: {
- status: {
- icon: 'status_failed',
- group: 'failed',
- text: 'Failed',
- },
- },
- commit: { id: '123' },
- },
- {
- id: 2,
- ref: 'master',
- sha: '213',
- details: {
- status: {
- icon: 'status_failed',
- group: 'failed',
- text: 'Failed',
- },
- },
- commit: { id: '213' },
- },
-];
-
-export const stages = [
- {
- dropdown_path: `${TEST_HOST}/testing`,
- name: 'build',
- status: {
- icon: 'status_failed',
- group: 'failed',
- text: 'failed',
- },
- },
- {
- dropdown_path: 'testing',
- name: 'test',
- status: {
- icon: 'status_failed',
- group: 'failed',
- text: 'failed',
- },
- },
-];
-
-export const jobs = [
- {
- id: 1,
- name: 'test',
- path: 'testing',
- status: {
- icon: 'status_success',
- text: 'passed',
- },
- stage: 'test',
- duration: 1,
- started: new Date(),
- },
- {
- id: 2,
- name: 'test 2',
- path: 'testing2',
- status: {
- icon: 'status_success',
- text: 'passed',
- },
- stage: 'test',
- duration: 1,
- started: new Date(),
- },
- {
- id: 3,
- name: 'test 3',
- path: 'testing3',
- status: {
- icon: 'status_success',
- text: 'passed',
- },
- stage: 'test',
- duration: 1,
- started: new Date(),
- },
- {
- id: 4,
- name: 'test 4',
- path: 'testing4',
- status: {
- icon: 'status_failed',
- text: 'failed',
- },
- stage: 'build',
- duration: 1,
- started: new Date(),
- },
-];
-
-export const fullPipelinesResponse = {
- data: {
- count: {
- all: 2,
- },
- pipelines: [
- {
- id: '51',
- path: 'test',
- commit: {
- id: '123',
- },
- details: {
- status: {
- icon: 'status_failed',
- text: 'failed',
- },
- stages: [...stages],
- },
- },
- {
- id: '50',
- commit: {
- id: 'abc123def456ghi789jkl',
- },
- details: {
- status: {
- icon: 'status_success',
- text: 'passed',
- },
- stages: [...stages],
- },
- },
- ],
- },
-};
-
-export const mergeRequests = [
- {
- id: 1,
- iid: 1,
- title: 'Test merge request',
- project_id: 1,
- web_url: `${TEST_HOST}/namespace/project-path/merge_requests/1`,
- },
-];
-
-export const branches = [
- {
- id: 1,
- name: 'master',
- commit: {
- message: 'Update master branch',
- committed_date: '2018-08-01T00:20:05Z',
- },
- can_push: true,
- protected: true,
- default: true,
- },
- {
- id: 2,
- name: 'protected/no-access',
- commit: {
- message: 'Update some stuff',
- committed_date: '2018-08-02T00:00:05Z',
- },
- can_push: false,
- protected: true,
- default: false,
- },
- {
- id: 3,
- name: 'protected/access',
- commit: {
- message: 'Update some stuff',
- committed_date: '2018-08-02T00:00:05Z',
- },
- can_push: true,
- protected: true,
- default: false,
- },
- {
- id: 4,
- name: 'regular',
- commit: {
- message: 'Update some more stuff',
- committed_date: '2018-06-30T00:20:05Z',
- },
- can_push: true,
- protected: false,
- default: false,
- },
- {
- id: 5,
- name: 'regular/no-access',
- commit: {
- message: 'Update some more stuff',
- committed_date: '2018-06-30T00:20:05Z',
- },
- can_push: false,
- protected: false,
- default: false,
- },
-];
+export * from '../../frontend/ide/mock_data';
diff --git a/spec/lib/gitlab/experimentation_spec.rb b/spec/lib/gitlab/experimentation_spec.rb
new file mode 100644
index 00000000000..4d473731f39
--- /dev/null
+++ b/spec/lib/gitlab/experimentation_spec.rb
@@ -0,0 +1,156 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Experimentation::ControllerConcern, type: :controller do
+ controller(ApplicationController) do
+ include Gitlab::Experimentation::ControllerConcern
+
+ def index
+ head :ok
+ end
+ end
+
+ describe '#set_experimentation_subject_id_cookie' do
+ before do
+ get :index
+ end
+
+ context 'cookie is present' do
+ before do
+ cookies[:experimentation_subject_id] = 'test'
+ end
+
+ it 'does not change the cookie' do
+ expect(cookies[:experimentation_subject_id]).to eq 'test'
+ end
+ end
+
+ context 'cookie is not present' do
+ it 'sets a permanent signed cookie' do
+ expect(cookies.permanent.signed[:experimentation_subject_id]).to be_present
+ end
+ end
+ end
+
+ describe '#experiment_enabled?' do
+ context 'cookie is not present' do
+ it 'calls Gitlab::Experimentation.enabled? with the name of the experiment and an experimentation_subject_index of nil' do
+ expect(Gitlab::Experimentation).to receive(:enabled?).with(:test_experiment, nil)
+ controller.experiment_enabled?(:test_experiment)
+ end
+ end
+
+ context 'cookie is present' do
+ before do
+ cookies.permanent.signed[:experimentation_subject_id] = 'abcd-1234'
+ get :index
+ end
+
+ it 'calls Gitlab::Experimentation.enabled? with the name of the experiment and an experimentation_subject_index of the modulo 100 of the hex value of the uuid' do
+ # 'abcd1234'.hex % 100 = 76
+ expect(Gitlab::Experimentation).to receive(:enabled?).with(:test_experiment, 76)
+ controller.experiment_enabled?(:test_experiment)
+ end
+ end
+ end
+end
+
+describe Gitlab::Experimentation do
+ before do
+ stub_const('Gitlab::Experimentation::EXPERIMENTS', {
+ test_experiment: {
+ feature_toggle: feature_toggle,
+ environment: environment,
+ enabled_ratio: enabled_ratio
+ }
+ })
+
+ stub_feature_flags(feature_toggle => true)
+ end
+
+ let(:feature_toggle) { :test_experiment_toggle }
+ let(:environment) { Rails.env.test? }
+ let(:enabled_ratio) { 0.1 }
+
+ describe '.enabled?' do
+ subject { described_class.enabled?(:test_experiment, experimentation_subject_index) }
+ let(:experimentation_subject_index) { 9 }
+
+ context 'feature toggle is enabled, we are on the right environment and we are selected' do
+ it { is_expected.to be_truthy }
+ end
+
+ describe 'experiment is not defined' do
+ it 'returns false' do
+ expect(described_class.enabled?(:missing_experiment, experimentation_subject_index)).to be_falsey
+ end
+ end
+
+ describe 'feature toggle' do
+ context 'feature toggle is not set' do
+ let(:feature_toggle) { nil }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'feature toggle is not set, but a feature with the experiment key as name does exist' do
+ before do
+ stub_feature_flags(test_experiment: false)
+ end
+
+ let(:feature_toggle) { nil }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'feature toggle is disabled' do
+ before do
+ stub_feature_flags(feature_toggle => false)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe 'environment' do
+ context 'environment is not set' do
+ let(:environment) { nil }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'we are on the wrong environment' do
+ let(:environment) { ::Gitlab.com? }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe 'enabled ratio' do
+ context 'enabled ratio is not set' do
+ let(:enabled_ratio) { nil }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'experimentation_subject_index is not set' do
+ let(:experimentation_subject_index) { nil }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'experimentation_subject_index is an empty string' do
+ let(:experimentation_subject_index) { '' }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'experimentation_subject_index outside enabled ratio' do
+ let(:experimentation_subject_index) { 11 }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/after_import_service_spec.rb b/spec/services/projects/after_import_service_spec.rb
index 51d3fd18881..27e8f3c45ba 100644
--- a/spec/services/projects/after_import_service_spec.rb
+++ b/spec/services/projects/after_import_service_spec.rb
@@ -19,6 +19,8 @@ describe Projects::AfterImportService do
allow(housekeeping_service)
.to receive(:execute).and_yield
+
+ expect(housekeeping_service).to receive(:increment!)
end
it 'performs housekeeping' do