diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-23 21:09:46 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-23 21:09:46 +0000 |
commit | 8a7aaf86831d2a556578ae558a4fcab8bb659b20 (patch) | |
tree | 61c2b55aa48ff8e853e546cd3009dfc5423279c8 | |
parent | 967812838c7e7742729a4c7aeb9859f98a509622 (diff) | |
download | gitlab-ce-8a7aaf86831d2a556578ae558a4fcab8bb659b20.tar.gz |
Add latest changes from gitlab-org/gitlab@master
31 files changed, 282 insertions, 147 deletions
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index fa2e3f94f87..b6deedfa5e4 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -251,9 +251,10 @@ class GfmAutoComplete { }); // Cache assignees list for easier filtering later - assignees = SidebarMediator.singleton?.store?.assignees?.map( - assignee => `${assignee.username} ${assignee.name}`, - ); + assignees = + SidebarMediator.singleton?.store?.assignees?.map( + assignee => `${assignee.username} ${assignee.name}`, + ) || []; const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers); return match && match.length ? match[1] : null; diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index 370f3c6e7a2..6ae10c98058 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -48,6 +48,7 @@ document.addEventListener('DOMContentLoaded', () => { leaveByUrl('project'); if (document.getElementById('js-tree-list')) { + initBlob(); import('ee_else_ce/repository') .then(m => m.default()) .catch(e => { diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue index f7c508c4e23..356f733fb8c 100644 --- a/app/assets/javascripts/vue_shared/components/commit.vue +++ b/app/assets/javascripts/vue_shared/components/commit.vue @@ -1,5 +1,5 @@ <script> -import _ from 'underscore'; +import { isString, isEmpty } from 'lodash'; import { GlTooltipDirective, GlLink } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; @@ -56,7 +56,7 @@ export default { required: false, default: undefined, validator: ref => - _.isUndefined(ref) || (_.isFinite(ref.iid) && _.isString(ref.path) && !_.isEmpty(ref.path)), + ref === undefined || (Number.isFinite(ref.iid) && isString(ref.path) && !isEmpty(ref.path)), }, /** diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue index 9f98943f6b4..328c7e3fd32 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue @@ -1,5 +1,5 @@ <script> -import _ from 'underscore'; +import { throttle } from 'lodash'; import { numberToHumanSize } from '../../../../lib/utils/number_utils'; export default { @@ -48,7 +48,7 @@ export default { mounted() { // The onImgLoad may have happened before the control was actually mounted this.onImgLoad(); - this.resizeThrottled = _.throttle(this.onImgLoad, 400); + this.resizeThrottled = throttle(this.onImgLoad, 400); window.addEventListener('resize', this.resizeThrottled, false); }, methods: { diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue index 8d77b156aa4..2f2618d448f 100644 --- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue @@ -1,5 +1,5 @@ <script> -import _ from 'underscore'; +import { throttle } from 'lodash'; import { pixeliseValue } from '../../../lib/utils/dom_utils'; import ImageViewer from '../../../content_viewer/viewers/image_viewer.vue'; @@ -98,7 +98,7 @@ export default { this.swipeOldImgInfo = imgInfo; this.prepareSwipe(); }, - resize: _.throttle(function throttledResize() { + resize: throttle(function throttledResize() { this.swipeBarPos = 0; this.swipeWrapWidth = 0; this.prepareSwipe(); diff --git a/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue b/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue index b5d3f3685bc..89a8595fc79 100644 --- a/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue +++ b/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue @@ -1,11 +1,11 @@ <script> import { GlLink } from '@gitlab/ui'; -import _ from 'underscore'; +import { escape as esc } from 'lodash'; import { __, sprintf } from '~/locale'; import icon from '../../../vue_shared/components/icon.vue'; function buildDocsLinkStart(path) { - return `<a href="${_.escape(path)}" target="_blank" rel="noopener noreferrer">`; + return `<a href="${esc(path)}" target="_blank" rel="noopener noreferrer">`; } export default { diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index e30876813c2..26e878d56a0 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -1,7 +1,7 @@ <script> import $ from 'jquery'; import '~/behaviors/markdown/render_gfm'; -import _ from 'underscore'; +import { unescape as unesc } from 'lodash'; import { __, sprintf } from '~/locale'; import { stripHtml } from '~/lib/utils/text_utility'; import Flash from '../../../flash'; @@ -115,7 +115,7 @@ export default { return text; } - return _.unescape(stripHtml(richText).replace(/\n/g, '')); + return unesc(stripHtml(richText).replace(/\n/g, '')); } return ''; diff --git a/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue b/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue index c472e54efda..a51b2a3ab6d 100644 --- a/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue +++ b/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue @@ -1,6 +1,6 @@ <script> import { GlButton } from '@gitlab/ui'; -import _ from 'underscore'; +import { isString } from 'lodash'; import Icon from '~/vue_shared/components/icon.vue'; import ProjectAvatar from '~/vue_shared/components/project_avatar/default.vue'; import highlight from '~/lib/utils/highlight'; @@ -17,7 +17,7 @@ export default { project: { type: Object, required: true, - validator: p => _.isFinite(p.id) && _.isString(p.name) && _.isString(p.name_with_namespace), + validator: p => Number.isFinite(p.id) && isString(p.name) && isString(p.name_with_namespace), }, selected: { type: Boolean, diff --git a/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue b/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue index f984a0a6203..30a9633b6dc 100644 --- a/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue +++ b/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue @@ -1,5 +1,5 @@ <script> -import _ from 'underscore'; +import { debounce } from 'lodash'; import { GlLoadingIcon, GlSearchBoxByType, GlInfiniteScroll } from '@gitlab/ui'; import ProjectListItem from './project_list_item.vue'; @@ -61,9 +61,9 @@ export default { this.$emit('bottomReached'); }, isSelected(project) { - return Boolean(_.find(this.selectedProjects, { id: project.id })); + return this.selectedProjects.some(({ id }) => project.id === id); }, - onInput: _.debounce(function debouncedOnInput() { + onInput: debounce(function debouncedOnInput() { this.$emit('searched', this.searchQuery); }, SEARCH_INPUT_TIMEOUT_MS), }, diff --git a/app/assets/javascripts/vue_shared/components/split_button.vue b/app/assets/javascripts/vue_shared/components/split_button.vue index f02b412e8a1..b11ec8b8838 100644 --- a/app/assets/javascripts/vue_shared/components/split_button.vue +++ b/app/assets/javascripts/vue_shared/components/split_button.vue @@ -1,10 +1,9 @@ <script> -import _ from 'underscore'; - +import { isString } from 'lodash'; import { GlDropdown, GlDropdownDivider, GlDropdownItem } from '@gitlab/ui'; const isValidItem = item => - _.isString(item.eventName) && _.isString(item.title) && _.isString(item.description); + isString(item.eventName) && isString(item.title) && isString(item.description); export default { components: { diff --git a/app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js b/app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js index fbebd7c7945..4fad34d22d8 100644 --- a/app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js +++ b/app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js @@ -1,4 +1,4 @@ -import _ from 'underscore'; +import { isEmpty } from 'lodash'; import { sprintf, __ } from '~/locale'; import { formatDate } from '~/lib/utils/datetime_utility'; import tooltip from '~/vue_shared/directives/tooltip'; @@ -130,7 +130,7 @@ const mixins = { return this.assignees.length > 0; }, hasMilestone() { - return !_.isEmpty(this.milestone); + return !isEmpty(this.milestone); }, iconName() { if (this.isMergeRequest && this.isMerged) { diff --git a/app/services/snippets/create_service.rb b/app/services/snippets/create_service.rb index 9178b929656..389d4819c68 100644 --- a/app/services/snippets/create_service.rb +++ b/app/services/snippets/create_service.rb @@ -50,6 +50,7 @@ module Snippets snippet_saved rescue => e # Rescuing all because we can receive Creation exceptions, GRPC exceptions, Git exceptions, ... snippet.errors.add(:base, e.message) + log_error(e.message) # If the commit action failed we need to remove the repository if exists snippet.repository.remove if snippet.repository_exists? diff --git a/app/services/snippets/update_service.rb b/app/services/snippets/update_service.rb index 874357f36cc..76ce8231410 100644 --- a/app/services/snippets/update_service.rb +++ b/app/services/snippets/update_service.rb @@ -51,8 +51,9 @@ module Snippets # the changes create_commit(snippet) if snippet.repository_exists? end - rescue + rescue => e snippet.errors.add(:repository, 'Error updating the snippet') + log_error(e.message) false end diff --git a/changelogs/unreleased/209059-fix-project-show-file-upload-not-working.yml b/changelogs/unreleased/209059-fix-project-show-file-upload-not-working.yml new file mode 100644 index 00000000000..92675668950 --- /dev/null +++ b/changelogs/unreleased/209059-fix-project-show-file-upload-not-working.yml @@ -0,0 +1,5 @@ +--- +title: Fix not working File upload from Project overview page. +merge_request: 26828 +author: Gilang Gumilar +type: fixed diff --git a/changelogs/unreleased/210005-fix-assignee-quickaction-on-new-issue.yml b/changelogs/unreleased/210005-fix-assignee-quickaction-on-new-issue.yml new file mode 100644 index 00000000000..b12d701e95a --- /dev/null +++ b/changelogs/unreleased/210005-fix-assignee-quickaction-on-new-issue.yml @@ -0,0 +1,5 @@ +--- +title: Fix assignee dropdown on new issue page +merge_request: 26971 +author: +type: fixed diff --git a/changelogs/unreleased/georgekoltsov-fix-issues-mrs-state.yml b/changelogs/unreleased/georgekoltsov-fix-issues-mrs-state.yml new file mode 100644 index 00000000000..018a14121e4 --- /dev/null +++ b/changelogs/unreleased/georgekoltsov-fix-issues-mrs-state.yml @@ -0,0 +1,6 @@ +--- +title: Fix issue/MR state not being preserved when importing a project using Project + Import/Export +merge_request: 27816 +author: +type: fixed diff --git a/changelogs/unreleased/resolve_gitlab_issue_196641.yml b/changelogs/unreleased/resolve_gitlab_issue_196641.yml new file mode 100644 index 00000000000..fc770b2582c --- /dev/null +++ b/changelogs/unreleased/resolve_gitlab_issue_196641.yml @@ -0,0 +1,5 @@ +--- +title: Replace underscore with lodash for ./app/assets/javascripts/vue_shared +merge_request: 25108 +author: Tobias Spagert +type: other diff --git a/doc/user/compliance/license_compliance/index.md b/doc/user/compliance/license_compliance/index.md index dd2bb75b1df..77314faea29 100644 --- a/doc/user/compliance/license_compliance/index.md +++ b/doc/user/compliance/license_compliance/index.md @@ -189,6 +189,38 @@ to explicitly add `-DskipTests` to your options. If you still need to run tests during `mvn install`, add `-DskipTests=false` to `MAVEN_CLI_OPTS`. +#### Using private Maven repos + +If you have a private Maven repository that requires login credentials, you can use the +`MAVEN_CLI_OPTS` variable to specify a custom [`settings.xml`](http://maven.apache.org/settings.html) +file. + +For example, you may have a settings file like this in your project source: + +```xml +<settings> + <servers> + <server> + <id>my-server</id> + <username>${private.username}</username> + <username>${private.password}</username> + </server> + </servers> +</settings> +``` + +You can use this file through the following declaration in your `gitlab-ci.yml` file: + +```yaml +license_scanning: + variables: + MAVEN_CLI_OPTS: --settings settings.xml -Dprivate.username=foo -Dprivate.password=bar +``` + +NOTE: **Note:** +If you don't want to expose the credentials in your `.gitlab-ci.yml` file, then +you can [set the variable in your project's settings](../../../ci/variables/README.md#via-the-ui). + ### Selecting the version of Python > - [Introduced](https://gitlab.com/gitlab-org/security-products/license-management/-/merge_requests/36) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0. diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml index aa6085de4f9..56f7ffbe4f8 100644 --- a/lib/gitlab/import_export/project/import_export.yml +++ b/lib/gitlab/import_export/project/import_export.yml @@ -340,6 +340,7 @@ methods: - :diff_head_sha - :source_branch_sha - :target_branch_sha + - :state events: - :action push_event_payload: @@ -350,6 +351,8 @@ methods: - :list_type ci_pipelines: - :notes + issues: + - :state preloads: statuses: diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 8e96296730e..1293c610d38 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -16433,6 +16433,9 @@ msgstr "" msgid "Release|Something went wrong while saving the release details" msgstr "" +msgid "Remediated: needs review" +msgstr "" + msgid "Remember me" msgstr "" @@ -19961,6 +19964,9 @@ msgstr "" msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6." msgstr "" +msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status." +msgstr "" + msgid "There are no GPG keys associated with this account." msgstr "" @@ -22364,6 +22370,9 @@ msgstr "" msgid "Vulnerability List" msgstr "" +msgid "Vulnerability remediated. Review before resolving." +msgstr "" + msgid "Vulnerability-Check" msgstr "" diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb index b7ec445a7aa..501a2d347d1 100644 --- a/spec/features/issues/gfm_autocomplete_spec.rb +++ b/spec/features/issues/gfm_autocomplete_spec.rb @@ -284,16 +284,17 @@ describe 'GFM autocomplete', :js do context 'assignees' do let(:issue_assignee) { create(:issue, project: project) } + let(:unassigned_user) { create(:user) } before do issue_assignee.update(assignees: [user]) - visit project_issue_path(project, issue_assignee) - - wait_for_requests + project.add_maintainer(unassigned_user) end it 'lists users who are currently not assigned to the issue when using /assign' do + visit project_issue_path(project, issue_assignee) + note = find('#note-body') page.within '.timeline-content-form' do note.native.send_keys('/as') @@ -305,6 +306,19 @@ describe 'GFM autocomplete', :js do wait_for_requests expect(find('#at-view-users .atwho-view-ul')).not_to have_content(user.username) + expect(find('#at-view-users .atwho-view-ul')).to have_content(unassigned_user.username) + end + + it 'shows dropdown on new issue form' do + visit new_project_issue_path(project) + + textarea = find('#issue_description') + textarea.native.send_keys('/ass') + find('.atwho-view li', text: '/assign') + textarea.native.send_keys(:tab) + + expect(find('#at-view-users .atwho-view-ul')).to have_content(unassigned_user.username) + expect(find('#at-view-users .atwho-view-ul')).to have_content(user.username) end end diff --git a/spec/features/projects/files/user_uploads_files_spec.rb b/spec/features/projects/files/user_uploads_files_spec.rb index 35a3835ff12..8a20c1387a3 100644 --- a/spec/features/projects/files/user_uploads_files_spec.rb +++ b/spec/features/projects/files/user_uploads_files_spec.rb @@ -5,103 +5,32 @@ require 'spec_helper' describe 'Projects > Files > User uploads files' do include DropzoneHelper - let(:fork_message) do - "You're not allowed to make changes to this project directly. "\ - "A fork of this project has been created that you can make changes in, so you can submit a merge request." - end let(:user) { create(:user) } let(:project) { create(:project, :repository, name: 'Shop', creator: user) } let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } - let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } - let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } before do project.add_maintainer(user) sign_in(user) end - context 'when an user has write access' do + context 'when a user has write access' do before do - visit(project_tree_path_root_ref) + visit(project_tree_path(project)) end - it 'uploads and commit a new text file', :js do - find('.add-to-tree').click - click_link('Upload file') - drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt')) - - page.within('#modal-upload-blob') do - fill_in(:commit_message, with: 'New commit message') - end - - fill_in(:branch_name, with: 'new_branch_name', visible: true) - click_button('Upload file') - - expect(page).to have_content('New commit message') - expect(current_path).to eq(project_new_merge_request_path(project)) + include_examples 'it uploads and commit a new text file' - click_link('Changes') - find("a[data-action='diffs']", text: 'Changes').click - - wait_for_requests - - expect(page).to have_content('Lorem ipsum dolor sit amet') - expect(page).to have_content('Sed ut perspiciatis unde omnis') - end - - it 'uploads and commit a new image file', :js do - find('.add-to-tree').click - click_link('Upload file') - drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'logo_sample.svg')) - - page.within('#modal-upload-blob') do - fill_in(:commit_message, with: 'New commit message') - fill_in(:branch_name, with: 'new_branch_name', visible: true) - click_button('Upload file') - end - - wait_for_all_requests - - visit(project_blob_path(project, 'new_branch_name/logo_sample.svg')) - - expect(page).to have_css('.file-content img') - end + include_examples 'it uploads and commit a new image file' end - context 'when an user does not have write access' do + context 'when a user does not have write access' do before do project2.add_reporter(user) - visit(project2_tree_path_root_ref) - end - - it 'uploads and commit a new file to a forked project', :js, :sidekiq_might_not_need_inline do - find('.add-to-tree').click - click_link('Upload file') - - expect(page).to have_content(fork_message) - - find('.add-to-tree').click - click_link('Upload file') - drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt')) - - page.within('#modal-upload-blob') do - fill_in(:commit_message, with: 'New commit message') - end - - click_button('Upload file') - expect(page).to have_content('New commit message') - - fork = user.fork_of(project2.reload) - - expect(current_path).to eq(project_new_merge_request_path(fork)) - - find("a[data-action='diffs']", text: 'Changes').click - - wait_for_requests - - expect(page).to have_content('Lorem ipsum dolor sit amet') - expect(page).to have_content('Sed ut perspiciatis unde omnis') + visit(project_tree_path(project2)) end + + include_examples 'it uploads and commit a new file to a forked project' end end diff --git a/spec/features/projects/show/user_uploads_files_spec.rb b/spec/features/projects/show/user_uploads_files_spec.rb new file mode 100644 index 00000000000..e279cdf92da --- /dev/null +++ b/spec/features/projects/show/user_uploads_files_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Projects > Show > User uploads files' do + include DropzoneHelper + + let(:user) { create(:user) } + let(:project) { create(:project, :repository, name: 'Shop', creator: user) } + let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } + + before do + project.add_maintainer(user) + sign_in(user) + end + + context 'when a user has write access' do + before do + visit(project_path(project)) + end + + include_examples 'it uploads and commit a new text file' + + include_examples 'it uploads and commit a new image file' + end + + context 'when a user does not have write access' do + before do + project2.add_reporter(user) + + visit(project_path(project2)) + end + + include_examples 'it uploads and commit a new file to a forked project' + end +end diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js index 6ba8f58086a..1edfda30fec 100644 --- a/spec/frontend/lib/utils/common_utils_spec.js +++ b/spec/frontend/lib/utils/common_utils_spec.js @@ -359,31 +359,16 @@ describe('common_utils', () => { }); describe('parseBoolean', () => { - const { parseBoolean } = commonUtils; - - it('returns true for "true"', () => { - expect(parseBoolean('true')).toEqual(true); - }); - - it('returns false for "false"', () => { - expect(parseBoolean('false')).toEqual(false); - }); - - it('returns false for "something"', () => { - expect(parseBoolean('something')).toEqual(false); - }); - - it('returns false for null', () => { - expect(parseBoolean(null)).toEqual(false); - }); - - it('is idempotent', () => { - const input = ['true', 'false', 'something', null]; - input.forEach(value => { - const result = parseBoolean(value); - - expect(parseBoolean(result)).toBe(result); - }); + it.each` + input | expected + ${'true'} | ${true} + ${'false'} | ${false} + ${'something'} | ${false} + ${null} | ${false} + ${true} | ${true} + ${false} | ${false} + `('returns $expected for $input', ({ input, expected }) => { + expect(commonUtils.parseBoolean(input)).toBe(expected); }); }); diff --git a/spec/javascripts/profile/account/components/delete_account_modal_spec.js b/spec/frontend/profile/account/components/delete_account_modal_spec.js index e2c557d79a9..4da82152818 100644 --- a/spec/javascripts/profile/account/components/delete_account_modal_spec.js +++ b/spec/frontend/profile/account/components/delete_account_modal_spec.js @@ -1,10 +1,11 @@ import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import { TEST_HOST } from 'helpers/test_constants'; +import mountComponent from 'helpers/vue_mount_component_helper'; import deleteAccountModal from '~/profile/account/components/delete_account_modal.vue'; describe('DeleteAccountModal component', () => { - const actionUrl = `${gl.TEST_HOST}/delete/user`; + const actionUrl = `${TEST_HOST}/delete/user`; const username = 'hasnoname'; let Component; let vm; @@ -43,7 +44,7 @@ describe('DeleteAccountModal component', () => { it('does not accept empty password', done => { const { form, input, submitButton } = findElements(); - spyOn(form, 'submit'); + jest.spyOn(form, 'submit').mockImplementation(() => {}); input.value = ''; input.dispatchEvent(new Event('input')); @@ -61,7 +62,7 @@ describe('DeleteAccountModal component', () => { it('submits form with password', done => { const { form, input, submitButton } = findElements(); - spyOn(form, 'submit'); + jest.spyOn(form, 'submit').mockImplementation(() => {}); input.value = 'anything'; input.dispatchEvent(new Event('input')); @@ -95,7 +96,7 @@ describe('DeleteAccountModal component', () => { it('does not accept wrong username', done => { const { form, input, submitButton } = findElements(); - spyOn(form, 'submit'); + jest.spyOn(form, 'submit').mockImplementation(() => {}); input.value = 'this is wrong'; input.dispatchEvent(new Event('input')); @@ -113,7 +114,7 @@ describe('DeleteAccountModal component', () => { it('submits form with correct username', done => { const { form, input, submitButton } = findElements(); - spyOn(form, 'submit'); + jest.spyOn(form, 'submit').mockImplementation(() => {}); input.value = username; input.dispatchEvent(new Event('input')); diff --git a/spec/javascripts/profile/account/components/update_username_spec.js b/spec/frontend/profile/account/components/update_username_spec.js index 902e00b85fd..be39a7f4d80 100644 --- a/spec/javascripts/profile/account/components/update_username_spec.js +++ b/spec/frontend/profile/account/components/update_username_spec.js @@ -1,13 +1,14 @@ import Vue from 'vue'; import MockAdapter from 'axios-mock-adapter'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import { TEST_HOST } from 'helpers/test_constants'; +import mountComponent from 'helpers/vue_mount_component_helper'; import axios from '~/lib/utils/axios_utils'; import updateUsername from '~/profile/account/components/update_username.vue'; describe('UpdateUsername component', () => { - const rootUrl = gl.TEST_HOST; - const actionUrl = `${gl.TEST_HOST}/update/username`; + const rootUrl = TEST_HOST; + const actionUrl = `${TEST_HOST}/update/username`; const username = 'hasnoname'; const newUsername = 'new_username'; let Component; @@ -106,7 +107,7 @@ describe('UpdateUsername component', () => { const { confirmModalBtn } = findElements(); axiosMock.onPut(actionUrl).replyOnce(() => [200, { message: 'Username changed' }]); - spyOn(axios, 'put').and.callThrough(); + jest.spyOn(axios, 'put'); vm.newUsername = newUsername; diff --git a/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js b/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js index 178df54b465..5d995f06abb 100644 --- a/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js +++ b/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js @@ -22,6 +22,7 @@ describe('ProjectSelector component', () => { beforeEach(() => { jasmine.clock().install(); + jasmine.clock().mockDate(); wrapper = mount(Vue.extend(ProjectSelector), { localVue, diff --git a/spec/lib/gitlab/asciidoc/include_processor_spec.rb b/spec/lib/gitlab/asciidoc/include_processor_spec.rb index 72fa05939ae..2781319567c 100644 --- a/spec/lib/gitlab/asciidoc/include_processor_spec.rb +++ b/spec/lib/gitlab/asciidoc/include_processor_spec.rb @@ -35,7 +35,7 @@ describe Gitlab::Asciidoc::IncludeProcessor do expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_truthy end - it 'allows the Nth + 1 include' do + it 'allows the Nth include' do (max_includes - 1).times { processor.send(:read_blob, ref, 'a.adoc') } expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_truthy diff --git a/spec/services/snippets/create_service_spec.rb b/spec/services/snippets/create_service_spec.rb index 26c80ee05b3..4cf574ed567 100644 --- a/spec/services/snippets/create_service_spec.rb +++ b/spec/services/snippets/create_service_spec.rb @@ -199,6 +199,12 @@ describe Snippets::CreateService do expect(SnippetRepository.count).to be_zero end + it 'logs the error' do + expect(Gitlab::AppLogger).to receive(:error).with('foobar') + + subject + end + it 'returns the error' do response = subject diff --git a/spec/services/snippets/update_service_spec.rb b/spec/services/snippets/update_service_spec.rb index 3605d3f76da..03dc857c666 100644 --- a/spec/services/snippets/update_service_spec.rb +++ b/spec/services/snippets/update_service_spec.rb @@ -167,13 +167,23 @@ describe Snippets::UpdateService do expect(blob.data).to eq(options[:content]) end - it 'returns error when the commit action fails' do - allow(snippet.snippet_repository).to receive(:multi_files_action).and_raise(SnippetRepository::CommitError) + context 'when an error is raised' do + before do + allow(snippet.snippet_repository).to receive(:multi_files_action).and_raise(SnippetRepository::CommitError, 'foobar') + end - response = subject + it 'logs the error' do + expect(Gitlab::AppLogger).to receive(:error).with('foobar') - expect(response).to be_error - expect(response.payload[:snippet].errors.full_messages).to eq ['Repository Error updating the snippet'] + subject + end + + it 'returns error with generic error message' do + response = subject + + expect(response).to be_error + expect(response.payload[:snippet].errors.full_messages).to eq ['Repository Error updating the snippet'] + end end it 'returns error if snippet does not have a snippet_repository' do diff --git a/spec/support/shared_examples/features/project_upload_files_shared_examples.rb b/spec/support/shared_examples/features/project_upload_files_shared_examples.rb new file mode 100644 index 00000000000..25203fa3182 --- /dev/null +++ b/spec/support/shared_examples/features/project_upload_files_shared_examples.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'it uploads and commit a new text file' do + it 'uploads and commit a new text file', :js do + find('.add-to-tree').click + click_link('Upload file') + drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt')) + + page.within('#modal-upload-blob') do + fill_in(:commit_message, with: 'New commit message') + end + + fill_in(:branch_name, with: 'upload_text', visible: true) + click_button('Upload file') + + expect(page).to have_content('New commit message') + expect(current_path).to eq(project_new_merge_request_path(project)) + + click_link('Changes') + find("a[data-action='diffs']", text: 'Changes').click + + wait_for_requests + + expect(page).to have_content('Lorem ipsum dolor sit amet') + expect(page).to have_content('Sed ut perspiciatis unde omnis') + end +end + +RSpec.shared_examples 'it uploads and commit a new image file' do + it 'uploads and commit a new image file', :js do + find('.add-to-tree').click + click_link('Upload file') + drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'logo_sample.svg')) + + page.within('#modal-upload-blob') do + fill_in(:commit_message, with: 'New commit message') + fill_in(:branch_name, with: 'upload_image', visible: true) + click_button('Upload file') + end + + wait_for_all_requests + + visit(project_blob_path(project, 'upload_image/logo_sample.svg')) + + expect(page).to have_css('.file-content img') + end +end + +RSpec.shared_examples 'it uploads and commit a new file to a forked project' do + let(:fork_message) do + "You're not allowed to make changes to this project directly. "\ + "A fork of this project has been created that you can make changes in, so you can submit a merge request." + end + + it 'uploads and commit a new file to a forked project', :js, :sidekiq_might_not_need_inline do + find('.add-to-tree').click + click_link('Upload file') + + expect(page).to have_content(fork_message) + + find('.add-to-tree').click + click_link('Upload file') + drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt')) + + page.within('#modal-upload-blob') do + fill_in(:commit_message, with: 'New commit message') + end + + click_button('Upload file') + + expect(page).to have_content('New commit message') + + fork = user.fork_of(project2.reload) + + expect(current_path).to eq(project_new_merge_request_path(fork)) + + find("a[data-action='diffs']", text: 'Changes').click + + wait_for_requests + + expect(page).to have_content('Lorem ipsum dolor sit amet') + expect(page).to have_content('Sed ut perspiciatis unde omnis') + end +end |