Commit 8a7aaf86 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 96781283
...@@ -251,9 +251,10 @@ class GfmAutoComplete { ...@@ -251,9 +251,10 @@ class GfmAutoComplete {
}); });
// Cache assignees list for easier filtering later // Cache assignees list for easier filtering later
assignees = SidebarMediator.singleton?.store?.assignees?.map( assignees =
SidebarMediator.singleton?.store?.assignees?.map(
assignee => `${assignee.username} ${assignee.name}`, assignee => `${assignee.username} ${assignee.name}`,
); ) || [];
const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers); const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers);
return match && match.length ? match[1] : null; return match && match.length ? match[1] : null;
......
...@@ -48,6 +48,7 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -48,6 +48,7 @@ document.addEventListener('DOMContentLoaded', () => {
leaveByUrl('project'); leaveByUrl('project');
if (document.getElementById('js-tree-list')) { if (document.getElementById('js-tree-list')) {
initBlob();
import('ee_else_ce/repository') import('ee_else_ce/repository')
.then(m => m.default()) .then(m => m.default())
.catch(e => { .catch(e => {
......
<script> <script>
import _ from 'underscore'; import { isString, isEmpty } from 'lodash';
import { GlTooltipDirective, GlLink } from '@gitlab/ui'; import { GlTooltipDirective, GlLink } from '@gitlab/ui';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
...@@ -56,7 +56,7 @@ export default { ...@@ -56,7 +56,7 @@ export default {
required: false, required: false,
default: undefined, default: undefined,
validator: ref => 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)),
}, },
/** /**
......
<script> <script>
import _ from 'underscore'; import { throttle } from 'lodash';
import { numberToHumanSize } from '../../../../lib/utils/number_utils'; import { numberToHumanSize } from '../../../../lib/utils/number_utils';
export default { export default {
...@@ -48,7 +48,7 @@ export default { ...@@ -48,7 +48,7 @@ export default {
mounted() { mounted() {
// The onImgLoad may have happened before the control was actually mounted // The onImgLoad may have happened before the control was actually mounted
this.onImgLoad(); this.onImgLoad();
this.resizeThrottled = _.throttle(this.onImgLoad, 400); this.resizeThrottled = throttle(this.onImgLoad, 400);
window.addEventListener('resize', this.resizeThrottled, false); window.addEventListener('resize', this.resizeThrottled, false);
}, },
methods: { methods: {
......
<script> <script>
import _ from 'underscore'; import { throttle } from 'lodash';
import { pixeliseValue } from '../../../lib/utils/dom_utils'; import { pixeliseValue } from '../../../lib/utils/dom_utils';
import ImageViewer from '../../../content_viewer/viewers/image_viewer.vue'; import ImageViewer from '../../../content_viewer/viewers/image_viewer.vue';
...@@ -98,7 +98,7 @@ export default { ...@@ -98,7 +98,7 @@ export default {
this.swipeOldImgInfo = imgInfo; this.swipeOldImgInfo = imgInfo;
this.prepareSwipe(); this.prepareSwipe();
}, },
resize: _.throttle(function throttledResize() { resize: throttle(function throttledResize() {
this.swipeBarPos = 0; this.swipeBarPos = 0;
this.swipeWrapWidth = 0; this.swipeWrapWidth = 0;
this.prepareSwipe(); this.prepareSwipe();
......
<script> <script>
import { GlLink } from '@gitlab/ui'; import { GlLink } from '@gitlab/ui';
import _ from 'underscore'; import { escape as esc } from 'lodash';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import icon from '../../../vue_shared/components/icon.vue'; import icon from '../../../vue_shared/components/icon.vue';
function buildDocsLinkStart(path) { 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 { export default {
......
<script> <script>
import $ from 'jquery'; import $ from 'jquery';
import '~/behaviors/markdown/render_gfm'; import '~/behaviors/markdown/render_gfm';
import _ from 'underscore'; import { unescape as unesc } from 'lodash';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import { stripHtml } from '~/lib/utils/text_utility'; import { stripHtml } from '~/lib/utils/text_utility';
import Flash from '../../../flash'; import Flash from '../../../flash';
...@@ -115,7 +115,7 @@ export default { ...@@ -115,7 +115,7 @@ export default {
return text; return text;
} }
return _.unescape(stripHtml(richText).replace(/\n/g, '')); return unesc(stripHtml(richText).replace(/\n/g, ''));
} }
return ''; return '';
......
<script> <script>
import { GlButton } from '@gitlab/ui'; import { GlButton } from '@gitlab/ui';
import _ from 'underscore'; import { isString } from 'lodash';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import ProjectAvatar from '~/vue_shared/components/project_avatar/default.vue'; import ProjectAvatar from '~/vue_shared/components/project_avatar/default.vue';
import highlight from '~/lib/utils/highlight'; import highlight from '~/lib/utils/highlight';
...@@ -17,7 +17,7 @@ export default { ...@@ -17,7 +17,7 @@ export default {
project: { project: {
type: Object, type: Object,
required: true, 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: { selected: {
type: Boolean, type: Boolean,
......
<script> <script>
import _ from 'underscore'; import { debounce } from 'lodash';
import { GlLoadingIcon, GlSearchBoxByType, GlInfiniteScroll } from '@gitlab/ui'; import { GlLoadingIcon, GlSearchBoxByType, GlInfiniteScroll } from '@gitlab/ui';
import ProjectListItem from './project_list_item.vue'; import ProjectListItem from './project_list_item.vue';
...@@ -61,9 +61,9 @@ export default { ...@@ -61,9 +61,9 @@ export default {
this.$emit('bottomReached'); this.$emit('bottomReached');
}, },
isSelected(project) { 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); this.$emit('searched', this.searchQuery);
}, SEARCH_INPUT_TIMEOUT_MS), }, SEARCH_INPUT_TIMEOUT_MS),
}, },
......
<script> <script>
import _ from 'underscore'; import { isString } from 'lodash';
import { GlDropdown, GlDropdownDivider, GlDropdownItem } from '@gitlab/ui'; import { GlDropdown, GlDropdownDivider, GlDropdownItem } from '@gitlab/ui';
const isValidItem = item => const isValidItem = item =>
_.isString(item.eventName) && _.isString(item.title) && _.isString(item.description); isString(item.eventName) && isString(item.title) && isString(item.description);
export default { export default {
components: { components: {
......
import _ from 'underscore'; import { isEmpty } from 'lodash';
import { sprintf, __ } from '~/locale'; import { sprintf, __ } from '~/locale';
import { formatDate } from '~/lib/utils/datetime_utility'; import { formatDate } from '~/lib/utils/datetime_utility';
import tooltip from '~/vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
...@@ -130,7 +130,7 @@ const mixins = { ...@@ -130,7 +130,7 @@ const mixins = {
return this.assignees.length > 0; return this.assignees.length > 0;
}, },
hasMilestone() { hasMilestone() {
return !_.isEmpty(this.milestone); return !isEmpty(this.milestone);
}, },
iconName() { iconName() {
if (this.isMergeRequest && this.isMerged) { if (this.isMergeRequest && this.isMerged) {
......
...@@ -50,6 +50,7 @@ module Snippets ...@@ -50,6 +50,7 @@ module Snippets
snippet_saved snippet_saved
rescue => e # Rescuing all because we can receive Creation exceptions, GRPC exceptions, Git exceptions, ... rescue => e # Rescuing all because we can receive Creation exceptions, GRPC exceptions, Git exceptions, ...
snippet.errors.add(:base, e.message) snippet.errors.add(:base, e.message)
log_error(e.message)
# If the commit action failed we need to remove the repository if exists # If the commit action failed we need to remove the repository if exists
snippet.repository.remove if snippet.repository_exists? snippet.repository.remove if snippet.repository_exists?
......
...@@ -51,8 +51,9 @@ module Snippets ...@@ -51,8 +51,9 @@ module Snippets
# the changes # the changes
create_commit(snippet) if snippet.repository_exists? create_commit(snippet) if snippet.repository_exists?
end end
rescue rescue => e
snippet.errors.add(:repository, 'Error updating the snippet') snippet.errors.add(:repository, 'Error updating the snippet')
log_error(e.message)
false false
end end
......
---
title: Fix not working File upload from Project overview page.
merge_request: 26828
author: Gilang Gumilar
type: fixed
---
title: Fix assignee dropdown on new issue page
merge_request: 26971
author:
type: fixed
---
title: Fix issue/MR state not being preserved when importing a project using Project
Import/Export
merge_request: 27816
author:
type: fixed
---
title: Replace underscore with lodash for ./app/assets/javascripts/vue_shared
merge_request: 25108
author: Tobias Spagert
type: other
...@@ -189,6 +189,38 @@ to explicitly add `-DskipTests` to your options. ...@@ -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 If you still need to run tests during `mvn install`, add `-DskipTests=false` to
`MAVEN_CLI_OPTS`. `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 ### 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. > - [Introduced](https://gitlab.com/gitlab-org/security-products/license-management/-/merge_requests/36) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0.
......
...@@ -340,6 +340,7 @@ methods: ...@@ -340,6 +340,7 @@ methods:
- :diff_head_sha - :diff_head_sha
- :source_branch_sha - :source_branch_sha
- :target_branch_sha - :target_branch_sha
- :state
events: events:
- :action - :action
push_event_payload: push_event_payload:
...@@ -350,6 +351,8 @@ methods: ...@@ -350,6 +351,8 @@ methods:
- :list_type - :list_type
ci_pipelines: ci_pipelines:
- :notes - :notes
issues:
- :state
preloads: preloads:
statuses: statuses:
......
...@@ -16433,6 +16433,9 @@ msgstr "" ...@@ -16433,6 +16433,9 @@ msgstr ""
msgid "Release|Something went wrong while saving the release details" msgid "Release|Something went wrong while saving the release details"
msgstr "" msgstr ""
msgid "Remediated: needs review"
msgstr ""
msgid "Remember me" msgid "Remember me"
msgstr "" msgstr ""
...@@ -19961,6 +19964,9 @@ 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." 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 "" 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." msgid "There are no GPG keys associated with this account."
msgstr "" msgstr ""
...@@ -22364,6 +22370,9 @@ msgstr "" ...@@ -22364,6 +22370,9 @@ msgstr ""
msgid "Vulnerability List" msgid "Vulnerability List"
msgstr "" msgstr ""
msgid "Vulnerability remediated. Review before resolving."
msgstr ""
msgid "Vulnerability-Check" msgid "Vulnerability-Check"
msgstr "" msgstr ""
......
...@@ -284,16 +284,17 @@ describe 'GFM autocomplete', :js do ...@@ -284,16 +284,17 @@ describe 'GFM autocomplete', :js do
context 'assignees' do context 'assignees' do
let(:issue_assignee) { create(:issue, project: project) } let(:issue_assignee) { create(:issue, project: project) }
let(:unassigned_user) { create(:user) }
before do before do
issue_assignee.update(assignees: [user]) issue_assignee.update(assignees: [user])
visit project_issue_path(project, issue_assignee) project.add_maintainer(unassigned_user)
wait_for_requests
end end
it 'lists users who are currently not assigned to the issue when using /assign' do 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') note = find('#note-body')
page.within '.timeline-content-form' do page.within '.timeline-content-form' do
note.native.send_keys('/as') note.native.send_keys('/as')
...@@ -305,6 +306,19 @@ describe 'GFM autocomplete', :js do ...@@ -305,6 +306,19 @@ describe 'GFM autocomplete', :js do
wait_for_requests 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')).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
end end
......
...@@ -5,103 +5,32 @@ require 'spec_helper' ...@@ -5,103 +5,32 @@ require 'spec_helper'
describe 'Projects > Files > User uploads files' do describe 'Projects > Files > User uploads files' do
include DropzoneHelper 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(:user) { create(:user) }
let(:project) { create(:project, :repository, name: 'Shop', creator: user) } let(:project) { create(:project, :repository, name: 'Shop', creator: user) }
let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } 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 before do
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
end end
context 'when an user has write access' do context 'when a user has write access' do
before 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))
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 end
wait_for_all_requests include_examples 'it uploads and commit a new text file'
visit(project_blob_path(project, 'new_branch_name/logo_sample.svg')) include_examples 'it uploads and commit a new image file'
expect(page).to have_css('.file-content img')
end
end end
context 'when an user does not have write access' do context 'when a user does not have write access' do
before do before do
project2.add_reporter(user) 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) visit(project_tree_path(project2))
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 end
click_button('Upload file') include_examples 'it uploads and commit a new file to a forked project'
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 end
end end
# 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
...@@ -359,31 +359,16 @@ describe('common_utils', () => { ...@@ -359,31 +359,16 @@ describe('common_utils', () => {
}); });
describe('parseBoolean', () => { describe('parseBoolean', () => {
const { parseBoolean } = commonUtils; it.each`
input | expected
it('returns true for "true"', () => { ${'true'} | ${true}
expect(parseBoolean('true')).toEqual(true); ${'false'} | ${false}
}); ${'something'} | ${false}
${null} | ${false}
it('returns false for "false"', () => { ${true} | ${true}
expect(parseBoolean('false')).toEqual(false); ${false} | ${false}
}); `('returns $expected for $input', ({ input, expected }) => {
expect(commonUtils.parseBoolean(input)).toBe(expected);
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);
});
}); });
}); });
......
import Vue from 'vue'; 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'; import deleteAccountModal from '~/profile/account/components/delete_account_modal.vue';
describe('DeleteAccountModal component', () => { describe('DeleteAccountModal component', () => {
const actionUrl = `${gl.TEST_HOST}/delete/user`; const actionUrl = `${TEST_HOST}/delete/user`;
const username = 'hasnoname'; const username = 'hasnoname';
let Component; let Component;
let vm; let vm;
...@@ -43,7 +44,7 @@ describe('DeleteAccountModal component', () => { ...@@ -43,7 +44,7 @@ describe('DeleteAccountModal component', () => {
it('does not accept empty password', done => { it('does not accept empty password', done => {
const { form, input, submitButton } = findElements(); const { form, input, submitButton } = findElements();
spyOn(form, 'submit'); jest.spyOn(form, 'submit').mockImplementation(() => {});
input.value = ''; input.value = '';
input.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
...@@ -61,7 +62,7 @@ describe('DeleteAccountModal component', () => { ...@@ -61,7 +62,7 @@ describe('DeleteAccountModal component', () => {
it('submits form with password', done => { it('submits form with password', done => {
const { form, input, submitButton } = findElements(); const { form, input, submitButton } = findElements();
spyOn(form, 'submit'); jest.spyOn(form, 'submit').mockImplementation(() => {});
input.value = 'anything'; input.value = 'anything';
input.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
...@@ -95,7 +96,7 @@ describe('DeleteAccountModal component', () => { ...@@ -95,7 +96,7 @@ describe('DeleteAccountModal component', () => {
it('does not accept wrong username', done => { it('does not accept wrong username', done => {
const { form, input, submitButton } = findElements(); const { form, input, submitButton } = findElements();
spyOn(form, 'submit'); jest.spyOn(form, 'submit').mockImplementation(() => {});
input.value = 'this is wrong'; input.value = 'this is wrong';
input.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
...@@ -113,7 +114,7 @@ describe('DeleteAccountModal component', () => { ...@@ -113,7 +114,7 @@ describe('DeleteAccountModal component', () => {
it('submits form with correct username', done => { it('submits form with correct username', done => {
const { form, input, submitButton } = findElements(); const { form, input, submitButton } = findElements();
spyOn(form, 'submit'); jest.spyOn(form, 'submit').mockImplementation(() => {});
input.value = username; input.value = username;
input.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
......
import Vue from 'vue'; import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter'; 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 axios from '~/lib/utils/axios_utils';
import updateUsername from '~/profile/account/components/update_username.vue'; import updateUsername from '~/profile/account/components/update_username.vue';
describe('UpdateUsername component', () => { describe('UpdateUsername component', () => {
const rootUrl = gl.TEST_HOST; const rootUrl = TEST_HOST;
const actionUrl = `${gl.TEST_HOST}/update/username`; const actionUrl = `${TEST_HOST}/update/username`;
const username = 'hasnoname'; const username = 'hasnoname';
const newUsername = 'new_username'; const newUsername = 'new_username';
let Component; let Component;
...@@ -106,7 +107,7 @@ describe('UpdateUsername component', () => { ...@@ -106,7 +107,7 @@ describe('UpdateUsername component', () => {
const { confirmModalBtn } = findElements(); const { confirmModalBtn } = findElements();
axiosMock.onPut(actionUrl).replyOnce(() => [200, { message: 'Username changed' }]); axiosMock.onPut(actionUrl).replyOnce(() => [200, { message: 'Username changed' }]);
spyOn(axios, 'put').and.callThrough(); jest.spyOn(axios, 'put');
vm.newUsername = newUsername; vm.newUsername = newUsername;
......
...@@ -22,6 +22,7 @@ describe('ProjectSelector component', () => { ...@@ -22,6 +22,7 @@ describe('ProjectSelector component', () => {
beforeEach(() => { beforeEach(() => {
jasmine.clock().install(); jasmine.clock().install();
jasmine.clock().mockDate();
wrapper = mount(Vue.extend(ProjectSelector), { wrapper = mount(Vue.extend(ProjectSelector), {
localVue, localVue,
......
...@@ -35,7 +35,7 @@ describe Gitlab::Asciidoc::IncludeProcessor do ...@@ -35,7 +35,7 @@ describe Gitlab::Asciidoc::IncludeProcessor do
expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_truthy expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_truthy
end 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') } (max_includes - 1).times { processor.send(:read_blob, ref, 'a.adoc') }
expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_truthy expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_truthy
......
...@@ -199,6 +199,12 @@ describe Snippets::CreateService do ...@@ -199,6 +199,12 @@ describe Snippets::CreateService do
expect(SnippetRepository.count).to be_zero expect(SnippetRepository.count).to be_zero
end end
it 'logs the error' do
expect(Gitlab::AppLogger).to receive(:error).with('foobar')
subject
end
it 'returns the error' do it 'returns the error' do
response = subject response = subject
......
...@@ -167,14 +167,24 @@ describe Snippets::UpdateService do ...@@ -167,14 +167,24 @@ describe Snippets::UpdateService do
expect(blob.data).to eq(options[:content]) expect(blob.data).to eq(options[:content])
end end
it 'returns error when the commit action fails' do context 'when an error is raised' do
allow(snippet.snippet_repository).to receive(:multi_files_action).and_raise(SnippetRepository::CommitError) before do
allow(snippet.snippet_repository).to receive(:multi_files_action).and_raise(SnippetRepository::CommitError, 'foobar')
end
it 'logs the error' do
expect(Gitlab::AppLogger).to receive(:error).with('foobar')
subject
end
it 'returns error with generic error message' do
response = subject response = subject
expect(response).to be_error expect(response).to be_error
expect(response.payload[:snippet].errors.full_messages).to eq ['Repository Error updating the snippet'] expect(response.payload[:snippet].errors.full_messages).to eq ['Repository Error updating the snippet']
end end
end
it 'returns error if snippet does not have a snippet_repository' do it 'returns error if snippet does not have a snippet_repository' do
allow(snippet).to receive(:snippet_repository).and_return(nil) allow(snippet).to receive(:snippet_repository).and_return(nil)
......
# 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment