Skip to content
Snippets Groups Projects
Commit 68f33e42 authored by Filipa Lacerda's avatar Filipa Lacerda
Browse files

Merge branch 'master' into 43404-pipelines-commit

* master: (124 commits)
  Double-check next value for internal ids.
  Resolve "skeleton placeholder on diff has white background"
  Replace the `project/commits/comments.feature` spinach test with an rspec analog
  Set ENV['IN_MEMORY_APPLICATION_SETTINGS'] to 'true in spec/db/production/settings_spec.rb
  Update docs on `.gitlab-ci.yml` and variables policy
  Add note about support of variables policy to docs
  Revert and fix
  corrected git cheat sheet url
  Bump parser gem to 2.5.1.0 to remove warning in rspec
  Update invalid test application_settings_controller_spec.rb
  Use stable docker image instead of latest
  Handle cancelled request
  Stop polling and fetch the table
  [ci skip] Updates table when stage is clicked
  Backport GitLab domain helper methods
  use jquery object in togglePopover
  Fix other listeners
  Fix the metrics link being not rendering the html
  Fix feature_highlight listener
  Update custom issue tracker docs
  ...
parents cb8fa2a0 ddd7a81d
No related branches found
No related tags found
No related merge requests found
Showing
with 877 additions and 178 deletions
Loading
Loading
@@ -108,4 +108,18 @@ feature 'Milestone' do
expect(page).to have_selector('.js-delete-milestone-button', count: 0)
end
end
feature 'deprecation popover', :js do
it 'opens deprecation popover' do
milestone = create(:milestone, project: project)
visit group_milestone_path(group, milestone, title: milestone.title)
expect(page).to have_selector('.milestone-deprecation-message')
find('.milestone-deprecation-message .js-popover-link').click
expect(page).to have_selector('.milestone-deprecation-message .popover')
end
end
end
require "spec_helper"
describe "User comments on commit", :js do
include Spec::Support::Helpers::Features::NotesHelpers
include RepoHelpers
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
COMMENT_TEXT = "XML attached".freeze
before do
sign_in(user)
project.add_developer(user)
visit(project_commit_path(project, sample_commit.id))
end
context "when adding new comment" do
it "adds comment" do
EMOJI = ":+1:".freeze
page.within(".js-main-target-form") do
expect(page).not_to have_link("Cancel")
fill_in("note[note]", with: "#{COMMENT_TEXT} #{EMOJI}")
# Check on `Preview` tab
click_link("Preview")
expect(find(".js-md-preview")).to have_content(COMMENT_TEXT).and have_css("gl-emoji")
expect(page).not_to have_css(".js-note-text")
# Check on `Write` tab
click_link("Write")
expect(page).to have_field("note[note]", with: "#{COMMENT_TEXT} #{EMOJI}")
# Submit comment from the `Preview` tab to get rid of a separate `it` block
# which would specially tests if everything gets cleared from the note form.
click_link("Preview")
click_button("Comment")
end
wait_for_requests
page.within(".note") do
expect(page).to have_content(COMMENT_TEXT).and have_css("gl-emoji")
end
page.within(".js-main-target-form") do
expect(page).to have_field("note[note]", with: "").and have_no_css(".js-md-preview")
end
end
end
context "when editing comment" do
before do
add_note(COMMENT_TEXT)
end
it "edits comment" do
NEW_COMMENT_TEXT = "+1 Awesome!".freeze
page.within(".main-notes-list") do
note = find(".note")
note.hover
note.find(".js-note-edit").click
end
page.find(".current-note-edit-form textarea")
page.within(".current-note-edit-form") do
fill_in("note[note]", with: NEW_COMMENT_TEXT)
click_button("Save comment")
end
wait_for_requests
page.within(".note") do
expect(page).to have_content(NEW_COMMENT_TEXT)
end
end
end
context "when deleting comment" do
before do
add_note(COMMENT_TEXT)
end
it "deletes comment" do
page.within(".note") do
expect(page).to have_content(COMMENT_TEXT)
end
page.within(".main-notes-list") do
note = find(".note")
note.hover
find(".more-actions").click
find(".more-actions .dropdown-menu li", match: :first)
accept_confirm { find(".js-note-delete").click }
end
expect(page).not_to have_css(".note")
end
end
end
require 'spec_helper'
describe 'Embedded Snippets' do
let(:snippet) { create(:personal_snippet, :public, file_name: 'random_dir.rb', content: content) }
let(:content) { "require 'fileutils'\nFileUtils.mkdir_p 'some/random_dir'\n" }
it 'loads snippet', :js do
script_url = "http://#{Capybara.current_session.server.host}:#{Capybara.current_session.server.port}/#{snippet_path(snippet, format: 'js')}"
embed_body = "<html><body><script src=\"#{script_url}\"></script></body></html>"
rack_app = proc do
['200', { 'Content-Type' => 'text/html' }, [embed_body]]
end
server = Capybara::Server.new(rack_app)
server.boot
visit("http://#{server.host}:#{server.port}/embedded_snippet.html")
expect(page).to have_content("random_dir.rb")
expect(page).to have_content("require 'fileutils'")
expect(page).to have_link('Open raw')
expect(page).to have_link('Download')
end
end
Loading
Loading
@@ -162,4 +162,11 @@ describe IconsHelper do
expect(file_type_icon_class('file', 0, 'CHANGELOG')).to eq 'file-text-o'
end
end
describe '#external_snippet_icon' do
it 'returns external snippet icon' do
expect(external_snippet_icon('download').to_s)
.to eq("<span class=\"gl-snippet-icon gl-snippet-icon-download\"></span>")
end
end
end
require 'spec_helper'
describe SnippetsHelper do
include IconsHelper
describe '#embedded_snippet_raw_button' do
it 'gives view raw button of embedded snippets for project snippets' do
@snippet = create(:project_snippet, :public)
expect(embedded_snippet_raw_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Open raw\" href=\"#{raw_project_snippet_url(@snippet.project, @snippet)}\">#{external_snippet_icon('doc_code')}</a>")
end
it 'gives view raw button of embedded snippets for personal snippets' do
@snippet = create(:personal_snippet, :public)
expect(embedded_snippet_raw_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Open raw\" href=\"#{raw_snippet_url(@snippet)}\">#{external_snippet_icon('doc_code')}</a>")
end
end
describe '#embedded_snippet_download_button' do
it 'gives download button of embedded snippets for project snippets' do
@snippet = create(:project_snippet, :public)
expect(embedded_snippet_download_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" title=\"Download\" rel=\"noopener noreferrer\" href=\"#{raw_project_snippet_url(@snippet.project, @snippet, inline: false)}\">#{external_snippet_icon('download')}</a>")
end
it 'gives download button of embedded snippets for personal snippets' do
@snippet = create(:personal_snippet, :public)
expect(embedded_snippet_download_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" title=\"Download\" rel=\"noopener noreferrer\" href=\"#{raw_snippet_url(@snippet, inline: false)}\">#{external_snippet_icon('download')}</a>")
end
end
end
Loading
Loading
@@ -3,12 +3,11 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import {
getSelector,
togglePopover,
dismiss,
mouseleave,
mouseenter,
inserted,
} from '~/feature_highlight/feature_highlight_helper';
import { togglePopover } from '~/shared/popover';
import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
 
describe('feature highlight helper', () => {
Loading
Loading
@@ -19,110 +18,6 @@ describe('feature highlight helper', () => {
});
});
 
describe('togglePopover', () => {
describe('togglePopover(true)', () => {
it('returns true when popover is shown', () => {
const context = {
hasClass: () => false,
popover: () => {},
toggleClass: () => {},
};
expect(togglePopover.call(context, true)).toEqual(true);
});
it('returns false when popover is already shown', () => {
const context = {
hasClass: () => true,
};
expect(togglePopover.call(context, true)).toEqual(false);
});
it('shows popover', (done) => {
const context = {
hasClass: () => false,
popover: () => {},
toggleClass: () => {},
};
spyOn(context, 'popover').and.callFake((method) => {
expect(method).toEqual('show');
done();
});
togglePopover.call(context, true);
});
it('adds disable-animation and js-popover-show class', (done) => {
const context = {
hasClass: () => false,
popover: () => {},
toggleClass: () => {},
};
spyOn(context, 'toggleClass').and.callFake((classNames, show) => {
expect(classNames).toEqual('disable-animation js-popover-show');
expect(show).toEqual(true);
done();
});
togglePopover.call(context, true);
});
});
describe('togglePopover(false)', () => {
it('returns true when popover is hidden', () => {
const context = {
hasClass: () => true,
popover: () => {},
toggleClass: () => {},
};
expect(togglePopover.call(context, false)).toEqual(true);
});
it('returns false when popover is already hidden', () => {
const context = {
hasClass: () => false,
};
expect(togglePopover.call(context, false)).toEqual(false);
});
it('hides popover', (done) => {
const context = {
hasClass: () => true,
popover: () => {},
toggleClass: () => {},
};
spyOn(context, 'popover').and.callFake((method) => {
expect(method).toEqual('hide');
done();
});
togglePopover.call(context, false);
});
it('removes disable-animation and js-popover-show class', (done) => {
const context = {
hasClass: () => true,
popover: () => {},
toggleClass: () => {},
};
spyOn(context, 'toggleClass').and.callFake((classNames, show) => {
expect(classNames).toEqual('disable-animation js-popover-show');
expect(show).toEqual(false);
done();
});
togglePopover.call(context, false);
});
});
});
describe('dismiss', () => {
let mock;
const context = {
Loading
Loading
@@ -163,56 +58,6 @@ describe('feature highlight helper', () => {
});
});
 
describe('mouseleave', () => {
it('calls hide popover if .popover:hover is false', () => {
const fakeJquery = {
length: 0,
};
spyOn($.fn, 'init').and.callFake(selector => (selector === '.popover:hover' ? fakeJquery : $.fn));
spyOn(togglePopover, 'call');
mouseleave();
expect(togglePopover.call).toHaveBeenCalledWith(jasmine.any(Object), false);
});
it('does not call hide popover if .popover:hover is true', () => {
const fakeJquery = {
length: 1,
};
spyOn($.fn, 'init').and.callFake(selector => (selector === '.popover:hover' ? fakeJquery : $.fn));
spyOn(togglePopover, 'call');
mouseleave();
expect(togglePopover.call).not.toHaveBeenCalledWith(false);
});
});
describe('mouseenter', () => {
const context = {};
it('shows popover', () => {
spyOn(togglePopover, 'call').and.returnValue(false);
mouseenter.call(context);
expect(togglePopover.call).toHaveBeenCalledWith(jasmine.any(Object), true);
});
it('registers mouseleave event if popover is showed', (done) => {
spyOn(togglePopover, 'call').and.returnValue(true);
spyOn($.fn, 'on').and.callFake((eventName) => {
expect(eventName).toEqual('mouseleave');
done();
});
mouseenter.call(context);
});
it('does not register mouseleave event if popover is not showed', () => {
spyOn(togglePopover, 'call').and.returnValue(false);
const spy = spyOn($.fn, 'on').and.callFake(() => {});
mouseenter.call(context);
expect(spy).not.toHaveBeenCalled();
});
});
describe('inserted', () => {
it('registers click event callback', (done) => {
const context = {
Loading
Loading
import $ from 'jquery';
import * as featureHighlightHelper from '~/feature_highlight/feature_highlight_helper';
import * as featureHighlight from '~/feature_highlight/feature_highlight';
import * as popover from '~/shared/popover';
import axios from '~/lib/utils/axios_utils';
import MockAdapter from 'axios-mock-adapter';
 
Loading
Loading
@@ -29,7 +29,6 @@ describe('feature highlight', () => {
mock = new MockAdapter(axios);
mock.onGet('/test').reply(200);
spyOn(window, 'addEventListener');
spyOn(window, 'removeEventListener');
featureHighlight.setupFeatureHighlightPopover('test', 0);
});
 
Loading
Loading
@@ -45,14 +44,14 @@ describe('feature highlight', () => {
});
 
it('setup mouseenter', () => {
const toggleSpy = spyOn(featureHighlightHelper.togglePopover, 'call');
const toggleSpy = spyOn(popover.togglePopover, 'call');
$(selector).trigger('mouseenter');
 
expect(toggleSpy).toHaveBeenCalledWith(jasmine.any(Object), true);
});
 
it('setup debounced mouseleave', (done) => {
const toggleSpy = spyOn(featureHighlightHelper.togglePopover, 'call');
const toggleSpy = spyOn(popover.togglePopover, 'call');
$(selector).trigger('mouseleave');
 
// Even though we've set the debounce to 0ms, setTimeout is needed for the debounce
Loading
Loading
@@ -64,12 +63,7 @@ describe('feature highlight', () => {
 
it('setup show.bs.popover', () => {
$(selector).trigger('show.bs.popover');
expect(window.addEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function));
});
it('setup hide.bs.popover', () => {
$(selector).trigger('hide.bs.popover');
expect(window.removeEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function));
expect(window.addEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function), { once: true });
});
 
it('removes disabled attribute', () => {
Loading
Loading
@@ -85,7 +79,7 @@ describe('feature highlight', () => {
it('toggles when clicked', () => {
$(selector).trigger('mouseenter');
const popoverId = $(selector).attr('aria-describedby');
const toggleSpy = spyOn(featureHighlightHelper.togglePopover, 'call');
const toggleSpy = spyOn(popover.togglePopover, 'call');
 
$(`#${popoverId} .dismiss-feature-highlight`).click();
 
Loading
Loading
Loading
Loading
@@ -27,7 +27,7 @@ describe('RepoLoadingFile', () => {
const lines = [...container.querySelectorAll(':scope > div')];
 
expect(container).toBeTruthy();
expect(lines.length).toEqual(6);
expect(lines.length).toEqual(3);
assertLines(lines);
});
}
Loading
Loading
This diff is collapsed.
Loading
Loading
@@ -4,6 +4,7 @@ import axios from '~/lib/utils/axios_utils';
import pipelinesComp from '~/pipelines/components/pipelines.vue';
import Store from '~/pipelines/stores/pipelines_store';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { pipelineWithStages, stageReply } from './mock_data';
 
describe('Pipelines', () => {
const jsonFixtureName = 'pipelines/pipelines.json';
Loading
Loading
@@ -668,4 +669,79 @@ describe('Pipelines', () => {
});
});
});
describe('updates results when a staged is clicked', () => {
beforeEach(() => {
const copyPipeline = Object.assign({}, pipelineWithStages);
copyPipeline.id += 1;
mock
.onGet('twitter/flight/pipelines.json').reply(200, {
pipelines: [pipelineWithStages],
count: {
all: 1,
finished: 1,
pending: 0,
running: 0,
},
}, {
'POLL-INTERVAL': 100,
})
.onGet(pipelineWithStages.details.stages[0].dropdown_path)
.reply(200, stageReply);
vm = mountComponent(PipelinesComponent, {
store: new Store(),
hasGitlabCi: true,
canCreatePipeline: true,
...paths,
});
});
describe('when a request is being made', () => {
it('stops polling, cancels the request, fetches pipelines & restarts polling', (done) => {
spyOn(vm.poll, 'stop');
spyOn(vm.poll, 'restart');
spyOn(vm, 'getPipelines').and.returnValue(Promise.resolve());
spyOn(vm.service.cancelationSource, 'cancel').and.callThrough();
setTimeout(() => {
vm.isMakingRequest = true;
return vm.$nextTick()
.then(() => {
vm.$el.querySelector('.js-builds-dropdown-button').click();
})
.then(() => {
expect(vm.service.cancelationSource.cancel).toHaveBeenCalled();
expect(vm.poll.stop).toHaveBeenCalled();
setTimeout(() => {
expect(vm.getPipelines).toHaveBeenCalled();
expect(vm.poll.restart).toHaveBeenCalled();
done();
}, 0);
});
}, 0);
});
});
describe('when no request is being made', () => {
it('stops polling, fetches pipelines & restarts polling', (done) => {
spyOn(vm.poll, 'stop');
spyOn(vm.poll, 'restart');
spyOn(vm, 'getPipelines').and.returnValue(Promise.resolve());
setTimeout(() => {
vm.$el.querySelector('.js-builds-dropdown-button').click();
expect(vm.poll.stop).toHaveBeenCalled();
setTimeout(() => {
expect(vm.getPipelines).toHaveBeenCalled();
expect(vm.poll.restart).toHaveBeenCalled();
done();
}, 0);
}, 0);
});
});
});
});
Loading
Loading
@@ -2,6 +2,7 @@ import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import stage from '~/pipelines/components/stage.vue';
import eventHub from '~/pipelines/event_hub';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
 
describe('Pipelines stage component', () => {
Loading
Loading
@@ -43,13 +44,15 @@ describe('Pipelines stage component', () => {
mock.onGet('path.json').reply(200, { html: 'foo' });
});
 
it('should render the received data', done => {
it('should render the received data and emit `clickedDropdown` event', done => {
spyOn(eventHub, '$emit');
component.$el.querySelector('button').click();
 
setTimeout(() => {
expect(
component.$el.querySelector('.js-builds-dropdown-container ul').textContent.trim(),
).toEqual('foo');
expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown');
done();
}, 0);
});
Loading
Loading
import $ from 'jquery';
import {
togglePopover,
mouseleave,
mouseenter,
} from '~/shared/popover';
describe('popover', () => {
describe('togglePopover', () => {
describe('togglePopover(true)', () => {
it('returns true when popover is shown', () => {
const context = {
hasClass: () => false,
popover: () => {},
toggleClass: () => {},
};
expect(togglePopover.call(context, true)).toEqual(true);
});
it('returns false when popover is already shown', () => {
const context = {
hasClass: () => true,
};
expect(togglePopover.call(context, true)).toEqual(false);
});
it('shows popover', (done) => {
const context = {
hasClass: () => false,
popover: () => {},
toggleClass: () => {},
};
spyOn(context, 'popover').and.callFake((method) => {
expect(method).toEqual('show');
done();
});
togglePopover.call(context, true);
});
it('adds disable-animation and js-popover-show class', (done) => {
const context = {
hasClass: () => false,
popover: () => {},
toggleClass: () => {},
};
spyOn(context, 'toggleClass').and.callFake((classNames, show) => {
expect(classNames).toEqual('disable-animation js-popover-show');
expect(show).toEqual(true);
done();
});
togglePopover.call(context, true);
});
});
describe('togglePopover(false)', () => {
it('returns true when popover is hidden', () => {
const context = {
hasClass: () => true,
popover: () => {},
toggleClass: () => {},
};
expect(togglePopover.call(context, false)).toEqual(true);
});
it('returns false when popover is already hidden', () => {
const context = {
hasClass: () => false,
};
expect(togglePopover.call(context, false)).toEqual(false);
});
it('hides popover', (done) => {
const context = {
hasClass: () => true,
popover: () => {},
toggleClass: () => {},
};
spyOn(context, 'popover').and.callFake((method) => {
expect(method).toEqual('hide');
done();
});
togglePopover.call(context, false);
});
it('removes disable-animation and js-popover-show class', (done) => {
const context = {
hasClass: () => true,
popover: () => {},
toggleClass: () => {},
};
spyOn(context, 'toggleClass').and.callFake((classNames, show) => {
expect(classNames).toEqual('disable-animation js-popover-show');
expect(show).toEqual(false);
done();
});
togglePopover.call(context, false);
});
});
});
describe('mouseleave', () => {
it('calls hide popover if .popover:hover is false', () => {
const fakeJquery = {
length: 0,
};
spyOn($.fn, 'init').and.callFake(selector => (selector === '.popover:hover' ? fakeJquery : $.fn));
spyOn(togglePopover, 'call');
mouseleave();
expect(togglePopover.call).toHaveBeenCalledWith(jasmine.any(Object), false);
});
it('does not call hide popover if .popover:hover is true', () => {
const fakeJquery = {
length: 1,
};
spyOn($.fn, 'init').and.callFake(selector => (selector === '.popover:hover' ? fakeJquery : $.fn));
spyOn(togglePopover, 'call');
mouseleave();
expect(togglePopover.call).not.toHaveBeenCalledWith(false);
});
});
describe('mouseenter', () => {
const context = {};
it('shows popover', () => {
spyOn(togglePopover, 'call').and.returnValue(false);
mouseenter.call(context);
expect(togglePopover.call).toHaveBeenCalledWith(jasmine.any(Object), true);
});
it('registers mouseleave event if popover is showed', (done) => {
spyOn(togglePopover, 'call').and.returnValue(true);
spyOn($.fn, 'on').and.callFake((eventName) => {
expect(eventName).toEqual('mouseleave');
done();
});
mouseenter.call(context);
});
it('does not register mouseleave event if popover is not showed', () => {
spyOn(togglePopover, 'call').and.returnValue(false);
const spy = spyOn($.fn, 'on').and.callFake(() => {});
mouseenter.call(context);
expect(spy).not.toHaveBeenCalled();
});
});
});
import findAndFollowLink from '~/shortcuts_dashboard_navigation';
import * as urlUtility from '~/lib/utils/url_utility';
describe('findAndFollowLink', () => {
it('visits a link when the selector exists', () => {
const href = '/some/path';
const locationSpy = spyOn(urlUtility, 'visitUrl');
setFixtures(`<a class="my-shortcut" href="${href}">link</a>`);
findAndFollowLink('.my-shortcut');
expect(locationSpy).toHaveBeenCalledWith(href);
});
it('does not throw an exception when the selector does not exist', () => {
const locationSpy = spyOn(urlUtility, 'visitUrl');
// this should not throw an exception
findAndFollowLink('.this-selector-does-not-exist');
expect(locationSpy).not.toHaveBeenCalled();
});
});
Loading
Loading
@@ -51,8 +51,7 @@ const createComponent = () => {
 
const messages = {
loadingMetrics: 'Loading deployment statistics',
hasMetrics:
'<a href="/root/acets-review-apps/environments/15/metrics"> Memory </a> usage is <b> unchanged </b> at 0MB',
hasMetrics: 'Memory usage is unchanged at 0MB',
loadFailed: 'Failed to load deployment statistics',
metricsUnavailable: 'Deployment statistics are not available currently',
};
Loading
Loading
Loading
Loading
@@ -14,8 +14,8 @@ describe('Skeleton loading container', () => {
vm.$destroy();
});
 
it('renders 6 skeleton lines by default', () => {
expect(vm.$el.querySelector('.skeleton-line-6')).not.toBeNull();
it('renders 3 skeleton lines by default', () => {
expect(vm.$el.querySelector('.skeleton-line-3')).not.toBeNull();
});
 
it('renders in full mode by default', () => {
Loading
Loading
Loading
Loading
@@ -3,6 +3,48 @@ require 'spec_helper'
describe API::Helpers do
subject { Class.new.include(described_class).new }
 
describe '#find_project' do
let(:project) { create(:project) }
shared_examples 'project finder' do
context 'when project exists' do
it 'returns requested project' do
expect(subject.find_project(existing_id)).to eq(project)
end
it 'returns nil' do
expect(subject.find_project(non_existing_id)).to be_nil
end
end
end
context 'when ID is used as an argument' do
let(:existing_id) { project.id }
let(:non_existing_id) { (Project.maximum(:id) || 0) + 1 }
it_behaves_like 'project finder'
end
context 'when PATH is used as an argument' do
let(:existing_id) { project.full_path }
let(:non_existing_id) { 'something/else' }
it_behaves_like 'project finder'
context 'with an invalid PATH' do
let(:non_existing_id) { 'undefined' } # path without slash
it_behaves_like 'project finder'
it 'does not hit the database' do
expect(Project).not_to receive(:find_by_full_path)
subject.find_project(non_existing_id)
end
end
end
end
describe '#find_namespace' do
let(:namespace) { create(:namespace) }
 
Loading
Loading
Loading
Loading
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cache do
let!(:project) { create(:project, :repository) }
let(:pipeline_status) { described_class.new(project) }
let(:cache_key) { "projects/#{project.id}/pipeline_status" }
let(:cache_key) { described_class.cache_key_for_project(project) }
 
describe '.load_for_project' do
it "loads the status" do
Loading
Loading
Loading
Loading
@@ -14,6 +14,12 @@ describe Gitlab do
expect(described_class.com?).to eq true
end
 
it 'is true when on other gitlab subdomain' do
stub_config_setting(url: 'https://example.gitlab.com')
expect(described_class.com?).to eq true
end
it 'is false when not on GitLab.com' do
stub_config_setting(url: 'http://example.com')
 
Loading
Loading
Loading
Loading
@@ -5,7 +5,7 @@ describe InternalId do
let(:usage) { :issues }
let(:issue) { build(:issue, project: project) }
let(:scope) { { project: project } }
let(:init) { ->(s) { s.project.issues.size } }
let(:init) { ->(s) { s.project.issues.maximum(:iid) } }
 
context 'validations' do
it { is_expected.to validate_presence_of(:usage) }
Loading
Loading
@@ -39,6 +39,29 @@ describe InternalId do
end
end
 
context 'with an InternalId record present and existing issues with a higher internal id' do
# This can happen if the old NonatomicInternalId is still in use
before do
issues = Array.new(rand(1..10)).map { create(:issue, project: project) }
issue = issues.last
issue.iid = issues.map { |i| i.iid }.max + 1
issue.save
end
let(:maximum_iid) { project.issues.map { |i| i.iid }.max }
it 'updates last_value to the maximum internal id present' do
subject
expect(described_class.find_by(project: project, usage: described_class.usages[usage.to_s]).last_value).to eq(maximum_iid + 1)
end
it 'returns next internal id correctly' do
expect(subject).to eq(maximum_iid + 1)
end
end
context 'with concurrent inserts on table' do
it 'looks up the record if it was created concurrently' do
args = { **scope, usage: described_class.usages[usage.to_s] }
Loading
Loading
@@ -81,7 +104,8 @@ describe InternalId do
 
describe '#increment_and_save!' do
let(:id) { create(:internal_id) }
subject { id.increment_and_save! }
let(:maximum_iid) { nil }
subject { id.increment_and_save!(maximum_iid) }
 
it 'returns incremented iid' do
value = id.last_value
Loading
Loading
@@ -102,5 +126,14 @@ describe InternalId do
expect(subject).to eq(1)
end
end
context 'with maximum_iid given' do
let(:id) { create(:internal_id, last_value: 1) }
let(:maximum_iid) { id.last_value + 10 }
it 'returns maximum_iid instead' do
expect(subject).to eq(12)
end
end
end
end
Loading
Loading
@@ -406,7 +406,7 @@ describe API::Runner do
expect(json_response['image']).to eq({ 'name' => 'ruby:2.1', 'entrypoint' => '/bin/sh' })
expect(json_response['services']).to eq([{ 'name' => 'postgres', 'entrypoint' => nil,
'alias' => nil, 'command' => nil },
{ 'name' => 'docker:dind', 'entrypoint' => '/bin/sh',
{ 'name' => 'docker:stable-dind', 'entrypoint' => '/bin/sh',
'alias' => 'docker', 'command' => 'sleep 30' }])
expect(json_response['steps']).to eq(expected_steps)
expect(json_response['artifacts']).to eq(expected_artifacts)
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment