summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-23 21:09:46 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-23 21:09:46 +0000
commit8a7aaf86831d2a556578ae558a4fcab8bb659b20 (patch)
tree61c2b55aa48ff8e853e546cd3009dfc5423279c8
parent967812838c7e7742729a4c7aeb9859f98a509622 (diff)
downloadgitlab-ce-8a7aaf86831d2a556578ae558a4fcab8bb659b20.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js7
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js1
-rw-r--r--app/assets/javascripts/vue_shared/components/commit.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/issue/issue_warning.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/split_button.vue5
-rw-r--r--app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js4
-rw-r--r--app/services/snippets/create_service.rb1
-rw-r--r--app/services/snippets/update_service.rb3
-rw-r--r--changelogs/unreleased/209059-fix-project-show-file-upload-not-working.yml5
-rw-r--r--changelogs/unreleased/210005-fix-assignee-quickaction-on-new-issue.yml5
-rw-r--r--changelogs/unreleased/georgekoltsov-fix-issues-mrs-state.yml6
-rw-r--r--changelogs/unreleased/resolve_gitlab_issue_196641.yml5
-rw-r--r--doc/user/compliance/license_compliance/index.md32
-rw-r--r--lib/gitlab/import_export/project/import_export.yml3
-rw-r--r--locale/gitlab.pot9
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb20
-rw-r--r--spec/features/projects/files/user_uploads_files_spec.rb87
-rw-r--r--spec/features/projects/show/user_uploads_files_spec.rb36
-rw-r--r--spec/frontend/lib/utils/common_utils_spec.js35
-rw-r--r--spec/frontend/profile/account/components/delete_account_modal_spec.js (renamed from spec/javascripts/profile/account/components/delete_account_modal_spec.js)13
-rw-r--r--spec/frontend/profile/account/components/update_username_spec.js (renamed from spec/javascripts/profile/account/components/update_username_spec.js)9
-rw-r--r--spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js1
-rw-r--r--spec/lib/gitlab/asciidoc/include_processor_spec.rb2
-rw-r--r--spec/services/snippets/create_service_spec.rb6
-rw-r--r--spec/services/snippets/update_service_spec.rb20
-rw-r--r--spec/support/shared_examples/features/project_upload_files_shared_examples.rb84
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