diff options
Diffstat (limited to 'spec')
25 files changed, 497 insertions, 229 deletions
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index e3213d24f6a..362d167befa 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -601,10 +601,10 @@ describe 'Issues', feature: true do expect(page.find_field("issue_description").value).to have_content 'banana_sample' end - it 'adds double newline to end of attachment markdown' do + it "doesn't add double newline to end of a single attachment markdown" do dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif') - expect(page.find_field("issue_description").value).to match /\n\n$/ + expect(page.find_field("issue_description").value).not_to match /\n\n$/ end end diff --git a/spec/features/merge_requests/diff_notes_resolve_spec.rb b/spec/features/merge_requests/diff_notes_resolve_spec.rb index 88d28b649a4..0e23c3a8849 100644 --- a/spec/features/merge_requests/diff_notes_resolve_spec.rb +++ b/spec/features/merge_requests/diff_notes_resolve_spec.rb @@ -198,6 +198,8 @@ feature 'Diff notes resolve', feature: true, js: true do it 'does not mark discussion as resolved when resolving single note' do page.first '.diff-content .note' do first('.line-resolve-btn').click + + expect(page).to have_selector('.note-action-button .loading') expect(first('.line-resolve-btn')['data-original-title']).to eq("Resolved by #{user.name}") end diff --git a/spec/fixtures/trace/ansi-sequence-and-unicode b/spec/fixtures/trace/ansi-sequence-and-unicode new file mode 100644 index 00000000000..5d2466f0d0f --- /dev/null +++ b/spec/fixtures/trace/ansi-sequence-and-unicode @@ -0,0 +1,5 @@ +[0m[01;34m.[0m +[30;42m..[0m +😺 +ヾ(´༎ຶД༎ຶ`)ノ +[01;32m許功蓋[0m diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 40efab6e4f7..a7fc5d14859 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -265,4 +265,27 @@ describe ProjectsHelper do end end end + + describe "#visibility_select_options" do + let(:project) { create(:project, :repository) } + let(:user) { create(:user) } + + before do + allow(helper).to receive(:current_user).and_return(user) + + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + end + + it "does not include the Public restricted level" do + expect(helper.send(:visibility_select_options, project, Gitlab::VisibilityLevel::PRIVATE)).not_to include('Public') + end + + it "includes the Internal level" do + expect(helper.send(:visibility_select_options, project, Gitlab::VisibilityLevel::PRIVATE)).to include('Internal') + end + + it "includes the Private level" do + expect(helper.send(:visibility_select_options, project, Gitlab::VisibilityLevel::PRIVATE)).to include('Private') + end + end end diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js index ea7753c7a1d..68ad5f66676 100644 --- a/spec/javascripts/awards_handler_spec.js +++ b/spec/javascripts/awards_handler_spec.js @@ -3,6 +3,8 @@ import Cookies from 'js-cookie'; import AwardsHandler from '~/awards_handler'; +require('~/lib/utils/common_utils'); + (function() { var awardsHandler, lazyAssert, urlRoot, openAndWaitForEmojiMenu; @@ -28,7 +30,7 @@ import AwardsHandler from '~/awards_handler'; loadFixtures('issues/issue_with_comment.html.raw'); awardsHandler = new AwardsHandler; spyOn(awardsHandler, 'postEmoji').and.callFake((function(_this) { - return function(url, emoji, cb) { + return function(button, url, emoji, cb) { return cb(); }; })(this)); @@ -63,7 +65,7 @@ import AwardsHandler from '~/awards_handler'; $emojiMenu = $('.emoji-menu'); expect($emojiMenu.length).toBe(1); expect($emojiMenu.hasClass('is-visible')).toBe(true); - expect($emojiMenu.find('#emoji_search').length).toBe(1); + expect($emojiMenu.find('.js-emoji-menu-search').length).toBe(1); return expect($('.js-awards-block.current').length).toBe(1); }); }); @@ -115,6 +117,27 @@ import AwardsHandler from '~/awards_handler'; return expect($emojiButton.next('.js-counter').text()).toBe('4'); }); }); + describe('::userAuthored', function() { + it('should update tooltip to user authored title', function() { + var $thumbsUpEmoji, $votesBlock; + $votesBlock = $('.js-awards-block').eq(0); + $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); + $thumbsUpEmoji.attr('data-title', 'sam'); + awardsHandler.userAuthored($thumbsUpEmoji); + return expect($thumbsUpEmoji.data("original-title")).toBe("You cannot vote on your own issue, MR and note"); + }); + it('should restore tooltip back to initial vote list', function() { + var $thumbsUpEmoji, $votesBlock; + jasmine.clock().install(); + $votesBlock = $('.js-awards-block').eq(0); + $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); + $thumbsUpEmoji.attr('data-title', 'sam'); + awardsHandler.userAuthored($thumbsUpEmoji); + jasmine.clock().tick(2801); + jasmine.clock().uninstall(); + return expect($thumbsUpEmoji.data("original-title")).toBe("sam"); + }); + }); describe('::getAwardUrl', function() { return it('returns the url for request', function() { return expect(awardsHandler.getAwardUrl()).toBe('http://test.host/frontend-fixtures/issues-project/issues/1/toggle_award_emoji'); @@ -194,16 +217,35 @@ import AwardsHandler from '~/awards_handler'; return expect($thumbsUpEmoji.data("original-title")).toBe('sam'); }); }); - describe('search', function() { - return it('should filter the emoji', function(done) { + describe('::searchEmojis', () => { + it('should filter the emoji', function(done) { return openAndWaitForEmojiMenu() .then(() => { expect($('[data-name=angel]').is(':visible')).toBe(true); expect($('[data-name=anger]').is(':visible')).toBe(true); - $('#emoji_search').val('ali').trigger('input'); + awardsHandler.searchEmojis('ali'); expect($('[data-name=angel]').is(':visible')).toBe(false); expect($('[data-name=anger]').is(':visible')).toBe(false); expect($('[data-name=alien]').is(':visible')).toBe(true); + expect($('.js-emoji-menu-search').val()).toBe('ali'); + }) + .then(done) + .catch((err) => { + done.fail(`Failed to open and build emoji menu: ${err.message}`); + }); + }); + it('should clear the search when searching for nothing', function(done) { + return openAndWaitForEmojiMenu() + .then(() => { + awardsHandler.searchEmojis('ali'); + expect($('[data-name=angel]').is(':visible')).toBe(false); + expect($('[data-name=anger]').is(':visible')).toBe(false); + expect($('[data-name=alien]').is(':visible')).toBe(true); + awardsHandler.searchEmojis(''); + expect($('[data-name=angel]').is(':visible')).toBe(true); + expect($('[data-name=anger]').is(':visible')).toBe(true); + expect($('[data-name=alien]').is(':visible')).toBe(true); + expect($('.js-emoji-menu-search').val()).toBe(''); }) .then(done) .catch((err) => { @@ -211,6 +253,7 @@ import AwardsHandler from '~/awards_handler'; }); }); }); + describe('emoji menu', function() { const emojiSelector = '[data-name="sunglasses"]'; const openEmojiMenuAndAddEmoji = function() { diff --git a/spec/javascripts/fixtures/merge_requests.rb b/spec/javascripts/fixtures/merge_requests.rb index fddeaaf504d..47d904b865b 100644 --- a/spec/javascripts/fixtures/merge_requests.rb +++ b/spec/javascripts/fixtures/merge_requests.rb @@ -7,6 +7,7 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} let(:project) { create(:project, namespace: namespace, path: 'merge-requests-project') } let(:merge_request) { create(:merge_request, :with_diffs, source_project: project, target_project: project, description: '- [ ] Task List Item') } + let(:merged_merge_request) { create(:merge_request, :merged, source_project: project, target_project: project) } let(:pipeline) do create( :ci_pipeline, @@ -32,6 +33,12 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont render_merge_request(example.description, merge_request) end + it 'merge_requests/merged_merge_request.html.raw' do |example| + allow_any_instance_of(MergeRequest).to receive(:source_branch_exists?).and_return(true) + allow_any_instance_of(MergeRequest).to receive(:can_remove_source_branch?).and_return(true) + render_merge_request(example.description, merged_merge_request) + end + private def render_merge_request(fixture_file_name, merge_request) diff --git a/spec/javascripts/issue_show/issue_title_spec.js b/spec/javascripts/issue_show/issue_title_spec.js index 806d728a874..03edbf9f947 100644 --- a/spec/javascripts/issue_show/issue_title_spec.js +++ b/spec/javascripts/issue_show/issue_title_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import issueTitle from '~/issue_show/issue_title'; +import issueTitle from '~/issue_show/issue_title.vue'; describe('Issue Title', () => { let IssueTitleComponent; diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js index 4200e943121..daef9b93fa5 100644 --- a/spec/javascripts/lib/utils/text_utility_spec.js +++ b/spec/javascripts/lib/utils/text_utility_spec.js @@ -1,110 +1,108 @@ require('~/lib/utils/text_utility'); -(() => { - describe('text_utility', () => { - describe('gl.text.getTextWidth', () => { - it('returns zero width when no text is passed', () => { - expect(gl.text.getTextWidth('')).toBe(0); - }); +describe('text_utility', () => { + describe('gl.text.getTextWidth', () => { + it('returns zero width when no text is passed', () => { + expect(gl.text.getTextWidth('')).toBe(0); + }); - it('returns zero width when no text is passed and font is passed', () => { - expect(gl.text.getTextWidth('', '100px sans-serif')).toBe(0); - }); + it('returns zero width when no text is passed and font is passed', () => { + expect(gl.text.getTextWidth('', '100px sans-serif')).toBe(0); + }); - it('returns width when text is passed', () => { - expect(gl.text.getTextWidth('foo') > 0).toBe(true); - }); + it('returns width when text is passed', () => { + expect(gl.text.getTextWidth('foo') > 0).toBe(true); + }); - it('returns bigger width when font is larger', () => { - const largeFont = gl.text.getTextWidth('foo', '100px sans-serif'); - const regular = gl.text.getTextWidth('foo', '10px sans-serif'); - expect(largeFont > regular).toBe(true); - }); + it('returns bigger width when font is larger', () => { + const largeFont = gl.text.getTextWidth('foo', '100px sans-serif'); + const regular = gl.text.getTextWidth('foo', '10px sans-serif'); + expect(largeFont > regular).toBe(true); }); + }); - describe('gl.text.pluralize', () => { - it('returns pluralized', () => { - expect(gl.text.pluralize('test', 2)).toBe('tests'); - }); + describe('gl.text.pluralize', () => { + it('returns pluralized', () => { + expect(gl.text.pluralize('test', 2)).toBe('tests'); + }); - it('returns pluralized when count is 0', () => { - expect(gl.text.pluralize('test', 0)).toBe('tests'); - }); + it('returns pluralized when count is 0', () => { + expect(gl.text.pluralize('test', 0)).toBe('tests'); + }); - it('does not return pluralized', () => { - expect(gl.text.pluralize('test', 1)).toBe('test'); - }); + it('does not return pluralized', () => { + expect(gl.text.pluralize('test', 1)).toBe('test'); }); + }); - describe('gl.text.highCountTrim', () => { - it('returns 99+ for count >= 100', () => { - expect(gl.text.highCountTrim(105)).toBe('99+'); - expect(gl.text.highCountTrim(100)).toBe('99+'); - }); + describe('gl.text.highCountTrim', () => { + it('returns 99+ for count >= 100', () => { + expect(gl.text.highCountTrim(105)).toBe('99+'); + expect(gl.text.highCountTrim(100)).toBe('99+'); + }); - it('returns exact number for count < 100', () => { - expect(gl.text.highCountTrim(45)).toBe(45); - }); + it('returns exact number for count < 100', () => { + expect(gl.text.highCountTrim(45)).toBe(45); }); + }); - describe('gl.text.insertText', () => { - let textArea; + describe('gl.text.insertText', () => { + let textArea; - beforeAll(() => { - textArea = document.createElement('textarea'); - document.querySelector('body').appendChild(textArea); - }); + beforeAll(() => { + textArea = document.createElement('textarea'); + document.querySelector('body').appendChild(textArea); + }); - afterAll(() => { - textArea.parentNode.removeChild(textArea); - }); + afterAll(() => { + textArea.parentNode.removeChild(textArea); + }); - describe('without selection', () => { - it('inserts the tag on an empty line', () => { - const initialValue = ''; + describe('without selection', () => { + it('inserts the tag on an empty line', () => { + const initialValue = ''; - textArea.value = initialValue; - textArea.selectionStart = 0; - textArea.selectionEnd = 0; + textArea.value = initialValue; + textArea.selectionStart = 0; + textArea.selectionEnd = 0; - gl.text.insertText(textArea, textArea.value, '*', null, '', false); + gl.text.insertText(textArea, textArea.value, '*', null, '', false); - expect(textArea.value).toEqual(`${initialValue}* `); - }); + expect(textArea.value).toEqual(`${initialValue}* `); + }); - it('inserts the tag on a new line if the current one is not empty', () => { - const initialValue = 'some text'; + it('inserts the tag on a new line if the current one is not empty', () => { + const initialValue = 'some text'; - textArea.value = initialValue; - textArea.setSelectionRange(initialValue.length, initialValue.length); + textArea.value = initialValue; + textArea.setSelectionRange(initialValue.length, initialValue.length); - gl.text.insertText(textArea, textArea.value, '*', null, '', false); + gl.text.insertText(textArea, textArea.value, '*', null, '', false); - expect(textArea.value).toEqual(`${initialValue}\n* `); - }); + expect(textArea.value).toEqual(`${initialValue}\n* `); + }); - it('inserts the tag on the same line if the current line only contains spaces', () => { - const initialValue = ' '; + it('inserts the tag on the same line if the current line only contains spaces', () => { + const initialValue = ' '; - textArea.value = initialValue; - textArea.setSelectionRange(initialValue.length, initialValue.length); + textArea.value = initialValue; + textArea.setSelectionRange(initialValue.length, initialValue.length); - gl.text.insertText(textArea, textArea.value, '*', null, '', false); + gl.text.insertText(textArea, textArea.value, '*', null, '', false); - expect(textArea.value).toEqual(`${initialValue}* `); - }); + expect(textArea.value).toEqual(`${initialValue}* `); + }); - it('inserts the tag on the same line if the current line only contains tabs', () => { - const initialValue = '\t\t\t'; + it('inserts the tag on the same line if the current line only contains tabs', () => { + const initialValue = '\t\t\t'; - textArea.value = initialValue; - textArea.setSelectionRange(initialValue.length, initialValue.length); + textArea.value = initialValue; + textArea.setSelectionRange(initialValue.length, initialValue.length); - gl.text.insertText(textArea, textArea.value, '*', null, '', false); + gl.text.insertText(textArea, textArea.value, '*', null, '', false); - expect(textArea.value).toEqual(`${initialValue}* `); - }); + expect(textArea.value).toEqual(`${initialValue}* `); }); }); }); -})(); +}); diff --git a/spec/javascripts/merged_buttons_spec.js b/spec/javascripts/merged_buttons_spec.js new file mode 100644 index 00000000000..b5c5e60dd97 --- /dev/null +++ b/spec/javascripts/merged_buttons_spec.js @@ -0,0 +1,44 @@ +/* global MergedButtons */ + +import '~/merged_buttons'; + +describe('MergedButtons', () => { + const fixturesPath = 'merge_requests/merged_merge_request.html.raw'; + preloadFixtures(fixturesPath); + + beforeEach(() => { + loadFixtures(fixturesPath); + this.mergedButtons = new MergedButtons(); + this.$removeBranchWidget = $('.remove_source_branch_widget:not(.failed)'); + this.$removeBranchProgress = $('.remove_source_branch_in_progress'); + this.$removeBranchFailed = $('.remove_source_branch_widget.failed'); + this.$removeBranchButton = $('.remove_source_branch'); + }); + + describe('removeSourceBranch', () => { + it('shows loader', () => { + $('.remove_source_branch').trigger('click'); + expect(this.$removeBranchProgress).toBeVisible(); + expect(this.$removeBranchWidget).not.toBeVisible(); + }); + }); + + describe('removeBranchSuccess', () => { + it('refreshes page when branch removed', () => { + spyOn(gl.utils, 'refreshCurrentPage').and.stub(); + const response = { status: 200 }; + this.$removeBranchButton.trigger('ajax:success', response, 'xhr'); + expect(gl.utils.refreshCurrentPage).toHaveBeenCalled(); + }); + }); + + describe('removeBranchError', () => { + it('shows error message', () => { + const response = { status: 500 }; + this.$removeBranchButton.trigger('ajax:error', response, 'xhr'); + expect(this.$removeBranchFailed).toBeVisible(); + expect(this.$removeBranchProgress).not.toBeVisible(); + expect(this.$removeBranchWidget).not.toBeVisible(); + }); + }); +}); diff --git a/spec/lib/banzai/filter/issuable_state_filter_spec.rb b/spec/lib/banzai/filter/issuable_state_filter_spec.rb index 603b79a323c..5cb98163746 100644 --- a/spec/lib/banzai/filter/issuable_state_filter_spec.rb +++ b/spec/lib/banzai/filter/issuable_state_filter_spec.rb @@ -6,8 +6,8 @@ describe Banzai::Filter::IssuableStateFilter, lib: true do let(:user) { create(:user) } - def create_link(data) - link_to('text', '', class: 'gfm has-tooltip', data: data) + def create_link(text, data) + link_to(text, '', class: 'gfm has-tooltip', data: data) end it 'ignores non-GFM links' do @@ -19,16 +19,37 @@ describe Banzai::Filter::IssuableStateFilter, lib: true do it 'ignores non-issuable links' do project = create(:empty_project, :public) - link = create_link(project: project, reference_type: 'issue') + link = create_link('text', project: project, reference_type: 'issue') doc = filter(link, current_user: user) expect(doc.css('a').last.text).to eq('text') end + it 'ignores issuable links with empty content' do + issue = create(:issue, :closed) + link = create_link('', issue: issue.id, reference_type: 'issue') + doc = filter(link, current_user: user) + + expect(doc.css('a').last.text).to eq('') + end + + it 'adds text with standard formatting' do + issue = create(:issue, :closed) + link = create_link( + 'something <strong>else</strong>'.html_safe, + issue: issue.id, + reference_type: 'issue' + ) + doc = filter(link, current_user: user) + + expect(doc.css('a').last.inner_html). + to eq('something <strong>else</strong> [closed]') + end + context 'for issue references' do it 'ignores open issue references' do issue = create(:issue) - link = create_link(issue: issue.id, reference_type: 'issue') + link = create_link('text', issue: issue.id, reference_type: 'issue') doc = filter(link, current_user: user) expect(doc.css('a').last.text).to eq('text') @@ -36,7 +57,7 @@ describe Banzai::Filter::IssuableStateFilter, lib: true do it 'ignores reopened issue references' do reopened_issue = create(:issue, :reopened) - link = create_link(issue: reopened_issue.id, reference_type: 'issue') + link = create_link('text', issue: reopened_issue.id, reference_type: 'issue') doc = filter(link, current_user: user) expect(doc.css('a').last.text).to eq('text') @@ -44,7 +65,7 @@ describe Banzai::Filter::IssuableStateFilter, lib: true do it 'appends [closed] to closed issue references' do closed_issue = create(:issue, :closed) - link = create_link(issue: closed_issue.id, reference_type: 'issue') + link = create_link('text', issue: closed_issue.id, reference_type: 'issue') doc = filter(link, current_user: user) expect(doc.css('a').last.text).to eq('text [closed]') @@ -54,7 +75,7 @@ describe Banzai::Filter::IssuableStateFilter, lib: true do context 'for merge request references' do it 'ignores open merge request references' do mr = create(:merge_request) - link = create_link(merge_request: mr.id, reference_type: 'merge_request') + link = create_link('text', merge_request: mr.id, reference_type: 'merge_request') doc = filter(link, current_user: user) expect(doc.css('a').last.text).to eq('text') @@ -62,7 +83,7 @@ describe Banzai::Filter::IssuableStateFilter, lib: true do it 'ignores reopened merge request references' do mr = create(:merge_request, :reopened) - link = create_link(merge_request: mr.id, reference_type: 'merge_request') + link = create_link('text', merge_request: mr.id, reference_type: 'merge_request') doc = filter(link, current_user: user) expect(doc.css('a').last.text).to eq('text') @@ -70,7 +91,7 @@ describe Banzai::Filter::IssuableStateFilter, lib: true do it 'ignores locked merge request references' do mr = create(:merge_request, :locked) - link = create_link(merge_request: mr.id, reference_type: 'merge_request') + link = create_link('text', merge_request: mr.id, reference_type: 'merge_request') doc = filter(link, current_user: user) expect(doc.css('a').last.text).to eq('text') @@ -78,7 +99,7 @@ describe Banzai::Filter::IssuableStateFilter, lib: true do it 'appends [closed] to closed merge request references' do mr = create(:merge_request, :closed) - link = create_link(merge_request: mr.id, reference_type: 'merge_request') + link = create_link('text', merge_request: mr.id, reference_type: 'merge_request') doc = filter(link, current_user: user) expect(doc.css('a').last.text).to eq('text [closed]') @@ -86,7 +107,7 @@ describe Banzai::Filter::IssuableStateFilter, lib: true do it 'appends [merged] to merged merge request references' do mr = create(:merge_request, :merged) - link = create_link(merge_request: mr.id, reference_type: 'merge_request') + link = create_link('text', merge_request: mr.id, reference_type: 'merge_request') doc = filter(link, current_user: user) expect(doc.css('a').last.text).to eq('text [merged]') diff --git a/spec/lib/banzai/reference_parser/base_parser_spec.rb b/spec/lib/banzai/reference_parser/base_parser_spec.rb index a3141894c74..d5746107ee1 100644 --- a/spec/lib/banzai/reference_parser/base_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/base_parser_spec.rb @@ -114,8 +114,27 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do expect(hash).to eq({ link => user }) end - it 'returns an empty Hash when the list of nodes is empty' do - expect(subject.grouped_objects_for_nodes([], User, 'data-user')).to eq({}) + it 'returns an empty Hash when entry does not exist in the database' do + link = double(:link) + + expect(link).to receive(:has_attribute?). + with('data-user'). + and_return(true) + + expect(link).to receive(:attr). + with('data-user'). + and_return('1') + + nodes = [link] + bad_id = user.id + 100 + + expect(subject).to receive(:unique_attribute_values). + with(nodes, 'data-user'). + and_return([bad_id.to_s]) + + hash = subject.grouped_objects_for_nodes(nodes, User, 'data-user') + + expect(hash).to eq({}) end end diff --git a/spec/lib/container_registry/path_spec.rb b/spec/lib/container_registry/path_spec.rb index b9c4572c269..f3b3a9a715f 100644 --- a/spec/lib/container_registry/path_spec.rb +++ b/spec/lib/container_registry/path_spec.rb @@ -33,10 +33,20 @@ describe ContainerRegistry::Path do end describe '#to_s' do - let(:path) { 'some/image' } + context 'when path does not have uppercase characters' do + let(:path) { 'some/image' } - it 'return a string with a repository path' do - expect(subject.to_s).to eq path + it 'return a string with a repository path' do + expect(subject.to_s).to eq 'some/image' + end + end + + context 'when path has uppercase characters' do + let(:path) { 'SoMe/ImAgE' } + + it 'return a string with a repository path' do + expect(subject.to_s).to eq 'some/image' + end end end @@ -70,6 +80,12 @@ describe ContainerRegistry::Path do it { is_expected.to be_valid } end + + context 'when path contains uppercase letters' do + let(:path) { 'Some/Registry' } + + it { is_expected.to be_valid } + end end describe '#has_repository?' do diff --git a/spec/lib/gitlab/ci/trace/stream_spec.rb b/spec/lib/gitlab/ci/trace/stream_spec.rb index 2e57ccef182..03f040f4465 100644 --- a/spec/lib/gitlab/ci/trace/stream_spec.rb +++ b/spec/lib/gitlab/ci/trace/stream_spec.rb @@ -17,12 +17,12 @@ describe Gitlab::Ci::Trace::Stream do describe '#limit' do let(:stream) do described_class.new do - StringIO.new("12345678") + StringIO.new((1..8).to_a.join("\n")) end end - it 'if size is larger we start from beggining' do - stream.limit(10) + it 'if size is larger we start from beginning' do + stream.limit(20) expect(stream.tell).to eq(0) end @@ -30,7 +30,43 @@ describe Gitlab::Ci::Trace::Stream do it 'if size is smaller we start from the end' do stream.limit(2) - expect(stream.tell).to eq(6) + expect(stream.raw).to eq("8") + end + + context 'when the trace contains ANSI sequence and Unicode' do + let(:stream) do + described_class.new do + File.open(expand_fixture_path('trace/ansi-sequence-and-unicode')) + end + end + + it 'forwards to the next linefeed, case 1' do + stream.limit(7) + + result = stream.raw + + expect(result).to eq('') + expect(result.encoding).to eq(Encoding.default_external) + end + + it 'forwards to the next linefeed, case 2' do + stream.limit(29) + + result = stream.raw + + expect(result).to eq("\e[01;32m許功蓋\e[0m\n") + expect(result.encoding).to eq(Encoding.default_external) + end + + # See https://gitlab.com/gitlab-org/gitlab-ce/issues/30796 + it 'reads in binary, output as Encoding.default_external' do + stream.limit(52) + + result = stream.html + + expect(result).to eq("ヾ(´༎ຶД༎ຶ`)ノ<br><span class=\"term-fg-green\">許功蓋</span><br>") + expect(result.encoding).to eq(Encoding.default_external) + end end end diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb index 4e71597521d..ced93c8f762 100644 --- a/spec/models/abuse_report_spec.rb +++ b/spec/models/abuse_report_spec.rb @@ -29,7 +29,8 @@ RSpec.describe AbuseReport, type: :model do it 'lets a worker delete the user' do expect(DeleteUserWorker).to receive(:perform_async).with(user.id, subject.user.id, - delete_solo_owned_groups: true) + delete_solo_owned_groups: true, + hard_delete: true) subject.remove_user(deleted_by: user) end diff --git a/spec/models/container_repository_spec.rb b/spec/models/container_repository_spec.rb index 6d6c9f2adfc..eff41d85972 100644 --- a/spec/models/container_repository_spec.rb +++ b/spec/models/container_repository_spec.rb @@ -34,8 +34,18 @@ describe ContainerRepository do end describe '#path' do - it 'returns a full path to the repository' do - expect(repository.path).to eq('group/test/my_image') + context 'when project path does not contain uppercase letters' do + it 'returns a full path to the repository' do + expect(repository.path).to eq('group/test/my_image') + end + end + + context 'when path contains uppercase letters' do + let(:project) { create(:project, path: 'MY_PROJECT', group: group) } + + it 'returns a full path without capital letters' do + expect(repository.path).to eq('group/my_project/my_image') + end end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 5e5c2b016b6..74d5ebc6db0 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -171,6 +171,27 @@ describe Repository, models: true do end end + describe '#commits' do + it 'sets follow when path is a single path' do + expect(Gitlab::Git::Commit).to receive(:where).with(a_hash_including(follow: true)).and_call_original.twice + + repository.commits('master', path: 'README.md') + repository.commits('master', path: ['README.md']) + end + + it 'does not set follow when path is multiple paths' do + expect(Gitlab::Git::Commit).to receive(:where).with(a_hash_including(follow: false)).and_call_original + + repository.commits('master', path: ['README.md', 'CHANGELOG']) + end + + it 'does not set follow when there are no paths' do + expect(Gitlab::Git::Commit).to receive(:where).with(a_hash_including(follow: false)).and_call_original + + repository.commits('master') + end + end + describe '#find_commits_by_message' do it 'returns commits with messages containing a given string' do commit_ids = repository.find_commits_by_message('submodule').map(&:id) @@ -1259,7 +1280,6 @@ describe Repository, models: true do :changelog, :license, :contributing, - :version, :gitignore, :koding, :gitlab_ci, diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 62f21049b0b..7a07ea618c0 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -144,6 +144,20 @@ describe Projects::CreateService, '#execute', services: true do end end + context 'when a bad service template is created' do + before do + create(:service, type: 'DroneCiService', project: nil, template: true, active: true) + end + + it 'reports an error in the imported project' do + opts[:import_url] = 'http://www.gitlab.com/gitlab-org/gitlab-ce' + project = create_project(user, opts) + + expect(project.errors.full_messages_for(:base).first).to match /Unable to save project. Error: Unable to save DroneCiService/ + expect(project.services.count).to eq 0 + end + end + def create_project(user, opts) Projects::CreateService.new(user, opts).execute end diff --git a/spec/services/users/build_service_spec.rb b/spec/services/users/build_service_spec.rb new file mode 100644 index 00000000000..2a6bfc1b3a0 --- /dev/null +++ b/spec/services/users/build_service_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe Users::BuildService, services: true do + describe '#execute' do + let(:params) do + { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass' } + end + + context 'with an admin user' do + let(:admin_user) { create(:admin) } + let(:service) { described_class.new(admin_user, params) } + + it 'returns a valid user' do + expect(service.execute).to be_valid + end + end + + context 'with non admin user' do + let(:user) { create(:user) } + let(:service) { described_class.new(user, params) } + + it 'raises AccessDeniedError exception' do + expect { service.execute }.to raise_error Gitlab::Access::AccessDeniedError + end + end + + context 'with nil user' do + let(:service) { described_class.new(nil, params) } + + it 'returns a valid user' do + expect(service.execute).to be_valid + end + + context 'when "send_user_confirmation_email" application setting is true' do + before do + stub_application_setting(send_user_confirmation_email: true, signup_enabled?: true) + end + + it 'does not confirm the user' do + expect(service.execute).not_to be_confirmed + end + end + + context 'when "send_user_confirmation_email" application setting is false' do + before do + stub_application_setting(send_user_confirmation_email: false, signup_enabled?: true) + end + + it 'confirms the user' do + expect(service.execute).to be_confirmed + end + end + end + end +end diff --git a/spec/services/users/create_service_spec.rb b/spec/services/users/create_service_spec.rb index a111aec2f89..75746278573 100644 --- a/spec/services/users/create_service_spec.rb +++ b/spec/services/users/create_service_spec.rb @@ -1,38 +1,6 @@ require 'spec_helper' describe Users::CreateService, services: true do - describe '#build' do - let(:params) do - { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass' } - end - - context 'with an admin user' do - let(:admin_user) { create(:admin) } - let(:service) { described_class.new(admin_user, params) } - - it 'returns a valid user' do - expect(service.build).to be_valid - end - end - - context 'with non admin user' do - let(:user) { create(:user) } - let(:service) { described_class.new(user, params) } - - it 'raises AccessDeniedError exception' do - expect { service.build }.to raise_error Gitlab::Access::AccessDeniedError - end - end - - context 'with nil user' do - let(:service) { described_class.new(nil, params) } - - it 'returns a valid user' do - expect(service.build).to be_valid - end - end - end - describe '#execute' do let(:admin_user) { create(:admin) } @@ -185,40 +153,18 @@ describe Users::CreateService, services: true do end let(:service) { described_class.new(nil, params) } - context 'when "send_user_confirmation_email" application setting is true' do - before do - current_application_settings = double(:current_application_settings, send_user_confirmation_email: true, signup_enabled?: true) - allow(service).to receive(:current_application_settings).and_return(current_application_settings) - end - - it 'does not confirm the user' do - expect(service.execute).not_to be_confirmed - end - end - - context 'when "send_user_confirmation_email" application setting is false' do - before do - current_application_settings = double(:current_application_settings, send_user_confirmation_email: false, signup_enabled?: true) - allow(service).to receive(:current_application_settings).and_return(current_application_settings) - end - - it 'confirms the user' do - expect(service.execute).to be_confirmed - end - - it 'persists the given attributes' do - user = service.execute - user.reload - - expect(user).to have_attributes( - name: params[:name], - username: params[:username], - email: params[:email], - password: params[:password], - created_by_id: nil, - admin: false - ) - end + it 'persists the given attributes' do + user = service.execute + user.reload + + expect(user).to have_attributes( + name: params[:name], + username: params[:username], + email: params[:email], + password: params[:password], + created_by_id: nil, + admin: false + ) end end end diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb index 43c18992d1a..4bc30018ebd 100644 --- a/spec/services/users/destroy_service_spec.rb +++ b/spec/services/users/destroy_service_spec.rb @@ -152,6 +152,12 @@ describe Users::DestroyService, services: true do service.execute(user) end + + it 'does not run `MigrateToGhostUser` if hard_delete option is given' do + expect_any_instance_of(Users::MigrateToGhostUserService).not_to receive(:execute) + + service.execute(user, hard_delete: true) + end end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a3665795452..e67ad8f3455 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,8 +9,14 @@ require 'rspec/rails' require 'shoulda/matchers' require 'rspec/retry' -if (ENV['RSPEC_PROFILING_POSTGRES_URL'] || ENV['RSPEC_PROFILING']) && - (!ENV.has_key?('CI') || ENV['CI_COMMIT_REF_NAME'] == 'master') +rspec_profiling_is_configured = + ENV['RSPEC_PROFILING_POSTGRES_URL'] || + ENV['RSPEC_PROFILING'] +branch_can_be_profiled = + ENV['CI_COMMIT_REF_NAME'] == 'master' || + ENV['CI_COMMIT_REF_NAME'] =~ /rspec-profile/ + +if rspec_profiling_is_configured && (!ENV.key?('CI') || branch_can_be_profiled) require 'rspec_profiling/rspec' end diff --git a/spec/support/fixture_helpers.rb b/spec/support/fixture_helpers.rb index a05c9d18002..5515c355cea 100644 --- a/spec/support/fixture_helpers.rb +++ b/spec/support/fixture_helpers.rb @@ -1,8 +1,11 @@ module FixtureHelpers def fixture_file(filename) return '' if filename.blank? - file_path = File.expand_path(Rails.root.join('spec/fixtures/', filename)) - File.read(file_path) + File.read(expand_fixture_path(filename)) + end + + def expand_fixture_path(filename) + File.expand_path(Rails.root.join('spec/fixtures/', filename)) end end diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb index b369dcbb305..aaf998a546f 100644 --- a/spec/tasks/gitlab/gitaly_rake_spec.rb +++ b/spec/tasks/gitlab/gitaly_rake_spec.rb @@ -8,7 +8,7 @@ describe 'gitlab:gitaly namespace rake task' do describe 'install' do let(:repo) { 'https://gitlab.com/gitlab-org/gitaly.git' } let(:clone_path) { Rails.root.join('tmp/tests/gitaly').to_s } - let(:tag) { "v#{File.read(Rails.root.join(Gitlab::GitalyClient::SERVER_VERSION_FILE)).chomp}" } + let(:version) { File.read(Rails.root.join(Gitlab::GitalyClient::SERVER_VERSION_FILE)).chomp } context 'no dir given' do it 'aborts and display a help message' do @@ -21,7 +21,7 @@ describe 'gitlab:gitaly namespace rake task' do context 'when an underlying Git command fail' do it 'aborts and display a help message' do expect_any_instance_of(Object). - to receive(:checkout_or_clone_tag).and_raise 'Git error' + to receive(:checkout_or_clone_version).and_raise 'Git error' expect { run_rake_task('gitlab:gitaly:install', clone_path) }.to raise_error 'Git error' end @@ -32,9 +32,9 @@ describe 'gitlab:gitaly namespace rake task' do expect(Dir).to receive(:chdir).with(clone_path) end - it 'calls checkout_or_clone_tag with the right arguments' do + it 'calls checkout_or_clone_version with the right arguments' do expect_any_instance_of(Object). - to receive(:checkout_or_clone_tag).with(tag: tag, repo: repo, target_dir: clone_path) + to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path) run_rake_task('gitlab:gitaly:install', clone_path) end @@ -48,7 +48,7 @@ describe 'gitlab:gitaly namespace rake task' do context 'gmake is available' do before do - expect_any_instance_of(Object).to receive(:checkout_or_clone_tag) + expect_any_instance_of(Object).to receive(:checkout_or_clone_version) allow_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true) end @@ -62,7 +62,7 @@ describe 'gitlab:gitaly namespace rake task' do context 'gmake is not available' do before do - expect_any_instance_of(Object).to receive(:checkout_or_clone_tag) + expect_any_instance_of(Object).to receive(:checkout_or_clone_version) allow_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true) end diff --git a/spec/tasks/gitlab/task_helpers_spec.rb b/spec/tasks/gitlab/task_helpers_spec.rb index 86e42d845ce..3d9ba7cdc6f 100644 --- a/spec/tasks/gitlab/task_helpers_spec.rb +++ b/spec/tasks/gitlab/task_helpers_spec.rb @@ -10,19 +10,38 @@ describe Gitlab::TaskHelpers do let(:repo) { 'https://gitlab.com/gitlab-org/gitlab-test.git' } let(:clone_path) { Rails.root.join('tmp/tests/task_helpers_tests').to_s } + let(:version) { '1.1.0' } let(:tag) { 'v1.1.0' } - describe '#checkout_or_clone_tag' do + describe '#checkout_or_clone_version' do before do allow(subject).to receive(:run_command!) - expect(subject).to receive(:reset_to_tag).with(tag, clone_path) end - context 'target_dir does not exist' do - it 'clones the repo, retrieve the tag from origin, and checkout the tag' do + it 'checkout the version and reset to it' do + expect(subject).to receive(:checkout_version).with(tag, clone_path) + expect(subject).to receive(:reset_to_version).with(tag, clone_path) + + subject.checkout_or_clone_version(version: version, repo: repo, target_dir: clone_path) + end + + context 'with a branch version' do + let(:version) { '=branch_name' } + let(:branch) { 'branch_name' } + + it 'checkout the version and reset to it with a branch name' do + expect(subject).to receive(:checkout_version).with(branch, clone_path) + expect(subject).to receive(:reset_to_version).with(branch, clone_path) + + subject.checkout_or_clone_version(version: version, repo: repo, target_dir: clone_path) + end + end + + context "target_dir doesn't exist" do + it 'clones the repo' do expect(subject).to receive(:clone_repo).with(repo, clone_path) - subject.checkout_or_clone_tag(tag: tag, repo: repo, target_dir: clone_path) + subject.checkout_or_clone_version(version: version, repo: repo, target_dir: clone_path) end end @@ -31,10 +50,10 @@ describe Gitlab::TaskHelpers do expect(Dir).to receive(:exist?).and_return(true) end - it 'fetch and checkout the tag' do - expect(subject).to receive(:checkout_tag).with(tag, clone_path) + it "doesn't clone the repository" do + expect(subject).not_to receive(:clone_repo) - subject.checkout_or_clone_tag(tag: tag, repo: repo, target_dir: clone_path) + subject.checkout_or_clone_version(version: version, repo: repo, target_dir: clone_path) end end end @@ -48,49 +67,23 @@ describe Gitlab::TaskHelpers do end end - describe '#checkout_tag' do + describe '#checkout_version' do it 'clones the repo in the target dir' do expect(subject). - to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch --tags --quiet]) + to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch --quiet]) expect(subject). to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} checkout --quiet #{tag}]) - subject.checkout_tag(tag, clone_path) + subject.checkout_version(tag, clone_path) end end - describe '#reset_to_tag' do - let(:tag) { 'v1.1.0' } - before do + describe '#reset_to_version' do + it 'resets --hard to the given version' do expect(subject). to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} reset --hard #{tag}]) - end - context 'when the tag is not checked out locally' do - before do - expect(subject). - to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} describe -- #{tag}]).and_raise(Gitlab::TaskFailedError) - end - - it 'fetch origin, ensure the tag exists, and resets --hard to the given tag' do - expect(subject). - to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch origin]) - expect(subject). - to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} describe -- origin/#{tag}]).and_return(tag) - - subject.reset_to_tag(tag, clone_path) - end - end - - context 'when the tag is checked out locally' do - before do - expect(subject). - to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} describe -- #{tag}]).and_return(tag) - end - - it 'resets --hard to the given tag' do - subject.reset_to_tag(tag, clone_path) - end + subject.reset_to_version(tag, clone_path) end end end diff --git a/spec/tasks/gitlab/workhorse_rake_spec.rb b/spec/tasks/gitlab/workhorse_rake_spec.rb index 8a66a4aa047..63d1cf2bbe5 100644 --- a/spec/tasks/gitlab/workhorse_rake_spec.rb +++ b/spec/tasks/gitlab/workhorse_rake_spec.rb @@ -8,7 +8,7 @@ describe 'gitlab:workhorse namespace rake task' do describe 'install' do let(:repo) { 'https://gitlab.com/gitlab-org/gitlab-workhorse.git' } let(:clone_path) { Rails.root.join('tmp/tests/gitlab-workhorse').to_s } - let(:tag) { "v#{File.read(Rails.root.join(Gitlab::Workhorse::VERSION_FILE)).chomp}" } + let(:version) { File.read(Rails.root.join(Gitlab::Workhorse::VERSION_FILE)).chomp } context 'no dir given' do it 'aborts and display a help message' do @@ -21,7 +21,7 @@ describe 'gitlab:workhorse namespace rake task' do context 'when an underlying Git command fail' do it 'aborts and display a help message' do expect_any_instance_of(Object). - to receive(:checkout_or_clone_tag).and_raise 'Git error' + to receive(:checkout_or_clone_version).and_raise 'Git error' expect { run_rake_task('gitlab:workhorse:install', clone_path) }.to raise_error 'Git error' end @@ -32,9 +32,9 @@ describe 'gitlab:workhorse namespace rake task' do expect(Dir).to receive(:chdir).with(clone_path) end - it 'calls checkout_or_clone_tag with the right arguments' do + it 'calls checkout_or_clone_version with the right arguments' do expect_any_instance_of(Object). - to receive(:checkout_or_clone_tag).with(tag: tag, repo: repo, target_dir: clone_path) + to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path) run_rake_task('gitlab:workhorse:install', clone_path) end @@ -48,7 +48,7 @@ describe 'gitlab:workhorse namespace rake task' do context 'gmake is available' do before do - expect_any_instance_of(Object).to receive(:checkout_or_clone_tag) + expect_any_instance_of(Object).to receive(:checkout_or_clone_version) allow_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true) end @@ -62,7 +62,7 @@ describe 'gitlab:workhorse namespace rake task' do context 'gmake is not available' do before do - expect_any_instance_of(Object).to receive(:checkout_or_clone_tag) + expect_any_instance_of(Object).to receive(:checkout_or_clone_version) allow_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true) end |