summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/javascripts/commons/gitlab_ui.js2
-rw-r--r--app/assets/javascripts/groups/components/groups.vue27
-rw-r--r--app/assets/javascripts/vue_shared/components/pagination_links.vue34
-rw-r--r--app/controllers/projects/merge_requests_controller.rb1
-rw-r--r--app/models/ci/build.rb1
-rw-r--r--app/models/ci/trigger_request.rb2
-rw-r--r--app/models/clusters/applications/jupyter.rb9
-rw-r--r--app/serializers/build_details_entity.rb6
-rw-r--r--app/serializers/trigger_variable_entity.rb7
-rw-r--r--app/workers/project_service_worker.rb6
-rw-r--r--changelogs/unreleased/21617-initialize-projects-with-readme.yml5
-rw-r--r--changelogs/unreleased/50989-add-trigger-information-to-job-api.yml5
-rw-r--r--changelogs/unreleased/fix-mention-in-edit-mr.yml5
-rw-r--r--changelogs/unreleased/issue_50528.yml5
-rw-r--r--changelogs/unreleased/sh-allow-key-id-in-params.yml5
-rw-r--r--changelogs/unreleased/update-gitlab-shell.yml5
-rw-r--r--config/application.rb5
-rw-r--r--doc/administration/monitoring/prometheus/index.md8
-rw-r--r--doc/api/projects.md1
-rw-r--r--doc/development/code_review.md5
-rw-r--r--lib/api/helpers/projects_helpers.rb1
-rw-r--r--lib/banzai/renderer/common_mark/html.rb14
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb45
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb14
-rw-r--r--spec/features/dashboard/groups_list_spec.rb4
-rw-r--r--spec/fixtures/api/schemas/job/job_details.json7
-rw-r--r--spec/fixtures/api/schemas/job/trigger.json28
-rw-r--r--spec/javascripts/groups/components/app_spec.js3
-rw-r--r--spec/javascripts/vue_shared/components/pagination_links_spec.js72
-rw-r--r--spec/lib/banzai/filter/markdown_filter_spec.rb23
-rw-r--r--spec/models/clusters/applications/jupyter_spec.rb13
-rw-r--r--spec/requests/api/projects_spec.rb8
-rw-r--r--spec/support/shared_examples/features/editable_merge_request_shared_examples.rb8
-rw-r--r--spec/workers/project_service_worker_spec.rb25
36 files changed, 373 insertions, 40 deletions
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 56b6be4ebb2..9c78b761ea1 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-8.3.1
+8.3.2
diff --git a/Gemfile.lock b/Gemfile.lock
index 8c545b7257c..0832fe25711 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -125,7 +125,7 @@ GEM
coderay (1.1.2)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
- commonmarker (0.17.8)
+ commonmarker (0.17.13)
ruby-enum (~> 0.5)
concord (0.1.5)
adamantium (~> 0.2.0)
diff --git a/app/assets/javascripts/commons/gitlab_ui.js b/app/assets/javascripts/commons/gitlab_ui.js
index c2dc0539398..aed26adfa5c 100644
--- a/app/assets/javascripts/commons/gitlab_ui.js
+++ b/app/assets/javascripts/commons/gitlab_ui.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import Pagination from '@gitlab-org/gitlab-ui/dist/components/base/pagination';
import progressBar from '@gitlab-org/gitlab-ui/dist/components/base/progress_bar';
import modal from '@gitlab-org/gitlab-ui/dist/components/base/modal';
import loadingIcon from '@gitlab-org/gitlab-ui/dist/components/base/loading_icon';
@@ -6,6 +7,7 @@ import loadingIcon from '@gitlab-org/gitlab-ui/dist/components/base/loading_icon
import dModal from '@gitlab-org/gitlab-ui/dist/directives/modal';
import dTooltip from '@gitlab-org/gitlab-ui/dist/directives/tooltip';
+Vue.component('gl-pagination', Pagination);
Vue.component('gl-progress-bar', progressBar);
Vue.component('gl-ui-modal', modal);
Vue.component('gl-loading-icon', loadingIcon);
diff --git a/app/assets/javascripts/groups/components/groups.vue b/app/assets/javascripts/groups/components/groups.vue
index a1beb222950..81b2e5ea37b 100644
--- a/app/assets/javascripts/groups/components/groups.vue
+++ b/app/assets/javascripts/groups/components/groups.vue
@@ -1,11 +1,11 @@
<script>
-import tablePagination from '~/vue_shared/components/table_pagination.vue';
+import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
import eventHub from '../event_hub';
import { getParameterByName } from '../../lib/utils/common_utils';
export default {
components: {
- tablePagination,
+ PaginationLinks,
},
props: {
groups: {
@@ -49,15 +49,18 @@ export default {
>
{{ searchEmptyMessage }}
</div>
- <group-folder
- v-if="!searchEmpty"
- :groups="groups"
- :action="action"
- />
- <table-pagination
- v-if="!searchEmpty"
- :change="change"
- :page-info="pageInfo"
- />
+ <template
+ v-else
+ >
+ <group-folder
+ :groups="groups"
+ :action="action"
+ />
+ <pagination-links
+ :change="change"
+ :page-info="pageInfo"
+ class="d-flex justify-content-center prepend-top-default"
+ />
+ </template>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/pagination_links.vue b/app/assets/javascripts/vue_shared/components/pagination_links.vue
new file mode 100644
index 00000000000..1f2a679c145
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/pagination_links.vue
@@ -0,0 +1,34 @@
+<script>
+import { s__ } from '../../locale';
+
+export default {
+ props: {
+ change: {
+ type: Function,
+ required: true,
+ },
+ pageInfo: {
+ type: Object,
+ required: true,
+ },
+ },
+ firstText: s__('Pagination|« First'),
+ prevText: s__('Pagination|Prev'),
+ nextText: s__('Pagination|Next'),
+ lastText: s__('Pagination|Last »'),
+};
+</script>
+
+<template>
+ <gl-pagination
+ v-bind="$attrs"
+ :change="change"
+ :page="pageInfo.page"
+ :per-page="pageInfo.perPage"
+ :total-items="pageInfo.total"
+ :first-text="$options.firstText"
+ :prev-text="$options.prevText"
+ :next-text="$options.nextText"
+ :last-text="$options.lastText"
+ />
+</template>
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index d31b58972ca..75a85fafa3f 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -330,6 +330,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
@source_project = @merge_request.source_project
@target_project = @merge_request.target_project
@target_branches = @merge_request.target_project.repository.branch_names
+ @noteable = @merge_request
end
def finder_type
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index faa160ad6ba..cc1408cb397 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -40,6 +40,7 @@ module Ci
delegate :url, to: :runner_session, prefix: true, allow_nil: true
delegate :terminal_specification, to: :runner_session, allow_nil: true
delegate :gitlab_deploy_token, to: :project
+ delegate :trigger_short_token, to: :trigger_request, allow_nil: true
##
# The "environment" field for builds is a String, and is the unexpanded name!
diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb
index 913936a0bcb..0b52c690e93 100644
--- a/app/models/ci/trigger_request.rb
+++ b/app/models/ci/trigger_request.rb
@@ -8,6 +8,8 @@ module Ci
belongs_to :pipeline, foreign_key: :commit_id
has_many :builds
+ delegate :short_token, to: :trigger, prefix: true, allow_nil: true
+
# We switched to Ci::PipelineVariable from Ci::TriggerRequest.variables.
# Ci::TriggerRequest doesn't save variables anymore.
validates :variables, absence: true
diff --git a/app/models/clusters/applications/jupyter.rb b/app/models/clusters/applications/jupyter.rb
index 3d84eeed5a8..2371b0237d8 100644
--- a/app/models/clusters/applications/jupyter.rb
+++ b/app/models/clusters/applications/jupyter.rb
@@ -73,10 +73,19 @@ module Clusters
"clientSecret" => oauth_application.secret,
"callbackUrl" => callback_url
}
+ },
+ "singleuser" => {
+ "extraEnv" => {
+ "GITLAB_PROJECT_ID" => project_id
+ }
}
}
end
+ def project_id
+ cluster&.project&.id
+ end
+
def gitlab_url
Gitlab.config.gitlab.url
end
diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb
index b107fc26f18..6f8194d9856 100644
--- a/app/serializers/build_details_entity.rb
+++ b/app/serializers/build_details_entity.rb
@@ -59,6 +59,12 @@ class BuildDetailsEntity < JobEntity
raw_project_job_path(project, build)
end
+ expose :trigger, if: -> (*) { build.trigger_request } do
+ expose :trigger_short_token, as: :short_token
+
+ expose :trigger_variables, as: :variables, using: TriggerVariableEntity
+ end
+
private
def build_failed_issue_options
diff --git a/app/serializers/trigger_variable_entity.rb b/app/serializers/trigger_variable_entity.rb
new file mode 100644
index 00000000000..56203113631
--- /dev/null
+++ b/app/serializers/trigger_variable_entity.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class TriggerVariableEntity < Grape::Entity
+ include RequestAwareEntity
+
+ expose :key, :value, :public
+end
diff --git a/app/workers/project_service_worker.rb b/app/workers/project_service_worker.rb
index a0bc9288cf0..25567cec08b 100644
--- a/app/workers/project_service_worker.rb
+++ b/app/workers/project_service_worker.rb
@@ -7,6 +7,10 @@ class ProjectServiceWorker
def perform(hook_id, data)
data = data.with_indifferent_access
- Service.find(hook_id).execute(data)
+ service = Service.find(hook_id)
+ service.execute(data)
+ rescue => error
+ service_class = service&.class&.name || "Not Found"
+ logger.error class: self.class.name, service_class: service_class, message: error.message
end
end
diff --git a/changelogs/unreleased/21617-initialize-projects-with-readme.yml b/changelogs/unreleased/21617-initialize-projects-with-readme.yml
new file mode 100644
index 00000000000..168f6af60c5
--- /dev/null
+++ b/changelogs/unreleased/21617-initialize-projects-with-readme.yml
@@ -0,0 +1,5 @@
+---
+title: Adds a initialize_with_readme parameter to POST /projects
+merge_request: 21617
+author: Steve
+type: added
diff --git a/changelogs/unreleased/50989-add-trigger-information-to-job-api.yml b/changelogs/unreleased/50989-add-trigger-information-to-job-api.yml
new file mode 100644
index 00000000000..5c8c78b8de8
--- /dev/null
+++ b/changelogs/unreleased/50989-add-trigger-information-to-job-api.yml
@@ -0,0 +1,5 @@
+---
+title: Add trigger information in job API
+merge_request: 21495
+author:
+type: other
diff --git a/changelogs/unreleased/fix-mention-in-edit-mr.yml b/changelogs/unreleased/fix-mention-in-edit-mr.yml
new file mode 100644
index 00000000000..a82b0ba9748
--- /dev/null
+++ b/changelogs/unreleased/fix-mention-in-edit-mr.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed mention autocomplete in edit merge request.
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/issue_50528.yml b/changelogs/unreleased/issue_50528.yml
new file mode 100644
index 00000000000..82d33bfa255
--- /dev/null
+++ b/changelogs/unreleased/issue_50528.yml
@@ -0,0 +1,5 @@
+---
+title: Log project services errors when executing async
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/sh-allow-key-id-in-params.yml b/changelogs/unreleased/sh-allow-key-id-in-params.yml
new file mode 100644
index 00000000000..2be1cfb0ed3
--- /dev/null
+++ b/changelogs/unreleased/sh-allow-key-id-in-params.yml
@@ -0,0 +1,5 @@
+---
+title: Filter any parameters ending with "key" in logs
+merge_request: 21688
+author:
+type: changed
diff --git a/changelogs/unreleased/update-gitlab-shell.yml b/changelogs/unreleased/update-gitlab-shell.yml
new file mode 100644
index 00000000000..f5d0e30a7be
--- /dev/null
+++ b/changelogs/unreleased/update-gitlab-shell.yml
@@ -0,0 +1,5 @@
+---
+title: Update GitLab Shell to v8.3.2
+merge_request: 21701
+author:
+type: fixed
diff --git a/config/application.rb b/config/application.rb
index fae92f6f372..f3c53fa63f3 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -85,6 +85,7 @@ module Gitlab
# - Any parameter ending with `token`
# - Any parameter containing `password`
# - Any parameter containing `secret`
+ # - Any parameter ending with `key`
# - Two-factor tokens (:otp_attempt)
# - Repo/Project Import URLs (:import_url)
# - Build traces (:trace)
@@ -92,15 +93,13 @@ module Gitlab
# - GitLab Pages SSL cert/key info (:certificate, :encrypted_key)
# - Webhook URLs (:hook)
# - Sentry DSN (:sentry_dsn)
- # - Deploy keys (:key)
# - File content from Web Editor (:content)
- config.filter_parameters += [/token$/, /password/, /secret/]
+ config.filter_parameters += [/token$/, /password/, /secret/, /key$/]
config.filter_parameters += %i(
certificate
encrypted_key
hook
import_url
- key
otp_attempt
sentry_dsn
trace
diff --git a/doc/administration/monitoring/prometheus/index.md b/doc/administration/monitoring/prometheus/index.md
index 9d525645952..b1b670c3b42 100644
--- a/doc/administration/monitoring/prometheus/index.md
+++ b/doc/administration/monitoring/prometheus/index.md
@@ -97,10 +97,10 @@ For a more fully featured dashboard, Grafana can be used and has
Sample Prometheus queries:
-- **% Memory used:** `(1 - ((node_memory_MemFree + node_memory_Cached) / node_memory_MemTotal)) * 100`
-- **% CPU load:** `1 - rate(node_cpu{mode="idle"}[5m])`
-- **Data transmitted:** `irate(node_network_transmit_bytes[5m])`
-- **Data received:** `irate(node_network_receive_bytes[5m])`
+- **% Memory available:** `((node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) or ((node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes) / node_memory_MemTotal_bytes)) * 100`
+- **% CPU utilization:** `1 - avg without (mode,cpu) (rate(node_cpu_seconds_total{mode="idle"}[5m]))`
+- **Data transmitted:** `rate(node_network_transmit_bytes_total{device!="lo"}[5m])`
+- **Data received:** `rate(node_network_receive_bytes_total{device!="lo"}[5m])`
## Configuring Prometheus to monitor Kubernetes
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 7e8b7c4b502..947e7db9c52 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -661,6 +661,7 @@ POST /projects
| `avatar` | mixed | no | Image file for avatar of the project |
| `printing_merge_request_link_enabled` | boolean | no | Show link to create/view merge request when pushing from the command line |
| `ci_config_path` | string | no | The path to CI config file |
+| `initialize_with_readme` | boolean | no | `false` by default |
## Create project for user
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index e50e6370c80..edf0b6f46df 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -27,8 +27,9 @@ There are a few rules to get your merge request accepted:
ask or assign any [reviewers][projects] for a first review.
1. If you need some guidance (e.g. it's your first merge request), feel free
to ask one of the [Merge request coaches][team].
- 1. The reviewer will assign the merge request to a maintainer once the
- reviewer is satisfied with the state of the merge request.
+ 1. It is recommended that you assign a maintainer that is from a different team than your own.
+ This ensures that all code across GitLab is consistent and can be easily understood by all contributors.
+
1. Keep in mind that maintainers are also going to perform a final code review.
The ideal scenario is that the reviewer has already addressed any concerns
the maintainer would have found, and the maintainer only has to perform the
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 381d5e8968c..98672f2f765 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -26,6 +26,7 @@ module API
optional :avatar, type: File, desc: 'Avatar image for project'
optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line'
optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests'
+ optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md"
end
params :optional_project_params do
diff --git a/lib/banzai/renderer/common_mark/html.rb b/lib/banzai/renderer/common_mark/html.rb
index 46b609c36b0..0b27316da1b 100644
--- a/lib/banzai/renderer/common_mark/html.rb
+++ b/lib/banzai/renderer/common_mark/html.rb
@@ -4,15 +4,11 @@ module Banzai
class HTML < CommonMarker::HtmlRenderer
def code_block(node)
block do
- code = node.string_content
- lang = node.fence_info
- lang_attr = lang.present? ? %Q{ lang="#{lang}"} : ''
- result =
- "<pre>" \
- "<code#{lang_attr}>#{ERB::Util.html_escape(code)}</code>" \
- "</pre>"
-
- out(result)
+ out("<pre#{sourcepos(node)}><code")
+ out(' lang="', node.fence_info, '"') if node.fence_info.present?
+ out('>')
+ out(escape_html(node.string_content))
+ out('</code></pre>')
end
end
end
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 8b6903011c3..b42f6419922 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -208,6 +208,51 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
end
+ context 'when requesting JSON job is triggered' do
+ let!(:merge_request) { create(:merge_request, source_project: project) }
+ let(:trigger) { create(:ci_trigger, project: project) }
+ let(:trigger_request) { create(:ci_trigger_request, pipeline: pipeline, trigger: trigger) }
+ let(:job) { create(:ci_build, pipeline: pipeline, trigger_request: trigger_request) }
+
+ before do
+ project.add_developer(user)
+ sign_in(user)
+
+ allow_any_instance_of(Ci::Build).to receive(:merge_request).and_return(merge_request)
+ end
+
+ context 'with no variables' do
+ before do
+ get_show(id: job.id, format: :json)
+ end
+
+ it 'exposes trigger information' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['trigger']['short_token']).to eq 'toke'
+ expect(json_response['trigger']['variables'].length).to eq 0
+ end
+ end
+
+ context 'with variables' do
+ before do
+ create(:ci_pipeline_variable, pipeline: pipeline, key: :TRIGGER_KEY_1, value: 'TRIGGER_VALUE_1')
+
+ get_show(id: job.id, format: :json)
+ end
+
+ it 'exposes trigger information and variables' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('job/job_details')
+ expect(json_response['trigger']['short_token']).to eq 'toke'
+ expect(json_response['trigger']['variables'].length).to eq 1
+ expect(json_response['trigger']['variables'].first['key']).to eq "TRIGGER_KEY_1"
+ expect(json_response['trigger']['variables'].first['value']).to eq "TRIGGER_VALUE_1"
+ expect(json_response['trigger']['variables'].first['public']).to eq false
+ end
+ end
+ end
+
def get_show(**extra_params)
params = {
namespace_id: project.namespace.to_param,
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index d9bb3981539..7446e0650f7 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -885,4 +885,18 @@ describe Projects::MergeRequestsController do
end
end
end
+
+ describe 'GET edit' do
+ it 'responds successfully' do
+ get :edit, namespace_id: project.namespace, project_id: project, id: merge_request
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+
+ it 'assigns the noteable to make sure autocompletes work' do
+ get :edit, namespace_id: project.namespace, project_id: project, id: merge_request
+
+ expect(assigns(:noteable)).not_to be_nil
+ end
+ end
end
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index eceb12e91cd..e75c43d5338 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -125,7 +125,7 @@ describe 'Dashboard Groups page', :js do
end
it 'loads results for next page' do
- expect(page).to have_selector('.gl-pagination .page', count: 2)
+ expect(page).to have_selector('.gl-pagination .page-item a[role=menuitemradio]', count: 2)
# Check first page
expect(page).to have_content(group2.full_name)
@@ -134,7 +134,7 @@ describe 'Dashboard Groups page', :js do
expect(page).not_to have_selector("#group-#{group.id}")
# Go to next page
- find(".gl-pagination .page:not(.active) a").click
+ find('.gl-pagination .page-item:not(.active) a[role=menuitemradio]').click
wait_for_requests
diff --git a/spec/fixtures/api/schemas/job/job_details.json b/spec/fixtures/api/schemas/job/job_details.json
index b8c099250be..b82f7413b50 100644
--- a/spec/fixtures/api/schemas/job/job_details.json
+++ b/spec/fixtures/api/schemas/job/job_details.json
@@ -1,8 +1,11 @@
{
- "allOf": [{ "$ref": "job.json" }],
+ "allOf": [
+ { "$ref": "job.json" }
+ ],
"description": "An extension of job.json with more detailed information",
"properties": {
"artifact": { "$ref": "artifact.json" },
- "terminal_path": { "type": "string" }
+ "terminal_path": { "type": "string" },
+ "trigger": { "$ref": "trigger.json" }
}
}
diff --git a/spec/fixtures/api/schemas/job/trigger.json b/spec/fixtures/api/schemas/job/trigger.json
new file mode 100644
index 00000000000..1c7e9cc7693
--- /dev/null
+++ b/spec/fixtures/api/schemas/job/trigger.json
@@ -0,0 +1,28 @@
+{
+ "type": "object",
+ "required": [
+ "short_token",
+ "variables"
+ ],
+ "properties": {
+ "short_token": { "type": "string" },
+ "variables": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "key",
+ "value",
+ "public"
+ ],
+ "properties": {
+ "key": { "type": "string" },
+ "value": { "type": "string" },
+ "public": { "type": "boolean" }
+ },
+ "additionalProperties": false
+ }
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/javascripts/groups/components/app_spec.js b/spec/javascripts/groups/components/app_spec.js
index 76933cf337b..89c07d1f06d 100644
--- a/spec/javascripts/groups/components/app_spec.js
+++ b/spec/javascripts/groups/components/app_spec.js
@@ -24,6 +24,8 @@ const createComponent = (hideProjects = false) => {
const store = new GroupsStore(false);
const service = new GroupsService(mockEndpoint);
+ store.state.pageInfo = mockPageInfo;
+
return new Component({
propsData: {
store,
@@ -484,7 +486,6 @@ describe('AppComponent', () => {
it('should render groups tree', done => {
vm.store.state.groups = [mockParentGroupItem];
vm.isLoading = false;
- vm.store.state.pageInfo = mockPageInfo;
Vue.nextTick(() => {
expect(vm.$el.querySelector('.groups-list-tree-container')).toBeDefined();
done();
diff --git a/spec/javascripts/vue_shared/components/pagination_links_spec.js b/spec/javascripts/vue_shared/components/pagination_links_spec.js
new file mode 100644
index 00000000000..c9d183872b4
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/pagination_links_spec.js
@@ -0,0 +1,72 @@
+import Vue from 'vue';
+import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
+import { s__ } from '~/locale';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Pagination links component', () => {
+ const paginationLinksComponent = Vue.extend(PaginationLinks);
+ const change = page => page;
+ const pageInfo = {
+ page: 3,
+ perPage: 5,
+ total: 30,
+ };
+ const translations = {
+ firstText: s__('Pagination|« First'),
+ prevText: s__('Pagination|Prev'),
+ nextText: s__('Pagination|Next'),
+ lastText: s__('Pagination|Last »'),
+ };
+
+ let paginationLinks;
+ let glPagination;
+ let destinationComponent;
+
+ beforeEach(() => {
+ paginationLinks = mountComponent(
+ paginationLinksComponent,
+ {
+ change,
+ pageInfo,
+ },
+ );
+ [glPagination] = paginationLinks.$children;
+ [destinationComponent] = glPagination.$children;
+ });
+
+ afterEach(() => {
+ paginationLinks.$destroy();
+ });
+
+ it('should provide translated text to GitLab UI pagination', () => {
+ Object.entries(translations).forEach(entry =>
+ expect(
+ destinationComponent[entry[0]],
+ ).toBe(entry[1]),
+ );
+ });
+
+ it('should pass change to GitLab UI pagination', () => {
+ expect(
+ Object.is(glPagination.change, change),
+ ).toBe(true);
+ });
+
+ it('should pass page from pageInfo to GitLab UI pagination', () => {
+ expect(
+ destinationComponent.value,
+ ).toBe(pageInfo.page);
+ });
+
+ it('should pass per page from pageInfo to GitLab UI pagination', () => {
+ expect(
+ destinationComponent.perPage,
+ ).toBe(pageInfo.perPage);
+ });
+
+ it('should pass total items from pageInfo to GitLab UI pagination', () => {
+ expect(
+ destinationComponent.totalRows,
+ ).toBe(pageInfo.total);
+ });
+});
diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb
index a515d07b072..cf49249756a 100644
--- a/spec/lib/banzai/filter/markdown_filter_spec.rb
+++ b/spec/lib/banzai/filter/markdown_filter_spec.rb
@@ -40,6 +40,12 @@ describe Banzai::Filter::MarkdownFilter do
expect(result).to start_with("<pre><code>")
end
+
+ it 'works with utf8 chars in language' do
+ result = filter("```日\nsome code\n```")
+
+ expect(result).to start_with("<pre><code lang=\"日\">")
+ end
end
context 'using Redcarpet' do
@@ -60,4 +66,21 @@ describe Banzai::Filter::MarkdownFilter do
end
end
end
+
+ describe 'footnotes in tables' do
+ it 'processes footnotes in table cells' do
+ text = <<-MD.strip_heredoc
+ | Column1 |
+ | --------- |
+ | foot [^1] |
+
+ [^1]: a footnote
+ MD
+
+ result = filter(text)
+
+ expect(result).to include('<td>foot <sup')
+ expect(result).to include('<section class="footnotes">')
+ end
+ end
end
diff --git a/spec/models/clusters/applications/jupyter_spec.rb b/spec/models/clusters/applications/jupyter_spec.rb
index 591a01d78a9..44a64928e94 100644
--- a/spec/models/clusters/applications/jupyter_spec.rb
+++ b/spec/models/clusters/applications/jupyter_spec.rb
@@ -108,8 +108,21 @@ describe Clusters::Applications::Jupyter do
expect(values).to include('rbac')
expect(values).to include('proxy')
expect(values).to include('auth')
+ expect(values).to include('singleuser')
expect(values).to match(/clientId: '?#{application.oauth_application.uid}/)
expect(values).to match(/callbackUrl: '?#{application.callback_url}/)
end
+
+ context 'when cluster belongs to a project' do
+ let(:project) { create(:project) }
+
+ before do
+ application.cluster.projects << project
+ end
+
+ it 'sets GitLab project id' do
+ expect(values).to match(/GITLAB_PROJECT_ID: '?#{project.id}/)
+ end
+ end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index b6f92042ecc..65cd423f0bb 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -557,6 +557,14 @@ describe API::Projects do
expect(json_response['visibility']).to eq('private')
end
+ it 'creates a new project initialized with a README.md' do
+ project = attributes_for(:project, initialize_with_readme: 1, name: 'somewhere')
+
+ post api('/projects', user), project
+
+ expect(json_response['readme_url']).to eql("#{Gitlab.config.gitlab.url}/#{json_response['namespace']['full_path']}/somewhere/blob/master/README.md")
+ end
+
it 'sets tag list to a project' do
project = attributes_for(:project, tag_list: %w[tagFirst tagSecond])
diff --git a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
index 3057845061b..a096627ee62 100644
--- a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
@@ -73,9 +73,13 @@ RSpec.shared_examples 'an editable merge request' do
it 'description has autocomplete', :js do
find('#merge_request_description').native.send_keys('')
- fill_in 'merge_request_description', with: '@'
+ fill_in 'merge_request_description', with: user.to_reference[0..4]
- expect(page).to have_selector('.atwho-view')
+ wait_for_requests
+
+ page.within('.atwho-view') do
+ expect(page).to have_content(user2.name)
+ end
end
it 'has class js-quick-submit in form' do
diff --git a/spec/workers/project_service_worker_spec.rb b/spec/workers/project_service_worker_spec.rb
new file mode 100644
index 00000000000..56934f122e4
--- /dev/null
+++ b/spec/workers/project_service_worker_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe ProjectServiceWorker, '#perform' do
+ let(:worker) { described_class.new }
+ let(:service) { JiraService.new }
+
+ before do
+ allow(Service).to receive(:find).and_return(service)
+ end
+
+ it 'executes service with given data' do
+ data = { test: 'test' }
+ expect(service).to receive(:execute).with(data)
+
+ worker.perform(1, data)
+ end
+
+ it 'logs error messages' do
+ allow(service).to receive(:execute).and_raise(StandardError, 'invalid URL')
+ expect(Sidekiq.logger).to receive(:error).with({ class: described_class.name, service_class: service.class.name, message: "invalid URL" })
+
+ worker.perform(1, {})
+ end
+end