From 405d8b456f65c7165e8cca83ab0be4c9db6cadd2 Mon Sep 17 00:00:00 2001 From: Jose Ivan Vargas Date: Thu, 18 Jan 2018 16:22:20 -0600 Subject: Added pipeline status to the files view --- .../commit_pipeline_status_component.vue | 95 ++++++++++++++++++++++ .../tree/services/commit_pipeline_service.js | 11 +++ .../javascripts/pages/projects/tree/show/index.js | 18 ++++ app/assets/stylesheets/pages/commits.scss | 9 ++ app/helpers/ci_status_helper.rb | 11 --- app/views/projects/commits/_commit.html.haml | 7 +- .../commit_pipeline_status_component_spec.js | 68 ++++++++++++++++ 7 files changed, 202 insertions(+), 17 deletions(-) create mode 100644 app/assets/javascripts/pages/projects/tree/components/commit_pipeline_status_component.vue create mode 100644 app/assets/javascripts/pages/projects/tree/services/commit_pipeline_service.js create mode 100644 spec/javascripts/commit/commit_pipeline_status_component_spec.js diff --git a/app/assets/javascripts/pages/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/pages/projects/tree/components/commit_pipeline_status_component.vue new file mode 100644 index 00000000000..2bd1e8e3266 --- /dev/null +++ b/app/assets/javascripts/pages/projects/tree/components/commit_pipeline_status_component.vue @@ -0,0 +1,95 @@ + + diff --git a/app/assets/javascripts/pages/projects/tree/services/commit_pipeline_service.js b/app/assets/javascripts/pages/projects/tree/services/commit_pipeline_service.js new file mode 100644 index 00000000000..4b4189bc2de --- /dev/null +++ b/app/assets/javascripts/pages/projects/tree/services/commit_pipeline_service.js @@ -0,0 +1,11 @@ +import axios from '~/lib/utils/axios_utils'; + +export default class CommitPipelineService { + constructor(endpoint) { + this.endpoint = endpoint; + } + + fetchData() { + return axios.get(this.endpoint); + } +} diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js index 28a0160f47d..7181d4dfd47 100644 --- a/app/assets/javascripts/pages/projects/tree/show/index.js +++ b/app/assets/javascripts/pages/projects/tree/show/index.js @@ -1,8 +1,10 @@ +import Vue from 'vue'; import TreeView from '../../../../tree'; import ShortcutsNavigation from '../../../../shortcuts_navigation'; import BlobViewer from '../../../../blob/viewer'; import NewCommitForm from '../../../../new_commit_form'; import { ajaxGet } from '../../../../lib/utils/common_utils'; +import commitPipelineStatus from '../components/commit_pipeline_status_component.vue'; export default () => { new ShortcutsNavigation(); // eslint-disable-line no-new @@ -11,5 +13,21 @@ export default () => { new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new $('#tree-slider').waitForImages(() => ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath)); + + const commitPipelineStatusEl = document.getElementById('commit-pipeline-status'); + // eslint-disable-next-line no-new + new Vue({ + el: '#commit-pipeline-status', + components: { + commitPipelineStatus, + }, + render(createElement) { + return createElement('commit-pipeline-status', { + props: { + endpoint: commitPipelineStatusEl.dataset.endpoint, + }, + }); + }, + }); }; diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index aeaa33bd3bd..adfd72556b8 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -195,6 +195,10 @@ .commit-actions { @media (min-width: $screen-sm-min) { font-size: 0; + + span { + font-size: 6px; + } } .ci-status-link { @@ -219,6 +223,11 @@ font-size: 14px; font-weight: $gl-font-weight-bold; } + + .ci-status-icon { + position: relative; + top: 3px; + } } .commit, diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 636316da80a..e8365f1da1e 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -103,17 +103,6 @@ module CiStatusHelper tooltip_placement: tooltip_placement) end - def render_commit_status(commit, ref: nil, tooltip_placement: 'auto left') - project = commit.project - path = pipelines_project_commit_path(project, commit) - - render_status_with_link( - 'commit', - commit.status(ref), - path, - tooltip_placement: tooltip_placement) - end - def render_pipeline_status(pipeline, tooltip_placement: 'auto left') project = pipeline.project path = project_pipeline_path(project, pipeline) diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index d66066a6d0b..436e1739180 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -26,9 +26,6 @@ %span.commit-row-message.visible-xs-inline · = commit.short_id - - if commit.status(ref) - .visible-xs-inline - = render_commit_status(commit, ref: ref) - if commit.description? %button.text-expander.hidden-xs.js-toggle-button{ type: "button" } ... @@ -48,9 +45,7 @@ - else = render partial: 'projects/commit/ajax_signature', locals: { commit: commit } - - if commit.status(ref) - = render_commit_status(commit, ref: ref) - + #commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id) } } = link_to commit.short_id, link, class: "commit-sha btn btn-transparent btn-link" = clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard")) = link_to_browse_code(project, commit) diff --git a/spec/javascripts/commit/commit_pipeline_status_component_spec.js b/spec/javascripts/commit/commit_pipeline_status_component_spec.js new file mode 100644 index 00000000000..f6fca9e97e5 --- /dev/null +++ b/spec/javascripts/commit/commit_pipeline_status_component_spec.js @@ -0,0 +1,68 @@ +import Vue from 'vue'; +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; +import commitPipelineStatus from '~/pages/projects/tree/components/commit_pipeline_status_component.vue'; +import mountComponent from '../helpers/vue_mount_component_helper'; + +describe('Commit pipeline status component', () => { + let vm; + let component; + let mock; + const mockCiStatus = { + details_path: '/root/hello-world/pipelines/1', + favicon: 'canceled.ico', + group: 'canceled', + has_details: true, + icon: 'status_canceled', + label: 'canceled', + text: 'canceled', + }; + + beforeEach(() => { + mock = new MockAdapter(axios); + mock.onGet('/dummy/endpoint').reply(() => { + const res = Promise.resolve([200, { + pipelines: [ + { + details: { + status: mockCiStatus, + }, + }, + ], + }]); + return res; + }); + component = Vue.extend(commitPipelineStatus); + }); + + afterEach(() => { + mock.reset(); + }); + + describe('While polling pipeline data', () => { + beforeEach(() => { + vm = mountComponent(component, { + endpoint: '/dummy/endpoint', + }); + }); + + afterEach(() => { + vm.poll.stop(); + vm.$destroy(); + }); + + it('contains a ciStatus when the polling is succesful ', (done) => { + setTimeout(() => { + expect(vm.ciStatus).toEqual(mockCiStatus); + done(); + }, 1000); + }); + + it('contains a ci-status icon when polling is succesful', (done) => { + setTimeout(() => { + expect(vm.$el.querySelector('.ci-status-icon')).not.toBe(null); + done(); + }); + }); + }); +}); -- cgit v1.2.1 From 6042454600d79f1d6fb8e216c78b3e8b619a7a3e Mon Sep 17 00:00:00 2001 From: Jose Ivan Vargas Date: Fri, 19 Jan 2018 15:09:42 -0600 Subject: Added changelog --- .../35779-realtime-update-of-pipeline-status-in-files-view.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/35779-realtime-update-of-pipeline-status-in-files-view.yml diff --git a/changelogs/unreleased/35779-realtime-update-of-pipeline-status-in-files-view.yml b/changelogs/unreleased/35779-realtime-update-of-pipeline-status-in-files-view.yml new file mode 100644 index 00000000000..82df00fe631 --- /dev/null +++ b/changelogs/unreleased/35779-realtime-update-of-pipeline-status-in-files-view.yml @@ -0,0 +1,5 @@ +--- +title: Add realtime ci status for the repository -> files view +merge_request: 16523 +author: +type: added -- cgit v1.2.1 From bfc2b8a3d2c06c80126365348ce75b3985185e83 Mon Sep 17 00:00:00 2001 From: Jose Ivan Vargas Date: Mon, 22 Jan 2018 15:57:32 -0600 Subject: Added realtime prop and corrected specs --- .../javascripts/pages/projects/show/index.js | 19 ++++ .../commit_pipeline_status_component.vue | 95 ------------------ .../tree/services/commit_pipeline_service.js | 11 --- .../javascripts/pages/projects/tree/show/index.js | 2 +- .../commit_pipeline_status_component.vue | 106 +++++++++++++++++++++ .../tree/services/commit_pipeline_service.js | 11 +++ features/project/project.feature | 1 + features/steps/shared/project.rb | 1 + .../commit_pipeline_status_component_spec.js | 9 +- 9 files changed, 146 insertions(+), 109 deletions(-) delete mode 100644 app/assets/javascripts/pages/projects/tree/components/commit_pipeline_status_component.vue delete mode 100644 app/assets/javascripts/pages/projects/tree/services/commit_pipeline_service.js create mode 100644 app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue create mode 100644 app/assets/javascripts/projects/tree/services/commit_pipeline_service.js diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index 55154cdddcb..0213b46eb7c 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -1,3 +1,4 @@ +import Vue from 'vue'; import ShortcutsNavigation from '~/shortcuts_navigation'; import NotificationsForm from '~/notifications_form'; import UserCallout from '~/user_callout'; @@ -5,6 +6,7 @@ import TreeView from '~/tree'; import BlobViewer from '~/blob/viewer/index'; import Activities from '~/activities'; import { ajaxGet } from '~/lib/utils/common_utils'; +import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; import Star from '../../../star'; import notificationsDropdown from '../../../notifications_dropdown'; @@ -24,4 +26,21 @@ export default () => { $('#tree-slider').waitForImages(() => { ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath); }); + + const commitPipelineStatusEl = document.getElementById('commit-pipeline-status'); + // eslint-disable-next-line no-new + new Vue({ + el: '#commit-pipeline-status', + components: { + commitPipelineStatus, + }, + render(createElement) { + return createElement('commit-pipeline-status', { + props: { + endpoint: commitPipelineStatusEl.dataset.endpoint, + realtime: false, + }, + }); + }, + }); }; diff --git a/app/assets/javascripts/pages/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/pages/projects/tree/components/commit_pipeline_status_component.vue deleted file mode 100644 index 2bd1e8e3266..00000000000 --- a/app/assets/javascripts/pages/projects/tree/components/commit_pipeline_status_component.vue +++ /dev/null @@ -1,95 +0,0 @@ - - diff --git a/app/assets/javascripts/pages/projects/tree/services/commit_pipeline_service.js b/app/assets/javascripts/pages/projects/tree/services/commit_pipeline_service.js deleted file mode 100644 index 4b4189bc2de..00000000000 --- a/app/assets/javascripts/pages/projects/tree/services/commit_pipeline_service.js +++ /dev/null @@ -1,11 +0,0 @@ -import axios from '~/lib/utils/axios_utils'; - -export default class CommitPipelineService { - constructor(endpoint) { - this.endpoint = endpoint; - } - - fetchData() { - return axios.get(this.endpoint); - } -} diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js index 7181d4dfd47..c0b3f98e66d 100644 --- a/app/assets/javascripts/pages/projects/tree/show/index.js +++ b/app/assets/javascripts/pages/projects/tree/show/index.js @@ -1,10 +1,10 @@ import Vue from 'vue'; +import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; import TreeView from '../../../../tree'; import ShortcutsNavigation from '../../../../shortcuts_navigation'; import BlobViewer from '../../../../blob/viewer'; import NewCommitForm from '../../../../new_commit_form'; import { ajaxGet } from '../../../../lib/utils/common_utils'; -import commitPipelineStatus from '../components/commit_pipeline_status_component.vue'; export default () => { new ShortcutsNavigation(); // eslint-disable-line no-new diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue new file mode 100644 index 00000000000..e13acf8555a --- /dev/null +++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue @@ -0,0 +1,106 @@ + + diff --git a/app/assets/javascripts/projects/tree/services/commit_pipeline_service.js b/app/assets/javascripts/projects/tree/services/commit_pipeline_service.js new file mode 100644 index 00000000000..4b4189bc2de --- /dev/null +++ b/app/assets/javascripts/projects/tree/services/commit_pipeline_service.js @@ -0,0 +1,11 @@ +import axios from '~/lib/utils/axios_utils'; + +export default class CommitPipelineService { + constructor(endpoint) { + this.endpoint = endpoint; + } + + fetchData() { + return axios.get(this.endpoint); + } +} diff --git a/features/project/project.feature b/features/project/project.feature index 23817ef3ac9..bcd72c5c5a3 100644 --- a/features/project/project.feature +++ b/features/project/project.feature @@ -23,6 +23,7 @@ Feature: Project And I visit project "Shop" page Then I should see project "Shop" README + @javascript Scenario: I should see last commit with CI Given project "Shop" has CI enabled Given project "Shop" has CI build diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index affbccccdf9..923d54a6545 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -218,6 +218,7 @@ module SharedProject end step 'I should see last commit with CI status' do + sleep 2 page.within ".blob-commit-info" do expect(page).to have_content(project.commit.sha[0..6]) expect(page).to have_link("Commit: skipped") diff --git a/spec/javascripts/commit/commit_pipeline_status_component_spec.js b/spec/javascripts/commit/commit_pipeline_status_component_spec.js index f6fca9e97e5..2a52097e0d5 100644 --- a/spec/javascripts/commit/commit_pipeline_status_component_spec.js +++ b/spec/javascripts/commit/commit_pipeline_status_component_spec.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; -import commitPipelineStatus from '~/pages/projects/tree/components/commit_pipeline_status_component.vue'; +import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; import mountComponent from '../helpers/vue_mount_component_helper'; describe('Commit pipeline status component', () => { @@ -25,7 +25,12 @@ describe('Commit pipeline status component', () => { pipelines: [ { details: { - status: mockCiStatus, + stages: [ + { + status: mockCiStatus, + title: 'Commit: canceled', + }, + ], }, }, ], -- cgit v1.2.1 From 449b0ebaf6f3ca65b48f372293117acc9a7e0abc Mon Sep 17 00:00:00 2001 From: Jose Ivan Vargas Date: Mon, 22 Jan 2018 18:34:21 -0600 Subject: Restored some code, add hidden class --- app/assets/javascripts/pages/projects/show/index.js | 18 ------------------ .../javascripts/pages/projects/tree/show/index.js | 5 +++++ app/helpers/ci_status_helper.rb | 11 +++++++++++ app/views/projects/commits/_commit.html.haml | 8 +++++++- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index 0213b46eb7c..4c42fda16d7 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -1,4 +1,3 @@ -import Vue from 'vue'; import ShortcutsNavigation from '~/shortcuts_navigation'; import NotificationsForm from '~/notifications_form'; import UserCallout from '~/user_callout'; @@ -26,21 +25,4 @@ export default () => { $('#tree-slider').waitForImages(() => { ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath); }); - - const commitPipelineStatusEl = document.getElementById('commit-pipeline-status'); - // eslint-disable-next-line no-new - new Vue({ - el: '#commit-pipeline-status', - components: { - commitPipelineStatus, - }, - render(createElement) { - return createElement('commit-pipeline-status', { - props: { - endpoint: commitPipelineStatusEl.dataset.endpoint, - realtime: false, - }, - }); - }, - }); }; diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js index c0b3f98e66d..f14c3f86687 100644 --- a/app/assets/javascripts/pages/projects/tree/show/index.js +++ b/app/assets/javascripts/pages/projects/tree/show/index.js @@ -15,6 +15,11 @@ export default () => { ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath)); const commitPipelineStatusEl = document.getElementById('commit-pipeline-status'); + const $statusLink = $('.ci-status-link'); + if ($statusLink.length > 0) { + $statusLink.remove(); + } + commitPipelineStatusEl.classList.remove('hidden'); // eslint-disable-next-line no-new new Vue({ el: '#commit-pipeline-status', diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index e8365f1da1e..636316da80a 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -103,6 +103,17 @@ module CiStatusHelper tooltip_placement: tooltip_placement) end + def render_commit_status(commit, ref: nil, tooltip_placement: 'auto left') + project = commit.project + path = pipelines_project_commit_path(project, commit) + + render_status_with_link( + 'commit', + commit.status(ref), + path, + tooltip_placement: tooltip_placement) + end + def render_pipeline_status(pipeline, tooltip_placement: 'auto left') project = pipeline.project path = project_pipeline_path(project, pipeline) diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 436e1739180..c94e10947e6 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -26,6 +26,9 @@ %span.commit-row-message.visible-xs-inline · = commit.short_id + - if commit.status(ref) + .visible-xs-inline + = render_commit_status(commit, ref: ref) - if commit.description? %button.text-expander.hidden-xs.js-toggle-button{ type: "button" } ... @@ -45,7 +48,10 @@ - else = render partial: 'projects/commit/ajax_signature', locals: { commit: commit } - #commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id) } } + - if commit.status(ref) + = render_commit_status(commit, ref: ref) + + #commit-pipeline-status.hidden{ data: { endpoint: pipelines_project_commit_path(project, commit.id) } } = link_to commit.short_id, link, class: "commit-sha btn btn-transparent btn-link" = clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard")) = link_to_browse_code(project, commit) -- cgit v1.2.1 From 28bbb4cb47ebb8669643e8fad34b75ea34f18e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20=22BKC=22=20Carlb=C3=A4cker?= Date: Fri, 26 Jan 2018 09:00:22 +0100 Subject: Migrate Gitlab::Git::Repository#write_config to Gitaly - Add tests for Repository#write_config --- GITALY_SERVER_VERSION | 2 +- lib/gitlab/git.rb | 1 + lib/gitlab/git/repository.rb | 14 +++++++++- lib/gitlab/gitaly_client/repository_service.rb | 13 +++++++++ spec/lib/gitlab/git/repository_spec.rb | 38 ++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 2 deletions(-) diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 62df9f538d8..4a7076db09a 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.76.0 +0.77.0 diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb index 71647099f83..9e8caa259d2 100644 --- a/lib/gitlab/git.rb +++ b/lib/gitlab/git.rb @@ -6,6 +6,7 @@ module Gitlab CommandError = Class.new(StandardError) CommitError = Class.new(StandardError) + OSError = Class.new(StandardError) class << self include Gitlab::EncodingHelper diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 8137c582c0f..8ba12808c10 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1311,7 +1311,15 @@ module Gitlab # rubocop:enable Metrics/ParameterLists def write_config(full_path:) - rugged.config['gitlab.fullpath'] = full_path if full_path.present? + return unless full_path.present? + + gitaly_migrate(:write_config) do |is_enabled| + if is_enabled + gitaly_repository_client.write_config(full_path: full_path) + else + rugged_write_config(full_path: full_path) + end + end end def gitaly_repository @@ -1451,6 +1459,10 @@ module Gitlab end end + def rugged_write_config(full_path:) + rugged.config['gitlab.fullpath'] = full_path + end + def shell_write_ref(ref_path, ref, old_ref) raise ArgumentError, "invalid ref_path #{ref_path.inspect}" if ref_path.include?(' ') raise ArgumentError, "invalid ref #{ref.inspect}" if ref.include?("\x00") diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb index 7adf32af209..60706b4f0d8 100644 --- a/lib/gitlab/gitaly_client/repository_service.rb +++ b/lib/gitlab/gitaly_client/repository_service.rb @@ -219,6 +219,19 @@ module Gitlab true end + + def write_config(full_path:) + request = Gitaly::WriteConfigRequest.new(repository: @gitaly_repo, full_path: full_path) + response = GitalyClient.call( + @storage, + :repository_service, + :write_config, + request, + timeout: GitalyClient.fast_timeout + ) + + raise Gitlab::Git::OSError.new(response.error) unless response.error.empty? + end end end end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index bf01e6ef8e8..6206e9bb0fb 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1790,6 +1790,44 @@ describe Gitlab::Git::Repository, seed_helper: true do end end + describe '#write_config' do + before do + repository.rugged.config["gitlab.fullpath"] = repository.path + end + + shared_examples 'writing repo config' do + context 'is given a path' do + it 'writes it to disk' do + repository.write_config(full_path: "not-the/real-path.git") + + config = File.read(File.join(repository.path, "config")) + + expect(config).to include("[gitlab]") + expect(config).to include("fullpath = not-the/real-path.git") + end + end + + context 'it is given an empty path' do + it 'does not write it to disk' do + repository.write_config(full_path: "") + + config = File.read(File.join(repository.path, "config")) + + expect(config).to include("[gitlab]") + expect(config).to include("fullpath = #{repository.path}") + end + end + end + + context "when gitaly_write_config is enabled" do + it_behaves_like "writing repo config" + end + + context "when gitaly_write_config is disabled", :disable_gitaly do + it_behaves_like "writing repo config" + end + end + describe '#merge' do let(:repository) do Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') -- cgit v1.2.1 From a2718ebaffd431b1eec91f3ee467d9632309b5f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 4 Aug 2017 19:14:04 +0200 Subject: Make user/author use project.creator in most factories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- spec/factories/commits.rb | 2 +- spec/factories/deployments.rb | 3 ++- spec/factories/events.rb | 2 +- spec/factories/issues.rb | 2 +- spec/factories/merge_requests.rb | 2 +- spec/factories/notes.rb | 2 +- spec/factories/project_wikis.rb | 2 +- spec/factories/sent_notifications.rb | 2 +- spec/factories/snippets.rb | 1 + spec/factories/subscriptions.rb | 2 +- spec/factories/timelogs.rb | 2 +- spec/factories/todos.rb | 4 ++-- spec/features/dashboard/merge_requests_spec.rb | 22 ++++++++++++---------- .../merge_request/user_awards_emoji_spec.rb | 2 +- .../lib/gitlab/slash_commands/issue_search_spec.rb | 2 +- spec/policies/project_policy_spec.rb | 2 +- spec/services/issues/close_service_spec.rb | 2 +- spec/services/issues/update_service_spec.rb | 3 ++- spec/services/merge_requests/close_service_spec.rb | 2 +- .../merge_requests/ff_merge_service_spec.rb | 3 ++- .../services/merge_requests/reopen_service_spec.rb | 2 +- .../services/merge_requests/update_service_spec.rb | 3 ++- spec/services/notification_service_spec.rb | 6 ++++-- 23 files changed, 42 insertions(+), 33 deletions(-) diff --git a/spec/factories/commits.rb b/spec/factories/commits.rb index 84a8bc56640..d5d819d862a 100644 --- a/spec/factories/commits.rb +++ b/spec/factories/commits.rb @@ -23,7 +23,7 @@ FactoryBot.define do end after(:build) do |commit, evaluator| - allow(commit).to receive(:author).and_return(evaluator.author || build(:author)) + allow(commit).to receive(:author).and_return(evaluator.author || build_stubbed(:author)) end trait :without_author do diff --git a/spec/factories/deployments.rb b/spec/factories/deployments.rb index 9d7d5e56611..cac56695319 100644 --- a/spec/factories/deployments.rb +++ b/spec/factories/deployments.rb @@ -3,13 +3,14 @@ FactoryBot.define do sha '97de212e80737a608d939f648d959671fb0a0142' ref 'master' tag false - user + user nil project nil deployable factory: :ci_build environment factory: :environment after(:build) do |deployment, evaluator| deployment.project ||= deployment.environment.project + deployment.user ||= deployment.project.creator unless deployment.project.repository_exists? allow(deployment.project.repository).to receive(:create_ref) diff --git a/spec/factories/events.rb b/spec/factories/events.rb index ed275243ac9..5798b81ecad 100644 --- a/spec/factories/events.rb +++ b/spec/factories/events.rb @@ -1,7 +1,7 @@ FactoryBot.define do factory :event do project - author factory: :user + author(factory: :user) { project.creator } action Event::JOINED trait(:created) { action Event::CREATED } diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb index 71dc169c6a2..998080a3dd5 100644 --- a/spec/factories/issues.rb +++ b/spec/factories/issues.rb @@ -1,8 +1,8 @@ FactoryBot.define do factory :issue do title { generate(:title) } - author project + author { project.creator } trait :confidential do confidential true diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index 40558c88d15..d26cb0c3417 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -1,9 +1,9 @@ FactoryBot.define do factory :merge_request do title { generate(:title) } - author association :source_project, :repository, factory: :project target_project { source_project } + author { source_project.creator } # $ git log --pretty=oneline feature..master # 5937ac0a7beb003549fc5fd26fc247adbce4a52e Add submodule from gitlab.com diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb index 707ecbd6be5..2defb4935ad 100644 --- a/spec/factories/notes.rb +++ b/spec/factories/notes.rb @@ -6,7 +6,7 @@ FactoryBot.define do factory :note do project note { generate(:title) } - author + author { project&.creator || create(:user) } on_issue factory :note_on_commit, traits: [:on_commit] diff --git a/spec/factories/project_wikis.rb b/spec/factories/project_wikis.rb index 89d8248f9f4..db2eb4fc863 100644 --- a/spec/factories/project_wikis.rb +++ b/spec/factories/project_wikis.rb @@ -3,7 +3,7 @@ FactoryBot.define do skip_create project - user factory: :user + user { project.creator } initialize_with { new(project, user) } end end diff --git a/spec/factories/sent_notifications.rb b/spec/factories/sent_notifications.rb index 80872067233..b0174dd06b7 100644 --- a/spec/factories/sent_notifications.rb +++ b/spec/factories/sent_notifications.rb @@ -1,7 +1,7 @@ FactoryBot.define do factory :sent_notification do project - recipient factory: :user + recipient { project.creator } noteable { create(:issue, project: project) } reply_key { SentNotification.reply_key } end diff --git a/spec/factories/snippets.rb b/spec/factories/snippets.rb index 2ab9a56d255..dc12b562108 100644 --- a/spec/factories/snippets.rb +++ b/spec/factories/snippets.rb @@ -21,6 +21,7 @@ FactoryBot.define do factory :project_snippet, parent: :snippet, class: :ProjectSnippet do project + author { project.creator } end factory :personal_snippet, parent: :snippet, class: :PersonalSnippet do diff --git a/spec/factories/subscriptions.rb b/spec/factories/subscriptions.rb index a4bc4e87b0a..8f7ab74ec70 100644 --- a/spec/factories/subscriptions.rb +++ b/spec/factories/subscriptions.rb @@ -1,7 +1,7 @@ FactoryBot.define do factory :subscription do - user project + user { project.creator } subscribable factory: :issue end end diff --git a/spec/factories/timelogs.rb b/spec/factories/timelogs.rb index af34b0681e2..b45f06b9a0a 100644 --- a/spec/factories/timelogs.rb +++ b/spec/factories/timelogs.rb @@ -3,7 +3,7 @@ FactoryBot.define do factory :timelog do time_spent 3600 - user issue + user { issue.project.creator } end end diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb index 6a6de665dd1..94f8caedfa6 100644 --- a/spec/factories/todos.rb +++ b/spec/factories/todos.rb @@ -1,8 +1,8 @@ FactoryBot.define do factory :todo do project - author - user + author { project.creator } + user { project.creator } target factory: :issue action { Todo::ASSIGNED } diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb index 991d360ccaf..744041ac425 100644 --- a/spec/features/dashboard/merge_requests_spec.rb +++ b/spec/features/dashboard/merge_requests_spec.rb @@ -44,36 +44,38 @@ feature 'Dashboard Merge Requests' do context 'merge requests exist' do let!(:assigned_merge_request) do - create(:merge_request, assignee: current_user, target_project: project, source_project: project) + create(:merge_request, + assignee: current_user, + source_project: project, + author: create(:user)) end let!(:assigned_merge_request_from_fork) do create(:merge_request, source_branch: 'markdown', assignee: current_user, - target_project: public_project, source_project: forked_project - ) + target_project: public_project, source_project: forked_project, + author: create(:user)) end let!(:authored_merge_request) do create(:merge_request, - source_branch: 'markdown', author: current_user, - target_project: project, source_project: project - ) + source_branch: 'markdown', + source_project: project, + author: current_user) end let!(:authored_merge_request_from_fork) do create(:merge_request, source_branch: 'feature_conflict', author: current_user, - target_project: public_project, source_project: forked_project - ) + target_project: public_project, source_project: forked_project) end let!(:other_merge_request) do create(:merge_request, source_branch: 'fix', - target_project: project, source_project: project - ) + source_project: project, + author: create(:user)) end before do diff --git a/spec/features/merge_request/user_awards_emoji_spec.rb b/spec/features/merge_request/user_awards_emoji_spec.rb index 15a0878fb16..2f24cfbd9e3 100644 --- a/spec/features/merge_request/user_awards_emoji_spec.rb +++ b/spec/features/merge_request/user_awards_emoji_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' describe 'Merge request > User awards emoji', :js do let(:project) { create(:project, :public, :repository) } let(:user) { project.creator } - let(:merge_request) { create(:merge_request, source_project: project) } + let(:merge_request) { create(:merge_request, source_project: project, author: create(:user)) } describe 'logged in' do before do diff --git a/spec/lib/gitlab/slash_commands/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/issue_search_spec.rb index e41e5254dde..35d01efc1bd 100644 --- a/spec/lib/gitlab/slash_commands/issue_search_spec.rb +++ b/spec/lib/gitlab/slash_commands/issue_search_spec.rb @@ -5,7 +5,7 @@ describe Gitlab::SlashCommands::IssueSearch do let!(:issue) { create(:issue, project: project, title: 'find me') } let!(:confidential) { create(:issue, :confidential, project: project, title: 'mepmep find') } let(:project) { create(:project) } - let(:user) { issue.author } + let(:user) { create(:user) } let(:regex_match) { described_class.match("issue search find") } subject do diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index f2593a1a75c..129344f105f 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -92,7 +92,7 @@ describe ProjectPolicy do it 'does not include the read_issue permission when the issue author is not a member of the private project' do project = create(:project, :private) - issue = create(:issue, project: project) + issue = create(:issue, project: project, author: create(:user)) user = issue.author expect(project.team.member?(issue.author)).to be false diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb index 8897a64a138..47c1ebbeb81 100644 --- a/spec/services/issues/close_service_spec.rb +++ b/spec/services/issues/close_service_spec.rb @@ -4,7 +4,7 @@ describe Issues::CloseService do let(:user) { create(:user) } let(:user2) { create(:user) } let(:guest) { create(:user) } - let(:issue) { create(:issue, assignees: [user2]) } + let(:issue) { create(:issue, assignees: [user2], author: create(:user)) } let(:project) { issue.project } let!(:todo) { create(:todo, :assigned, user: user, project: project, target: issue, author: user2) } diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 1cb6f2e097f..41237dd7160 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -13,7 +13,8 @@ describe Issues::UpdateService, :mailer do create(:issue, title: 'Old title', description: "for #{user2.to_reference}", assignee_ids: [user3.id], - project: project) + project: project, + author: create(:user)) end before do diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb index 4d12de3ecce..216e0cd4266 100644 --- a/spec/services/merge_requests/close_service_spec.rb +++ b/spec/services/merge_requests/close_service_spec.rb @@ -4,7 +4,7 @@ describe MergeRequests::CloseService do let(:user) { create(:user) } let(:user2) { create(:user) } let(:guest) { create(:user) } - let(:merge_request) { create(:merge_request, assignee: user2) } + let(:merge_request) { create(:merge_request, assignee: user2, author: create(:user)) } let(:project) { merge_request.project } let!(:todo) { create(:todo, :assigned, user: user, project: project, target: merge_request, author: user2) } diff --git a/spec/services/merge_requests/ff_merge_service_spec.rb b/spec/services/merge_requests/ff_merge_service_spec.rb index aa90feeef89..5ef6365fcc9 100644 --- a/spec/services/merge_requests/ff_merge_service_spec.rb +++ b/spec/services/merge_requests/ff_merge_service_spec.rb @@ -7,7 +7,8 @@ describe MergeRequests::FfMergeService do create(:merge_request, source_branch: 'flatten-dir', target_branch: 'improve/awesome', - assignee: user2) + assignee: user2, + author: create(:user)) end let(:project) { merge_request.project } diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb index a44d63e5f9f..9ee37c51d95 100644 --- a/spec/services/merge_requests/reopen_service_spec.rb +++ b/spec/services/merge_requests/reopen_service_spec.rb @@ -4,7 +4,7 @@ describe MergeRequests::ReopenService do let(:user) { create(:user) } let(:user2) { create(:user) } let(:guest) { create(:user) } - let(:merge_request) { create(:merge_request, :closed, assignee: user2) } + let(:merge_request) { create(:merge_request, :closed, assignee: user2, author: create(:user)) } let(:project) { merge_request.project } before do diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index 2238da2d14d..c31259239ee 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -12,7 +12,8 @@ describe MergeRequests::UpdateService, :mailer do create(:merge_request, :simple, title: 'Old title', description: "FYI #{user2.to_reference}", assignee_id: user3.id, - source_project: project) + source_project: project, + author: create(:user)) end before do diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 5c59455e3e1..35eb84e5e88 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -458,7 +458,7 @@ describe NotificationService, :mailer do context "merge request diff note" do let(:project) { create(:project, :repository) } let(:user) { create(:user) } - let(:merge_request) { create(:merge_request, source_project: project, assignee: user) } + let(:merge_request) { create(:merge_request, source_project: project, assignee: user, author: create(:user)) } let(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request) } before do @@ -469,11 +469,13 @@ describe NotificationService, :mailer do describe '#new_note' do it "records sent notifications" do - # Ensure create SentNotification by noteable = merge_request 6 times, not noteable = note + # 3 SentNotification are sent: the MR assignee and author, and the @u_watcher expect(SentNotification).to receive(:record_note).with(note, any_args).exactly(3).times.and_call_original notification.new_note(note) + expect(SentNotification.last(3).map(&:recipient).map(&:id)) + .to contain_exactly(merge_request.assignee.id, merge_request.author.id, @u_watcher.id) expect(SentNotification.last.in_reply_to_discussion_id).to eq(note.discussion_id) end end -- cgit v1.2.1 From c6311207eef6cf66f77d410269efc7581288cb2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 31 Jan 2018 18:30:31 +0100 Subject: Fix a JSON schema that doesn't include enough fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- spec/fixtures/api/schemas/public_api/v4/user/basic.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/fixtures/api/schemas/public_api/v4/user/basic.json b/spec/fixtures/api/schemas/public_api/v4/user/basic.json index bf330d8278c..2d815be32a6 100644 --- a/spec/fixtures/api/schemas/public_api/v4/user/basic.json +++ b/spec/fixtures/api/schemas/public_api/v4/user/basic.json @@ -2,12 +2,16 @@ "type": ["object", "null"], "required": [ "id", + "name", + "username", "state", "avatar_url", "web_url" ], "properties": { "id": { "type": "integer" }, + "name": { "type": "string" }, + "username": { "type": "string" }, "state": { "type": "string" }, "avatar_url": { "type": "string" }, "web_url": { "type": "string" } -- cgit v1.2.1 From 73582d9fe786364d585768e1766a29b342b34b33 Mon Sep 17 00:00:00 2001 From: James Ramsay Date: Wed, 31 Jan 2018 14:32:35 -0500 Subject: Fix subgroup creation docs --- .../group/subgroups/img/create_subgroup_button.png | Bin 8402 -> 11161 bytes doc/user/group/subgroups/index.md | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/user/group/subgroups/img/create_subgroup_button.png b/doc/user/group/subgroups/img/create_subgroup_button.png index 000b54c2855..d1355d4b2c3 100644 Binary files a/doc/user/group/subgroups/img/create_subgroup_button.png and b/doc/user/group/subgroups/img/create_subgroup_button.png differ diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md index 161a3af9903..2a982344e5f 100644 --- a/doc/user/group/subgroups/index.md +++ b/doc/user/group/subgroups/index.md @@ -90,7 +90,8 @@ structure. To create a subgroup: -1. In the group's dashboard go to the **Subgroups** page and click **New subgroup**. +1. In the group's dashboard expand the **New project** split button, select + **New subgroup** and click the **New subgroup** button. ![Subgroups page](img/create_subgroup_button.png) -- cgit v1.2.1 From b5b304466fbd8904196afeaa65adeb5282b21987 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 1 Feb 2018 10:34:12 +0000 Subject: Converted mini_pipeline_graph_dropdown.js to axios --- .../javascripts/mini_pipeline_graph_dropdown.js | 26 ++++---- .../mini_pipeline_graph_dropdown_spec.js | 73 ++++++++++++++-------- 2 files changed, 58 insertions(+), 41 deletions(-) diff --git a/app/assets/javascripts/mini_pipeline_graph_dropdown.js b/app/assets/javascripts/mini_pipeline_graph_dropdown.js index ca3d271663b..c7bccd483ac 100644 --- a/app/assets/javascripts/mini_pipeline_graph_dropdown.js +++ b/app/assets/javascripts/mini_pipeline_graph_dropdown.js @@ -1,5 +1,6 @@ /* eslint-disable no-new */ -import Flash from './flash'; +import flash from './flash'; +import axios from './lib/utils/axios_utils'; /** * In each pipelines table we have a mini pipeline graph for each pipeline. @@ -78,27 +79,22 @@ export default class MiniPipelineGraph { const button = e.relatedTarget; const endpoint = button.dataset.stageEndpoint; - return $.ajax({ - dataType: 'json', - type: 'GET', - url: endpoint, - beforeSend: () => { - this.renderBuildsList(button, ''); - this.toggleLoading(button); - }, - success: (data) => { + this.renderBuildsList(button, ''); + this.toggleLoading(button); + + axios.get(endpoint) + .then(({ data }) => { this.toggleLoading(button); this.renderBuildsList(button, data.html); this.stopDropdownClickPropagation(); - }, - error: () => { + }) + .catch(() => { this.toggleLoading(button); if ($(button).parent().hasClass('open')) { $(button).dropdown('toggle'); } - new Flash('An error occurred while fetching the builds.', 'alert'); - }, - }); + flash('An error occurred while fetching the builds.', 'alert'); + }); } /** diff --git a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js index 481b46c3ac6..6fa6f44f953 100644 --- a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js +++ b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js @@ -1,7 +1,9 @@ /* eslint-disable no-new */ +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown'; -import '~/flash'; +import timeoutPromise from './helpers/set_timeout_promise_helper'; describe('Mini Pipeline Graph Dropdown', () => { preloadFixtures('static/mini_dropdown_graph.html.raw'); @@ -27,6 +29,16 @@ describe('Mini Pipeline Graph Dropdown', () => { }); describe('When dropdown is clicked', () => { + let mock; + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + }); + it('should call getBuildsList', () => { const getBuildsListSpy = spyOn( MiniPipelineGraph.prototype, @@ -41,46 +53,55 @@ describe('Mini Pipeline Graph Dropdown', () => { }); it('should make a request to the endpoint provided in the html', () => { - const ajaxSpy = spyOn($, 'ajax').and.callFake(function () {}); + const ajaxSpy = spyOn(axios, 'get').and.callThrough(); + + mock.onGet('foobar').reply(200, { + html: '', + }); new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents(); document.querySelector('.js-builds-dropdown-button').click(); - expect(ajaxSpy.calls.allArgs()[0][0].url).toEqual('foobar'); + expect(ajaxSpy.calls.allArgs()[0][0]).toEqual('foobar'); }); - it('should not close when user uses cmd/ctrl + click', () => { - spyOn($, 'ajax').and.callFake(function (params) { - params.success({ - html: `
  • - - - build - - -
  • `, - }); + it('should not close when user uses cmd/ctrl + click', (done) => { + mock.onGet('foobar').reply(200, { + html: `
  • + + + build + + +
  • `, }); new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents(); document.querySelector('.js-builds-dropdown-button').click(); - document.querySelector('a.mini-pipeline-graph-dropdown-item').click(); - - expect($('.js-builds-dropdown-list').is(':visible')).toEqual(true); + timeoutPromise() + .then(() => { + document.querySelector('a.mini-pipeline-graph-dropdown-item').click(); + }) + .then(timeoutPromise) + .then(() => { + expect($('.js-builds-dropdown-list').is(':visible')).toEqual(true); + }) + .then(done) + .catch(done.fail); }); - }); - it('should close the dropdown when request returns an error', (done) => { - spyOn($, 'ajax').and.callFake(options => options.error()); + it('should close the dropdown when request returns an error', (done) => { + mock.onGet('foobar').networkError(); - new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents(); + new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents(); - document.querySelector('.js-builds-dropdown-button').click(); + document.querySelector('.js-builds-dropdown-button').click(); - setTimeout(() => { - expect($('.js-builds-dropdown-tests .dropdown').hasClass('open')).toEqual(false); - done(); - }, 0); + setTimeout(() => { + expect($('.js-builds-dropdown-tests .dropdown').hasClass('open')).toEqual(false); + done(); + }); + }); }); }); -- cgit v1.2.1 From c09a89c7557a93b728bab9eef9175abb1fd458d4 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 1 Feb 2018 10:42:49 +0000 Subject: Converted branch_graph.js to axios --- app/assets/javascripts/network/branch_graph.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/network/branch_graph.js b/app/assets/javascripts/network/branch_graph.js index 5aad3908eb6..d3edcb724f1 100644 --- a/app/assets/javascripts/network/branch_graph.js +++ b/app/assets/javascripts/network/branch_graph.js @@ -1,5 +1,8 @@ /* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, quotes, comma-dangle, one-var, one-var-declaration-per-line, no-mixed-operators, no-loop-func, no-floating-decimal, consistent-return, no-unused-vars, prefer-template, prefer-arrow-callback, camelcase, max-len */ +import { __ } from '../locale'; +import axios from '../lib/utils/axios_utils'; +import flash from '../flash'; import Raphael from './raphael'; export default (function() { @@ -26,16 +29,13 @@ export default (function() { } BranchGraph.prototype.load = function() { - return $.ajax({ - url: this.options.url, - method: "get", - dataType: "json", - success: $.proxy(function(data) { + axios.get(this.options.url) + .then(({ data }) => { $(".loading", this.element).hide(); this.prepareData(data.days, data.commits); - return this.buildGraph(); - }, this) - }); + this.buildGraph(); + }) + .catch(() => __('Error fetching network graph.')); }; BranchGraph.prototype.prepareData = function(days, commits) { -- cgit v1.2.1 From 1c8553f21e1cbfa730f47ea8c0be4080a9238f89 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 1 Feb 2018 10:50:18 +0000 Subject: Converted notes.js to axios --- app/assets/javascripts/notes.js | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index a2b8e6f6495..bcb342f407f 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -16,6 +16,7 @@ import Autosize from 'autosize'; import 'vendor/jquery.caret'; // required by jquery.atwho import 'vendor/jquery.atwho'; import AjaxCache from '~/lib/utils/ajax_cache'; +import axios from './lib/utils/axios_utils'; import { getLocationHash } from './lib/utils/url_utility'; import Flash from './flash'; import CommentTypeToggle from './comment_type_toggle'; @@ -252,26 +253,20 @@ export default class Notes { return; } this.refreshing = true; - return $.ajax({ - url: this.notes_url, - headers: { 'X-Last-Fetched-At': this.last_fetched_at }, - dataType: 'json', - success: (function(_this) { - return function(data) { - var notes; - notes = data.notes; - _this.last_fetched_at = data.last_fetched_at; - _this.setPollingInterval(data.notes.length); - return $.each(notes, function(i, note) { - _this.renderNote(note); - }); - }; - })(this) - }).always((function(_this) { - return function() { - return _this.refreshing = false; - }; - })(this)); + axios.get(this.notes_url, { + headers: { + 'X-Last-Fetched-At': this.last_fetched_at, + }, + }).then(({ data }) => { + const notes = data.notes; + this.last_fetched_at = data.last_fetched_at; + this.setPollingInterval(data.notes.length); + $.each(notes, (i, note) => this.renderNote(note)); + + this.refreshing = false; + }).catch(() => { + this.refreshing = false; + }); } /** -- cgit v1.2.1 From ab8e3a5595a441eb5b24cd7db5a877b65dcef704 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 1 Feb 2018 10:53:36 +0000 Subject: Converted notifications_form.js to axios --- app/assets/javascripts/notifications_form.js | 38 ++++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/app/assets/javascripts/notifications_form.js b/app/assets/javascripts/notifications_form.js index 4534360d577..4e0afe13590 100644 --- a/app/assets/javascripts/notifications_form.js +++ b/app/assets/javascripts/notifications_form.js @@ -1,3 +1,7 @@ +import { __ } from './locale'; +import axios from './lib/utils/axios_utils'; +import flash from './flash'; + export default class NotificationsForm { constructor() { this.toggleCheckbox = this.toggleCheckbox.bind(this); @@ -27,24 +31,20 @@ export default class NotificationsForm { saveEvent($checkbox, $parent) { const form = $parent.parents('form:first'); - return $.ajax({ - url: form.attr('action'), - method: form.attr('method'), - dataType: 'json', - data: form.serialize(), - beforeSend: () => { - this.showCheckboxLoadingSpinner($parent); - }, - }).done((data) => { - $checkbox.enable(); - if (data.saved) { - $parent.find('.custom-notification-event-loading').toggleClass('fa-spin fa-spinner fa-check is-done'); - setTimeout(() => { - $parent.removeClass('is-loading') - .find('.custom-notification-event-loading') - .toggleClass('fa-spin fa-spinner fa-check is-done'); - }, 2000); - } - }); + this.showCheckboxLoadingSpinner($parent); + + axios[form.attr('method')](form.attr('action'), form.serialize()) + .then(({ data }) => { + $checkbox.enable(); + if (data.saved) { + $parent.find('.custom-notification-event-loading').toggleClass('fa-spin fa-spinner fa-check is-done'); + setTimeout(() => { + $parent.removeClass('is-loading') + .find('.custom-notification-event-loading') + .toggleClass('fa-spin fa-spinner fa-check is-done'); + }, 2000); + } + }) + .catch(() => flash(__('There was an error saving your notification settings.'))); } } -- cgit v1.2.1 From ee1c471bad95cb640ea63393954825dd5a68a9e2 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 1 Feb 2018 11:12:37 +0000 Subject: Converted pager.js to axios --- app/assets/javascripts/pager.js | 33 +++++++++--------- spec/javascripts/pager_spec.js | 76 +++++++++++++++++++++++++++++++++-------- 2 files changed, 77 insertions(+), 32 deletions(-) diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js index 6552a88b606..fd3105b1960 100644 --- a/app/assets/javascripts/pager.js +++ b/app/assets/javascripts/pager.js @@ -1,4 +1,5 @@ import { getParameterByName } from '~/lib/utils/common_utils'; +import axios from './lib/utils/axios_utils'; import { removeParams } from './lib/utils/url_utility'; const ENDLESS_SCROLL_BOTTOM_PX = 400; @@ -22,24 +23,22 @@ export default { getOld() { this.loading.show(); - $.ajax({ - type: 'GET', - url: this.url, - data: `limit=${this.limit}&offset=${this.offset}`, - dataType: 'json', - error: () => this.loading.hide(), - success: (data) => { - this.append(data.count, this.prepareData(data.html)); - this.callback(); - - // keep loading until we've filled the viewport height - if (!this.disable && !this.isScrollable()) { - this.getOld(); - } else { - this.loading.hide(); - } + axios.get(this.url, { + params: { + limit: this.limit, + offset: this.offset, }, - }); + }).then(({ data }) => { + this.append(data.count, this.prepareData(data.html)); + this.callback(); + + // keep loading until we've filled the viewport height + if (!this.disable && !this.isScrollable()) { + this.getOld(); + } else { + this.loading.hide(); + } + }).catch(() => this.loading.hide()); }, append(count, html) { diff --git a/spec/javascripts/pager_spec.js b/spec/javascripts/pager_spec.js index 2fd87754238..fd9b83e3514 100644 --- a/spec/javascripts/pager_spec.js +++ b/spec/javascripts/pager_spec.js @@ -1,5 +1,6 @@ /* global fixture */ - +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; import * as utils from '~/lib/utils/url_utility'; import Pager from '~/pager'; @@ -9,7 +10,6 @@ describe('pager', () => { beforeEach(() => { setFixtures('
    '); - spyOn($, 'ajax'); }); afterEach(() => { @@ -47,39 +47,85 @@ describe('pager', () => { }); describe('getOld', () => { + const urlRegex = /\/some_list(.*)$/; + let mock; + + function mockSuccess() { + mock.onGet(urlRegex).reply(200, { + count: 20, + html: '', + }); + } + + function mockError() { + mock.onGet(urlRegex).networkError(); + } + beforeEach(() => { setFixtures('
    '); + spyOn(axios, 'get').and.callThrough(); + Pager.init(); + + mock = new MockAdapter(axios); }); - it('shows loader while loading next page', () => { + afterEach(() => { + mock.restore(); + }); + + it('shows loader while loading next page', (done) => { + mockSuccess(); + spyOn(Pager.loading, 'show'); Pager.getOld(); - expect(Pager.loading.show).toHaveBeenCalled(); + + setTimeout(() => { + expect(Pager.loading.show).toHaveBeenCalled(); + + done(); + }); }); - it('hides loader on success', () => { - spyOn($, 'ajax').and.callFake(options => options.success({})); + it('hides loader on success', (done) => { + mockSuccess(); + spyOn(Pager.loading, 'hide'); Pager.getOld(); - expect(Pager.loading.hide).toHaveBeenCalled(); + + setTimeout(() => { + expect(Pager.loading.hide).toHaveBeenCalled(); + + done(); + }); }); - it('hides loader on error', () => { - spyOn($, 'ajax').and.callFake(options => options.error()); + it('hides loader on error', (done) => { + mockError(); + spyOn(Pager.loading, 'hide'); Pager.getOld(); - expect(Pager.loading.hide).toHaveBeenCalled(); + + setTimeout(() => { + expect(Pager.loading.hide).toHaveBeenCalled(); + + done(); + }); }); - it('sends request to url with offset and limit params', () => { - spyOn($, 'ajax'); + it('sends request to url with offset and limit params', (done) => { Pager.offset = 100; Pager.limit = 20; Pager.getOld(); - const [{ data, url }] = $.ajax.calls.argsFor(0); - expect(data).toBe('limit=20&offset=100'); - expect(url).toBe('/some_list'); + + setTimeout(() => { + const [url, params] = $.ajax.calls.argsFor(0); + console.log(url, params); + // expect(data).toBe('limit=20&offset=100'); + // expect(url).toBe('/some_list'); + + done(); + }); }); }); }); -- cgit v1.2.1 From 43f1088f5b086b95a3d5cdd90a33d26bb483cba5 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 1 Feb 2018 11:18:01 +0000 Subject: Converted usage_ping.js to use axios also removed dependancy on jQuery because it is super simple & not required --- .../javascripts/pages/admin/cohorts/usage_ping.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/pages/admin/cohorts/usage_ping.js b/app/assets/javascripts/pages/admin/cohorts/usage_ping.js index 2389056bd02..914a9661c27 100644 --- a/app/assets/javascripts/pages/admin/cohorts/usage_ping.js +++ b/app/assets/javascripts/pages/admin/cohorts/usage_ping.js @@ -1,12 +1,13 @@ +import axios from '../../../lib/utils/axios_utils'; +import { __ } from '../../../locale'; +import flash from '../../../flash'; + export default function UsagePing() { - const usageDataUrl = $('.usage-data').data('endpoint'); + const el = document.querySelector('.usage-data'); - $.ajax({ - type: 'GET', - url: usageDataUrl, - dataType: 'html', - success(html) { - $('.usage-data').html(html); - }, - }); + axios.get(el.dataset.endpoint, { + responseType: 'text', + }).then(({ data }) => { + el.innerHTML = data; + }).catch(() => flash(__('Error fetching usage ping data.'))); } -- cgit v1.2.1 From 4cea24f89c757dafcba190b46d1f5f866acd50f6 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 1 Feb 2018 11:32:31 +0000 Subject: Converted todos.js to axios --- .../pages/dashboard/todos/index/todos.js | 37 +++++++++------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js index e976a3d2f1d..b3f6a72fdcb 100644 --- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js +++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js @@ -2,6 +2,9 @@ import { visitUrl } from '~/lib/utils/url_utility'; import UsersSelect from '~/users_select'; import { isMetaClick } from '~/lib/utils/common_utils'; +import { __ } from '../../../../locale'; +import flash from '../../../../flash'; +import axios from '../../../../lib/utils/axios_utils'; export default class Todos { constructor() { @@ -59,18 +62,12 @@ export default class Todos { const target = e.target; target.setAttribute('disabled', true); target.classList.add('disabled'); - $.ajax({ - type: 'POST', - url: target.dataset.href, - dataType: 'json', - data: { - '_method': target.dataset.method, - }, - success: (data) => { + + axios[target.dataset.method](target.dataset.href) + .then(({ data }) => { this.updateRowState(target); - return this.updateBadges(data); - }, - }); + this.updateBadges(data); + }).catch(() => flash(__('Error updating todo status.'))); } updateRowState(target) { @@ -98,19 +95,15 @@ export default class Todos { e.preventDefault(); const target = e.currentTarget; - const requestData = { '_method': target.dataset.method, ids: this.todo_ids }; target.setAttribute('disabled', true); target.classList.add('disabled'); - $.ajax({ - type: 'POST', - url: target.dataset.href, - dataType: 'json', - data: requestData, - success: (data) => { - this.updateAllState(target, data); - return this.updateBadges(data); - }, - }); + + axios[target.dataset.method](target.dataset.href, { + ids: this.todo_ids, + }).then(({ data }) => { + this.updateAllState(target, data); + this.updateBadges(data); + }).catch(() => flash(__('Error updating status for all todos.'))); } updateAllState(target, data) { -- cgit v1.2.1 From fe2e8dba22471eb39a703672c5d8039c20e94fa6 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 1 Feb 2018 12:26:55 +0000 Subject: fixed infinite loop crashing tests --- spec/javascripts/pager_spec.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/spec/javascripts/pager_spec.js b/spec/javascripts/pager_spec.js index fd9b83e3514..b09494f0b77 100644 --- a/spec/javascripts/pager_spec.js +++ b/spec/javascripts/pager_spec.js @@ -47,12 +47,12 @@ describe('pager', () => { }); describe('getOld', () => { - const urlRegex = /\/some_list(.*)$/; + const urlRegex = /(.*)some_list(.*)$/; let mock; function mockSuccess() { mock.onGet(urlRegex).reply(200, { - count: 20, + count: 0, html: '', }); } @@ -65,9 +65,9 @@ describe('pager', () => { setFixtures('
    '); spyOn(axios, 'get').and.callThrough(); - Pager.init(); - mock = new MockAdapter(axios); + + Pager.init(); }); afterEach(() => { @@ -119,10 +119,15 @@ describe('pager', () => { Pager.getOld(); setTimeout(() => { - const [url, params] = $.ajax.calls.argsFor(0); - console.log(url, params); - // expect(data).toBe('limit=20&offset=100'); - // expect(url).toBe('/some_list'); + const [url, params] = axios.get.calls.argsFor(0); + + expect(params).toEqual({ + params: { + limit: 20, + offset: 100, + }, + }); + expect(url).toBe('/some_list'); done(); }); -- cgit v1.2.1 From a51dea0d43dd9a7995c710558b18da4f2696e284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 1 Feb 2018 13:37:47 +0100 Subject: Improve doc/development/automatic_ce_ee_merge.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- doc/development/automatic_ce_ee_merge.md | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/doc/development/automatic_ce_ee_merge.md b/doc/development/automatic_ce_ee_merge.md index 5a784b6de06..cf6314f9521 100644 --- a/doc/development/automatic_ce_ee_merge.md +++ b/doc/development/automatic_ce_ee_merge.md @@ -5,17 +5,32 @@ Enterprise Edition (look for the [`CE Upstream` merge requests]). This merge is done automatically in a [scheduled pipeline](https://gitlab.com/gitlab-org/release-tools/-/jobs/43201679). -If a merge is already in progress, the job [doesn't create a new one](https://gitlab.com/gitlab-org/release-tools/-/jobs/43157687). -**If you are pinged in a `CE Upstream` merge request to resolve a conflict, -please resolve the conflict as soon as possible or ask someone else to do it!** - ->**Note:** -It's ok to resolve more conflicts than the one that you are asked to resolve. In -that case, it's a good habit to ask for a double-check on your resolution by -someone who is familiar with the code you touched. +## What to do if you are pinged in a `CE Upstream` merge request to resolve a conflict? + +1. Please resolve the conflict as soon as possible or ask someone else to do it + - It's ok to resolve more conflicts than the one that you are asked to resolve. + In that case, it's a good habit to ask for a double-check on your resolution + by someone who is familiar with the code you touched. +1. Once you have resolved your conflicts, push to the branch (no force-push) +1. Assign the merge request to the next person that has to resolve a conflict +1. If all conflicts are resolved after your resolution is pushed, keep the merge + request assigned to you: **you are now responsible for the merge request to be + green** +1. If you need any help, you can ping the current [release managers], or ask in + the `#ce-to-ee` Slack channel + +A few notes about the automatic CE->EE merge job: + +- If a merge is already in progress, the job + [doesn't create a new one](https://gitlab.com/gitlab-org/release-tools/-/jobs/43157687). +- If there is nothing to merge (i.e. EE is up-to-date with CE), the job doesn't + create a new one +- The job posts messages to the `#ce-to-ee` Slack channel to inform what's the + current CE->EE merge status (e.g. "A new MR has been created", "A MR is still pending") [`CE Upstream` merge requests]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests?label_name%5B%5D=CE+upstream +[release managers]: https://about.gitlab.com/release-managers/ ## Always merge EE merge requests before their CE counterparts -- cgit v1.2.1 From 4aedec11922c1794652c342a8cb9767a1ad04756 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 1 Feb 2018 15:44:36 +0100 Subject: Ban Rugged from Repository --- scripts/lint-rugged | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/lint-rugged b/scripts/lint-rugged index 03f780f880b..cabd083e9f9 100755 --- a/scripts/lint-rugged +++ b/scripts/lint-rugged @@ -1,7 +1,7 @@ #!/usr/bin/env ruby ALLOWED = [ - # Can be deleted (?) once rugged is no longer used in production. Doesn't make Rugged calls. + # Can be fixed once Rugged is no longer used in production. Doesn't make Rugged calls. 'config/initializers/8_metrics.rb', # Can be deleted once wiki's are fully (mandatory) migrated @@ -13,9 +13,6 @@ ALLOWED = [ # Needs to be migrated, https://gitlab.com/gitlab-org/gitaly/issues/954 'lib/tasks/gitlab/cleanup.rake', - # https://gitlab.com/gitlab-org/gitaly/issues/961 - 'app/models/repository.rb', - # The only place where Rugged code is still allowed in production 'lib/gitlab/git/' ].freeze -- cgit v1.2.1 From 1123d9dc460353cbc3b46606cc2235f0433f35e1 Mon Sep 17 00:00:00 2001 From: Jose Ivan Vargas Date: Tue, 23 Jan 2018 12:50:58 -0600 Subject: Added more tests and corrected typos --- .../javascripts/pages/projects/show/index.js | 1 - .../javascripts/pages/projects/tree/show/index.js | 35 +++++---- .../commit_pipeline_status_component.vue | 78 ++++++++++++-------- app/assets/stylesheets/pages/commits.scss | 10 ++- app/views/projects/commits/_commit.html.haml | 2 +- features/project/project.feature | 1 - features/steps/shared/project.rb | 1 - .../commit_pipeline_status_component_spec.js | 85 +++++++++++++++------- 8 files changed, 131 insertions(+), 82 deletions(-) diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index 4c42fda16d7..55154cdddcb 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -5,7 +5,6 @@ import TreeView from '~/tree'; import BlobViewer from '~/blob/viewer/index'; import Activities from '~/activities'; import { ajaxGet } from '~/lib/utils/common_utils'; -import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; import Star from '../../../star'; import notificationsDropdown from '../../../notifications_dropdown'; diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js index f14c3f86687..c4b3356e478 100644 --- a/app/assets/javascripts/pages/projects/tree/show/index.js +++ b/app/assets/javascripts/pages/projects/tree/show/index.js @@ -15,24 +15,23 @@ export default () => { ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath)); const commitPipelineStatusEl = document.getElementById('commit-pipeline-status'); - const $statusLink = $('.ci-status-link'); - if ($statusLink.length > 0) { - $statusLink.remove(); + const statusLink = document.querySelector('.commit-actions .ci-status-link'); + if (statusLink != null) { + statusLink.remove(); + // eslint-disable-next-line no-new + new Vue({ + el: commitPipelineStatusEl, + components: { + commitPipelineStatus, + }, + render(createElement) { + return createElement('commit-pipeline-status', { + props: { + endpoint: commitPipelineStatusEl.dataset.endpoint, + }, + }); + }, + }); } - commitPipelineStatusEl.classList.remove('hidden'); - // eslint-disable-next-line no-new - new Vue({ - el: '#commit-pipeline-status', - components: { - commitPipelineStatus, - }, - render(createElement) { - return createElement('commit-pipeline-status', { - props: { - endpoint: commitPipelineStatusEl.dataset.endpoint, - }, - }); - }, - }); }; diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue index e13acf8555a..63f20a0041d 100644 --- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue +++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue @@ -1,8 +1,10 @@ diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index adfd72556b8..17801ed5910 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -196,6 +196,14 @@ @media (min-width: $screen-sm-min) { font-size: 0; + div { + display: inline; + } + + .fa-spinner { + font-size: 12px; + } + span { font-size: 6px; } @@ -226,7 +234,7 @@ .ci-status-icon { position: relative; - top: 3px; + top: 1px; } } diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index c94e10947e6..90272ad9554 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -51,7 +51,7 @@ - if commit.status(ref) = render_commit_status(commit, ref: ref) - #commit-pipeline-status.hidden{ data: { endpoint: pipelines_project_commit_path(project, commit.id) } } + #commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id) } } = link_to commit.short_id, link, class: "commit-sha btn btn-transparent btn-link" = clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard")) = link_to_browse_code(project, commit) diff --git a/features/project/project.feature b/features/project/project.feature index bcd72c5c5a3..23817ef3ac9 100644 --- a/features/project/project.feature +++ b/features/project/project.feature @@ -23,7 +23,6 @@ Feature: Project And I visit project "Shop" page Then I should see project "Shop" README - @javascript Scenario: I should see last commit with CI Given project "Shop" has CI enabled Given project "Shop" has CI build diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index 923d54a6545..affbccccdf9 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -218,7 +218,6 @@ module SharedProject end step 'I should see last commit with CI status' do - sleep 2 page.within ".blob-commit-info" do expect(page).to have_content(project.commit.sha[0..6]) expect(page).to have_link("Commit: skipped") diff --git a/spec/javascripts/commit/commit_pipeline_status_component_spec.js b/spec/javascripts/commit/commit_pipeline_status_component_spec.js index 2a52097e0d5..90f290e845e 100644 --- a/spec/javascripts/commit/commit_pipeline_status_component_spec.js +++ b/spec/javascripts/commit/commit_pipeline_status_component_spec.js @@ -6,7 +6,7 @@ import mountComponent from '../helpers/vue_mount_component_helper'; describe('Commit pipeline status component', () => { let vm; - let component; + let Component; let mock; const mockCiStatus = { details_path: '/root/hello-world/pipelines/1', @@ -19,34 +19,25 @@ describe('Commit pipeline status component', () => { }; beforeEach(() => { - mock = new MockAdapter(axios); - mock.onGet('/dummy/endpoint').reply(() => { - const res = Promise.resolve([200, { - pipelines: [ - { - details: { - stages: [ - { - status: mockCiStatus, - title: 'Commit: canceled', - }, - ], - }, - }, - ], - }]); - return res; - }); - component = Vue.extend(commitPipelineStatus); - }); - - afterEach(() => { - mock.reset(); + Component = Vue.extend(commitPipelineStatus); }); - describe('While polling pipeline data', () => { + describe('While polling pipeline data succesfully', () => { beforeEach(() => { - vm = mountComponent(component, { + mock = new MockAdapter(axios); + mock.onGet('/dummy/endpoint').reply(() => { + const res = Promise.resolve([200, { + pipelines: [ + { + details: { + status: mockCiStatus, + }, + }, + ], + }]); + return res; + }); + vm = mountComponent(Component, { endpoint: '/dummy/endpoint', }); }); @@ -54,18 +45,58 @@ describe('Commit pipeline status component', () => { afterEach(() => { vm.poll.stop(); vm.$destroy(); + mock.restore(); + }); + + it('shows the loading icon when polling is starting', (done) => { + expect(vm.$el.querySelector('.loading-container')).not.toBe(null); + setTimeout(() => { + expect(vm.$el.querySelector('.loading-container')).toBe(null); + done(); + }); }); it('contains a ciStatus when the polling is succesful ', (done) => { setTimeout(() => { expect(vm.ciStatus).toEqual(mockCiStatus); done(); - }, 1000); + }); }); it('contains a ci-status icon when polling is succesful', (done) => { setTimeout(() => { expect(vm.$el.querySelector('.ci-status-icon')).not.toBe(null); + expect(vm.$el.querySelector('.ci-status-icon').classList).toContain(`ci-status-icon-${mockCiStatus.group}`); + done(); + }); + }); + }); + + describe('When polling data was not succesful', () => { + beforeEach(() => { + mock = new MockAdapter(axios); + mock.onGet('/dummy/endpoint').reply(() => { + const res = Promise.reject([502, { }]); + return res; + }); + vm = new Component({ + props: { + endpoint: '/dummy/endpoint', + }, + }); + }); + + afterEach(() => { + vm.poll.stop(); + vm.$destroy(); + mock.restore(); + }); + + it('calls an errorCallback', (done) => { + spyOn(vm, 'errorCallback').and.callThrough(); + vm.$mount(); + setTimeout(() => { + expect(vm.errorCallback.calls.count()).toEqual(1); done(); }); }); -- cgit v1.2.1 From 8128ecaecc038224285d713ca08b136462d6f365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 1 Feb 2018 18:01:47 +0100 Subject: Speed-up the gitlab_git_test job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index be18520b876..b4afa953175 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -738,8 +738,9 @@ cache gems: gitlab_git_test: <<: *dedicated-runner <<: *except-docs-and-qa - <<: *pull-cache variables: SETUP_DB: "false" + before_script: [] + cache: {} script: - spec/support/prepare-gitlab-git-test-for-commit --check-for-changes -- cgit v1.2.1 From 391d1915c8a9e6f723594a6f4930dcd90fe1bc1a Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Thu, 20 Jul 2017 17:54:36 +0700 Subject: Hide pipeline schedule 'take ownership' for current owner --- app/policies/ci/pipeline_schedule_policy.rb | 8 ++++ .../_pipeline_schedule.html.haml | 3 +- ...-user-interface-bugs-for-schedule-pipelines.yml | 5 +++ spec/policies/ci/pipeline_schedule_policy_spec.rb | 14 +++++++ .../_pipeline_schedule.html.haml_spec.rb | 47 ++++++++++++++++++++++ 5 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/35285-user-interface-bugs-for-schedule-pipelines.yml create mode 100644 spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb diff --git a/app/policies/ci/pipeline_schedule_policy.rb b/app/policies/ci/pipeline_schedule_policy.rb index abcf536b2f7..dc7a4aed577 100644 --- a/app/policies/ci/pipeline_schedule_policy.rb +++ b/app/policies/ci/pipeline_schedule_policy.rb @@ -10,6 +10,10 @@ module Ci can?(:developer_access) && pipeline_schedule.owned_by?(@user) end + condition(:non_owner_of_schedule) do + !pipeline_schedule.owned_by?(@user) + end + rule { can?(:developer_access) }.policy do enable :play_pipeline_schedule end @@ -19,6 +23,10 @@ module Ci enable :admin_pipeline_schedule end + rule { can?(:master_access) & non_owner_of_schedule }.policy do + enable :take_ownership_pipeline_schedule + end + rule { protected_ref }.prevent :play_pipeline_schedule end end diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml index 800e234275c..a8692b83b07 100644 --- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml +++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml @@ -29,9 +29,10 @@ - if can?(current_user, :play_pipeline_schedule, pipeline_schedule) = link_to play_pipeline_schedule_path(pipeline_schedule), method: :post, title: s_('Play'), class: 'btn' do = icon('play') - - if can?(current_user, :update_pipeline_schedule, pipeline_schedule) + - if can?(current_user, :take_ownership_pipeline_schedule, pipeline_schedule) = link_to take_ownership_pipeline_schedule_path(pipeline_schedule), method: :post, title: s_('PipelineSchedules|Take ownership'), class: 'btn' do = s_('PipelineSchedules|Take ownership') + - if can?(current_user, :update_pipeline_schedule, pipeline_schedule) = link_to edit_pipeline_schedule_path(pipeline_schedule), title: _('Edit'), class: 'btn' do = icon('pencil') - if can?(current_user, :admin_pipeline_schedule, pipeline_schedule) diff --git a/changelogs/unreleased/35285-user-interface-bugs-for-schedule-pipelines.yml b/changelogs/unreleased/35285-user-interface-bugs-for-schedule-pipelines.yml new file mode 100644 index 00000000000..f3a04469884 --- /dev/null +++ b/changelogs/unreleased/35285-user-interface-bugs-for-schedule-pipelines.yml @@ -0,0 +1,5 @@ +--- +title: Hide pipeline schedule take ownership for current owner +merge_request: 12986 +author: +type: fixed diff --git a/spec/policies/ci/pipeline_schedule_policy_spec.rb b/spec/policies/ci/pipeline_schedule_policy_spec.rb index 1b0e9fac355..c0c3eda4911 100644 --- a/spec/policies/ci/pipeline_schedule_policy_spec.rb +++ b/spec/policies/ci/pipeline_schedule_policy_spec.rb @@ -88,5 +88,19 @@ describe Ci::PipelineSchedulePolicy, :models do expect(policy).to be_allowed :admin_pipeline_schedule end end + + describe 'rules for non-owner of schedule' do + let(:owner) { create(:user) } + + before do + project.add_master(owner) + project.add_master(user) + pipeline_schedule.update(owner: owner) + end + + it 'includes abilities to take ownership' do + expect(policy).to be_allowed :take_ownership_pipeline_schedule + end + end end end diff --git a/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb b/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb new file mode 100644 index 00000000000..6e7d8db99c4 --- /dev/null +++ b/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +describe 'projects/pipeline_schedules/_pipeline_schedule' do + let(:owner) { create(:user) } + let(:master) { create(:user) } + let(:project) { create(:project) } + let(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly, project: project) } + + before do + assign(:project, project) + + allow(view).to receive(:current_user).and_return(user) + allow(view).to receive(:pipeline_schedule).and_return(pipeline_schedule) + + allow(view).to receive(:can?).and_return(true) + end + + context 'taking ownership of schedule' do + context 'when non-owner is signed in' do + let(:user) { master } + + before do + allow(view).to receive(:can?).with(master, :take_ownership_pipeline_schedule, pipeline_schedule).and_return(true) + end + + it 'non-owner can take ownership of pipeline' do + render + + expect(rendered).to have_link('Take ownership') + end + end + + context 'when owner is signed in' do + let(:user) { owner } + + before do + allow(view).to receive(:can?).with(owner, :take_ownership_pipeline_schedule, pipeline_schedule).and_return(false) + end + + it 'owner cannot take ownership of pipeline' do + render + + expect(rendered).not_to have_link('Take ownership') + end + end + end +end -- cgit v1.2.1 From f072af2ea48b192ee479f1caca394b84a2c9402c Mon Sep 17 00:00:00 2001 From: Jose Ivan Vargas Date: Tue, 30 Jan 2018 16:52:36 -0600 Subject: Add confirmation input component --- .../vue_shared/components/confirmation_input.vue | 62 +++++++++++++++++++++ .../add-confirmation-input-for-modals.yml | 5 ++ .../components/confirmation_input_spec.js | 63 ++++++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 app/assets/javascripts/vue_shared/components/confirmation_input.vue create mode 100644 changelogs/unreleased/add-confirmation-input-for-modals.yml create mode 100644 spec/javascripts/vue_shared/components/confirmation_input_spec.js diff --git a/app/assets/javascripts/vue_shared/components/confirmation_input.vue b/app/assets/javascripts/vue_shared/components/confirmation_input.vue new file mode 100644 index 00000000000..1aa03ea6317 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/confirmation_input.vue @@ -0,0 +1,62 @@ + + + diff --git a/changelogs/unreleased/add-confirmation-input-for-modals.yml b/changelogs/unreleased/add-confirmation-input-for-modals.yml new file mode 100644 index 00000000000..ff1027bc55a --- /dev/null +++ b/changelogs/unreleased/add-confirmation-input-for-modals.yml @@ -0,0 +1,5 @@ +--- +title: Add confirmation-input component +merge_request: 16816 +author: +type: other diff --git a/spec/javascripts/vue_shared/components/confirmation_input_spec.js b/spec/javascripts/vue_shared/components/confirmation_input_spec.js new file mode 100644 index 00000000000..a6a12614e77 --- /dev/null +++ b/spec/javascripts/vue_shared/components/confirmation_input_spec.js @@ -0,0 +1,63 @@ +import Vue from 'vue'; +import confirmationInput from '~/vue_shared/components/confirmation_input.vue'; +import mountComponent from '../../helpers/vue_mount_component_helper'; + +describe('Confirmation input component', () => { + const Component = Vue.extend(confirmationInput); + const props = { + inputId: 'dummy-id', + confirmationKey: 'confirmation-key', + confirmationValue: 'confirmation-value', + }; + let vm; + + afterEach(() => { + vm.$destroy(); + }); + + describe('props', () => { + beforeEach(() => { + vm = mountComponent(Component, props); + }); + + it('sets id of the input field to inputId', () => { + expect(vm.$refs.enteredValue.id).toBe(props.inputId); + }); + + it('sets name of the input field to confirmationKey', () => { + expect(vm.$refs.enteredValue.name).toBe(props.confirmationKey); + }); + }); + + describe('computed', () => { + describe('inputLabel', () => { + it('escapes confirmationValue by default', () => { + vm = mountComponent(Component, { ...props, confirmationValue: 'nds escap"ng' }); + expect(vm.inputLabel).toBe('Type n<e></e>ds escap"ng to confirm:'); + }); + + it('does not escape confirmationValue if escapeValue is false', () => { + vm = mountComponent(Component, { ...props, confirmationValue: 'nds escap"ng', shouldEscapeConfirmationValue: false }); + expect(vm.inputLabel).toBe('Type nds escap"ng to confirm:'); + }); + }); + }); + + describe('methods', () => { + describe('hasCorrectValue', () => { + beforeEach(() => { + vm = mountComponent(Component, props); + }); + + it('returns false if entered value is incorrect', () => { + vm.$refs.enteredValue.value = 'incorrect'; + expect(vm.hasCorrectValue()).toBe(false); + }); + + it('returns true if entered value is correct', () => { + vm.$refs.enteredValue.value = props.confirmationValue; + expect(vm.hasCorrectValue()).toBe(true); + }); + }); + }); +}); -- cgit v1.2.1 From ece547f4f9523279ab5940dc53499b9b03bfeceb Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Thu, 1 Feb 2018 19:49:07 +0000 Subject: Docs: update GitLab products description --- doc/README.md | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/doc/README.md b/doc/README.md index 330670587f5..c8b6b4f32b8 100644 --- a/doc/README.md +++ b/doc/README.md @@ -8,23 +8,13 @@ comments: false Welcome to [GitLab](https://about.gitlab.com/), a Git-based fully featured platform for software development! -GitLab offers the most scalable Git-based fully integrated platform for software development, with flexible products and subscription plans: +GitLab offers the most scalable Git-based fully integrated platform for software development, with flexible products and subscription plans. -- **GitLab Community Edition (CE)** is an [open source product](https://gitlab.com/gitlab-org/gitlab-ce/), -self-hosted, free to use. Every feature available in GitLab CE is also available on GitLab Enterprise Edition (Starter and Premium) and GitLab.com. -- **GitLab Enterprise Edition (EE)** is an [open-core product](https://gitlab.com/gitlab-org/gitlab-ee/), -self-hosted, fully featured solution of GitLab, available under distinct [subscriptions](https://about.gitlab.com/products/): **GitLab Enterprise Edition Starter (EES)**, **GitLab Enterprise Edition Premium (EEP)**, and **GitLab Enterprise Edition Ultimate (EEU)**. -- **GitLab.com**: SaaS GitLab solution, with [free and paid subscriptions](https://about.gitlab.com/gitlab-com/). GitLab.com is hosted by GitLab, Inc., and administrated by GitLab (users don't have access to admin settings). +With GitLab self-hosted, you deploy your own GitLab instance on-premises or on a private cloud of your choice. GitLab self-hosted is available for [free and with paid subscriptions](https://about.gitlab.com/products/): Libre, Starter, Premium, and Ultimate. -> **GitLab EE** contains all features available in **GitLab CE**, -plus premium features available in each version: **Enterprise Edition Starter** -(**EES**), **Enterprise Edition Premium** (**EEP**), and **Enterprise Edition Ultimate** -(**EEU**). Everything available in **EES** is also available in **EEP**. Every feature -available in **EEP** is also available in **EEU**. +GitLab.com is our SaaS offering. It's hosted, managed, and administered by GitLab, with [free and paid plans](https://about.gitlab.com/gitlab-com/) for individuals and teams: Free, Bronze, Silver, and Gold. ----- - -Shortcuts to GitLab's most visited docs: +## Shortcuts to GitLab's most visited docs | [GitLab CI/CD](ci/README.md) | Other | | :----- | :----- | @@ -134,14 +124,8 @@ Manage your [repositories](user/project/repository/index.md) from the UI (user i ## Administrator documentation -[Administration documentation](administration/index.md) applies to admin users of GitLab -self-hosted instances: - -- GitLab Community Edition -- GitLab [Enterprise Editions](https://about.gitlab.com/gitlab-ee/) - - Enterprise Edition Starter (EES) - - Enterprise Edition Premium (EEP) - - Enterprise Edition Ultimate (EEU) +[Administration documentation](administration/index.md) applies to admin users of [GitLab +self-hosted instances](#self-hosted-gitlab): Libre, Starter, Premium, Ultimate. Learn how to install, configure, update, upgrade, integrate, and maintain your own instance. Regular users don't have access to GitLab administration tools and settings. -- cgit v1.2.1