diff options
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 |