Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-01-30 00:09:22 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-01-30 00:09:22 +0300
commit27d314277bfe7fffec215efa9b1833a23bb82940 (patch)
tree898c606409718e70579beea62174624f84e28629 /spec
parent6b9d3a4e8351e662c4586b24bb152de78ae9e3bf (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/milestones/user_views_milestone_spec.rb31
-rw-r--r--spec/frontend/lib/utils/datetime_range_spec.js231
-rw-r--r--spec/javascripts/pipelines/header_component_spec.js55
-rw-r--r--spec/javascripts/vue_shared/components/header_ci_component_spec.js37
-rw-r--r--spec/migrations/add_deploy_token_type_to_deploy_tokens_spec.rb24
-rw-r--r--spec/models/concerns/milestoneish_spec.rb17
-rw-r--r--spec/models/deploy_token_spec.rb2
7 files changed, 316 insertions, 81 deletions
diff --git a/spec/features/milestones/user_views_milestone_spec.rb b/spec/features/milestones/user_views_milestone_spec.rb
index b1c2a87ef94..71abb195ad1 100644
--- a/spec/features/milestones/user_views_milestone_spec.rb
+++ b/spec/features/milestones/user_views_milestone_spec.rb
@@ -25,37 +25,6 @@ describe "User views milestone" do
expect { visit_milestone }.not_to exceed_query_limit(control)
end
- context 'limiting milestone issues' do
- before_all do
- 2.times do
- create(:issue, milestone: milestone, project: project)
- create(:issue, milestone: milestone, project: project, assignees: [user])
- create(:issue, milestone: milestone, project: project, state: :closed)
- end
- end
-
- context 'when issues on milestone are over DISPLAY_ISSUES_LIMIT' do
- it "limits issues to display and shows warning" do
- stub_const('Milestoneish::DISPLAY_ISSUES_LIMIT', 3)
-
- visit(project_milestone_path(project, milestone))
-
- expect(page).to have_selector('.issuable-row', count: 3)
- expect(page).to have_selector('#milestone-issue-count-warning', text: 'Showing 3 of 6 issues. View all issues')
- expect(page).to have_link('View all issues', href: project_issues_path(project))
- end
- end
-
- context 'when issues on milestone are below DISPLAY_ISSUES_LIMIT' do
- it 'does not display warning' do
- visit(project_milestone_path(project, milestone))
-
- expect(page).not_to have_selector('#milestone-issue-count-warning', text: 'Showing 3 of 6 issues. View all issues')
- expect(page).to have_selector('.issuable-row', count: 6)
- end
- end
- end
-
private
def visit_milestone
diff --git a/spec/frontend/lib/utils/datetime_range_spec.js b/spec/frontend/lib/utils/datetime_range_spec.js
new file mode 100644
index 00000000000..13eb69e1761
--- /dev/null
+++ b/spec/frontend/lib/utils/datetime_range_spec.js
@@ -0,0 +1,231 @@
+import _ from 'lodash';
+import { getRangeType, convertToFixedRange } from '~/lib/utils/datetime_range';
+
+const MOCK_NOW = Date.UTC(2020, 0, 23, 20);
+
+const MOCK_NOW_ISO_STRING = new Date(MOCK_NOW).toISOString();
+
+describe('Date time range utils', () => {
+ describe('getRangeType', () => {
+ it('infers correctly the range type from the input object', () => {
+ const rangeTypes = {
+ fixed: [{ start: MOCK_NOW_ISO_STRING, end: MOCK_NOW_ISO_STRING }],
+ anchored: [{ anchor: MOCK_NOW_ISO_STRING, duration: { seconds: 0 } }],
+ rolling: [{ duration: { seconds: 0 } }],
+ open: [{ anchor: MOCK_NOW_ISO_STRING }],
+ invalid: [
+ {},
+ { start: MOCK_NOW_ISO_STRING },
+ { end: MOCK_NOW_ISO_STRING },
+ { start: 'NOT_A_DATE', end: 'NOT_A_DATE' },
+ { duration: { seconds: 'NOT_A_NUMBER' } },
+ { duration: { seconds: Infinity } },
+ { duration: { minutes: 20 } },
+ { anchor: MOCK_NOW_ISO_STRING, duration: { seconds: 'NOT_A_NUMBER' } },
+ { anchor: MOCK_NOW_ISO_STRING, duration: { seconds: Infinity } },
+ { junk: 'exists' },
+ ],
+ };
+
+ Object.entries(rangeTypes).forEach(([type, examples]) => {
+ examples.forEach(example => expect(getRangeType(example)).toEqual(type));
+ });
+ });
+ });
+
+ describe('convertToFixedRange', () => {
+ beforeEach(() => {
+ jest.spyOn(Date, 'now').mockImplementation(() => MOCK_NOW);
+ });
+
+ afterEach(() => {
+ Date.now.mockRestore();
+ });
+
+ describe('When a fixed range is input', () => {
+ const defaultFixedRange = {
+ start: '2020-01-01T00:00:00.000Z',
+ end: '2020-01-31T23:59:00.000Z',
+ label: 'January 2020',
+ };
+
+ const mockFixedRange = params => ({ ...defaultFixedRange, ...params });
+
+ it('converts a fixed range to an equal fixed range', () => {
+ const aFixedRange = mockFixedRange();
+
+ expect(convertToFixedRange(aFixedRange)).toEqual({
+ start: defaultFixedRange.start,
+ end: defaultFixedRange.end,
+ });
+ });
+
+ it('throws an error when fixed range does not contain an end time', () => {
+ const aFixedRangeMissingEnd = _.omit(mockFixedRange(), 'end');
+
+ expect(() => convertToFixedRange(aFixedRangeMissingEnd)).toThrow();
+ });
+
+ it('throws an error when fixed range does not contain a start time', () => {
+ const aFixedRangeMissingStart = _.omit(mockFixedRange(), 'start');
+
+ expect(() => convertToFixedRange(aFixedRangeMissingStart)).toThrow();
+ });
+
+ it('throws an error when the dates cannot be parsed', () => {
+ const wrongStart = mockFixedRange({ start: 'I_CANNOT_BE_PARSED' });
+ const wrongEnd = mockFixedRange({ end: 'I_CANNOT_BE_PARSED' });
+
+ expect(() => convertToFixedRange(wrongStart)).toThrow();
+ expect(() => convertToFixedRange(wrongEnd)).toThrow();
+ });
+ });
+
+ describe('When an anchored range is input', () => {
+ const defaultAnchoredRange = {
+ anchor: '2020-01-01T00:00:00.000Z',
+ direction: 'after',
+ duration: {
+ seconds: 60 * 2,
+ },
+ label: 'First two minutes of 2020',
+ };
+ const mockAnchoredRange = params => ({ ...defaultAnchoredRange, ...params });
+
+ it('converts to a fixed range', () => {
+ const anAnchoredRange = mockAnchoredRange();
+
+ expect(convertToFixedRange(anAnchoredRange)).toEqual({
+ start: '2020-01-01T00:00:00.000Z',
+ end: '2020-01-01T00:02:00.000Z',
+ });
+ });
+
+ it('converts to a fixed range with a `before` direction', () => {
+ const anAnchoredRange = mockAnchoredRange({ direction: 'before' });
+
+ expect(convertToFixedRange(anAnchoredRange)).toEqual({
+ start: '2019-12-31T23:58:00.000Z',
+ end: '2020-01-01T00:00:00.000Z',
+ });
+ });
+
+ it('converts to a fixed range without an explicit direction, defaulting to `before`', () => {
+ const anAnchoredRange = _.omit(mockAnchoredRange(), 'direction');
+
+ expect(convertToFixedRange(anAnchoredRange)).toEqual({
+ start: '2019-12-31T23:58:00.000Z',
+ end: '2020-01-01T00:00:00.000Z',
+ });
+ });
+
+ it('throws an error when the anchor cannot be parsed', () => {
+ const wrongAnchor = mockAnchoredRange({ anchor: 'I_CANNOT_BE_PARSED' });
+ expect(() => convertToFixedRange(wrongAnchor)).toThrow();
+ });
+ });
+
+ describe('when a rolling range is input', () => {
+ it('converts to a fixed range', () => {
+ const aRollingRange = {
+ direction: 'after',
+ duration: {
+ seconds: 60 * 2,
+ },
+ label: 'Next 2 minutes',
+ };
+
+ expect(convertToFixedRange(aRollingRange)).toEqual({
+ start: '2020-01-23T20:00:00.000Z',
+ end: '2020-01-23T20:02:00.000Z',
+ });
+ });
+
+ it('converts to a fixed range with an implicit `before` direction', () => {
+ const aRollingRangeWithNoDirection = {
+ duration: {
+ seconds: 60 * 2,
+ },
+ label: 'Last 2 minutes',
+ };
+
+ expect(convertToFixedRange(aRollingRangeWithNoDirection)).toEqual({
+ start: '2020-01-23T19:58:00.000Z',
+ end: '2020-01-23T20:00:00.000Z',
+ });
+ });
+
+ it('throws an error when the duration is not in the right format', () => {
+ const wrongDuration = {
+ direction: 'before',
+ duration: {
+ minutes: 20,
+ },
+ label: 'Last 20 minutes',
+ };
+
+ expect(() => convertToFixedRange(wrongDuration)).toThrow();
+ });
+
+ it('throws an error when the anchor is not valid', () => {
+ const wrongAnchor = {
+ anchor: 'CAN_T_PARSE_THIS',
+ direction: 'after',
+ label: '2020 so far',
+ };
+
+ expect(() => convertToFixedRange(wrongAnchor)).toThrow();
+ });
+ });
+
+ describe('when an open range is input', () => {
+ it('converts to a fixed range with an `after` direction', () => {
+ const soFar2020 = {
+ anchor: '2020-01-01T00:00:00.000Z',
+ direction: 'after',
+ label: '2020 so far',
+ };
+
+ expect(convertToFixedRange(soFar2020)).toEqual({
+ start: '2020-01-01T00:00:00.000Z',
+ end: '2020-01-23T20:00:00.000Z',
+ });
+ });
+
+ it('converts to a fixed range with the explicit `before` direction', () => {
+ const before2020 = {
+ anchor: '2020-01-01T00:00:00.000Z',
+ direction: 'before',
+ label: 'Before 2020',
+ };
+
+ expect(convertToFixedRange(before2020)).toEqual({
+ start: '1970-01-01T00:00:00.000Z',
+ end: '2020-01-01T00:00:00.000Z',
+ });
+ });
+
+ it('converts to a fixed range with the implicit `before` direction', () => {
+ const alsoBefore2020 = {
+ anchor: '2020-01-01T00:00:00.000Z',
+ label: 'Before 2020',
+ };
+
+ expect(convertToFixedRange(alsoBefore2020)).toEqual({
+ start: '1970-01-01T00:00:00.000Z',
+ end: '2020-01-01T00:00:00.000Z',
+ });
+ });
+
+ it('throws an error when the anchor cannot be parsed', () => {
+ const wrongAnchor = {
+ anchor: 'CAN_T_PARSE_THIS',
+ direction: 'after',
+ label: '2020 so far',
+ };
+
+ expect(() => convertToFixedRange(wrongAnchor)).toThrow();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/pipelines/header_component_spec.js b/spec/javascripts/pipelines/header_component_spec.js
index 8c033447ce4..9043f30397d 100644
--- a/spec/javascripts/pipelines/header_component_spec.js
+++ b/spec/javascripts/pipelines/header_component_spec.js
@@ -8,6 +8,7 @@ describe('Pipeline details header', () => {
let props;
beforeEach(() => {
+ spyOn(eventHub, '$emit');
HeaderComponent = Vue.extend(headerComponent);
const threeWeeksAgo = new Date();
@@ -33,8 +34,9 @@ describe('Pipeline details header', () => {
email: 'foo@bar.com',
avatar_url: 'link',
},
- retry_path: 'path',
- delete_path: 'path',
+ retry_path: 'retry',
+ cancel_path: 'cancel',
+ delete_path: 'delete',
},
isLoading: false,
};
@@ -43,9 +45,14 @@ describe('Pipeline details header', () => {
});
afterEach(() => {
+ eventHub.$off();
vm.$destroy();
});
+ const findDeleteModal = () => document.getElementById(headerComponent.DELETE_MODAL_ID);
+ const findDeleteModalSubmit = () =>
+ [...findDeleteModal().querySelectorAll('.btn')].find(x => x.textContent === 'Delete pipeline');
+
it('should render provided pipeline info', () => {
expect(
vm.$el
@@ -56,22 +63,46 @@ describe('Pipeline details header', () => {
});
describe('action buttons', () => {
- it('should call postAction when retry button action is clicked', done => {
- eventHub.$on('headerPostAction', action => {
- expect(action.path).toEqual('path');
- done();
- });
+ it('should not trigger eventHub when nothing happens', () => {
+ expect(eventHub.$emit).not.toHaveBeenCalled();
+ });
+ it('should call postAction when retry button action is clicked', () => {
vm.$el.querySelector('.js-retry-button').click();
+
+ expect(eventHub.$emit).toHaveBeenCalledWith('headerPostAction', 'retry');
+ });
+
+ it('should call postAction when cancel button action is clicked', () => {
+ vm.$el.querySelector('.js-btn-cancel-pipeline').click();
+
+ expect(eventHub.$emit).toHaveBeenCalledWith('headerPostAction', 'cancel');
});
- it('should fire modal event when delete button action is clicked', done => {
- vm.$root.$on('bv::modal::show', action => {
- expect(action.componentId).toEqual('pipeline-delete-modal');
- done();
+ it('does not show delete modal', () => {
+ expect(findDeleteModal()).not.toBeVisible();
+ });
+
+ describe('when delete button action is clicked', () => {
+ beforeEach(done => {
+ vm.$el.querySelector('.js-btn-delete-pipeline').click();
+
+ // Modal needs two ticks to show
+ vm.$nextTick()
+ .then(() => vm.$nextTick())
+ .then(done)
+ .catch(done.fail);
});
- vm.$el.querySelector('.js-btn-delete-pipeline').click();
+ it('should show delete modal', () => {
+ expect(findDeleteModal()).toBeVisible();
+ });
+
+ it('should call delete when modal is submitted', () => {
+ findDeleteModalSubmit().click();
+
+ expect(eventHub.$emit).toHaveBeenCalledWith('headerDeleteAction', 'delete');
+ });
});
});
});
diff --git a/spec/javascripts/vue_shared/components/header_ci_component_spec.js b/spec/javascripts/vue_shared/components/header_ci_component_spec.js
index ea2eed2886a..b1abc972e1d 100644
--- a/spec/javascripts/vue_shared/components/header_ci_component_spec.js
+++ b/spec/javascripts/vue_shared/components/header_ci_component_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import mountComponent, { mountComponentWithSlots } from 'spec/helpers/vue_mount_component_helper';
import headerCi from '~/vue_shared/components/header_ci_component.vue';
describe('Header CI Component', () => {
@@ -27,14 +27,6 @@ describe('Header CI Component', () => {
email: 'foo@bar.com',
avatar_url: 'link',
},
- actions: [
- {
- label: 'Retry',
- path: 'path',
- cssClass: 'btn',
- isLoading: false,
- },
- ],
hasSidebarButton: true,
};
});
@@ -43,6 +35,8 @@ describe('Header CI Component', () => {
vm.$destroy();
});
+ const findActionButtons = () => vm.$el.querySelector('.header-action-buttons');
+
describe('render', () => {
beforeEach(() => {
vm = mountComponent(HeaderCi, props);
@@ -68,24 +62,23 @@ describe('Header CI Component', () => {
expect(vm.$el.querySelector('.js-user-link').innerText.trim()).toContain(props.user.name);
});
- it('should render provided actions', () => {
- const btn = vm.$el.querySelector('.btn');
+ it('should render sidebar toggle button', () => {
+ expect(vm.$el.querySelector('.js-sidebar-build-toggle')).not.toBeNull();
+ });
- expect(btn.tagName).toEqual('BUTTON');
- expect(btn.textContent.trim()).toEqual(props.actions[0].label);
+ it('should not render header action buttons when empty', () => {
+ expect(findActionButtons()).toBeNull();
});
+ });
- it('should show loading icon', done => {
- vm.actions[0].isLoading = true;
+ describe('slot', () => {
+ it('should render header action buttons', () => {
+ vm = mountComponentWithSlots(HeaderCi, { props, slots: { default: 'Test Actions' } });
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.btn .gl-spinner').getAttribute('style')).toBeFalsy();
- done();
- });
- });
+ const buttons = findActionButtons();
- it('should render sidebar toggle button', () => {
- expect(vm.$el.querySelector('.js-sidebar-build-toggle')).not.toBeNull();
+ expect(buttons).not.toBeNull();
+ expect(buttons.textContent).toEqual('Test Actions');
});
});
diff --git a/spec/migrations/add_deploy_token_type_to_deploy_tokens_spec.rb b/spec/migrations/add_deploy_token_type_to_deploy_tokens_spec.rb
new file mode 100644
index 00000000000..fb8213a6bd6
--- /dev/null
+++ b/spec/migrations/add_deploy_token_type_to_deploy_tokens_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20200122161638_add_deploy_token_type_to_deploy_tokens.rb')
+
+describe AddDeployTokenTypeToDeployTokens, :migration do
+ let(:deploy_tokens) { table(:deploy_tokens) }
+ let(:deploy_token) do
+ deploy_tokens.create(name: 'token_test',
+ username: 'gitlab+deploy-token-1',
+ token_encrypted: 'dr8rPXwM+Mbs2p3Bg1+gpnXqrnH/wu6vaHdcc7A3isPR67WB',
+ read_repository: true,
+ expires_at: Time.now + 1.year)
+ end
+
+ it 'updates the deploy_token_type column to 2' do
+ expect(deploy_token).not_to respond_to(:deploy_token_type)
+
+ migrate!
+
+ deploy_token.reload
+ expect(deploy_token.deploy_token_type).to eq(2)
+ end
+end
diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb
index e39cbedde68..d46c9747845 100644
--- a/spec/models/concerns/milestoneish_spec.rb
+++ b/spec/models/concerns/milestoneish_spec.rb
@@ -33,32 +33,17 @@ describe Milestone, 'Milestoneish' do
end
describe '#sorted_issues' do
- before do
+ it 'sorts issues by label priority' do
issue.labels << label_1
security_issue_1.labels << label_2
closed_issue_1.labels << label_3
- end
- it 'sorts issues by label priority' do
issues = milestone.sorted_issues(member)
expect(issues.first).to eq(issue)
expect(issues.second).to eq(security_issue_1)
expect(issues.third).not_to eq(closed_issue_1)
end
-
- it 'limits issue count' do
- stub_const('Milestoneish::DISPLAY_ISSUES_LIMIT', 4)
-
- issues = milestone.sorted_issues(member)
-
- # Cannot use issues.count here because it is sorting
- # by a virtual column 'highest_priority' and it will break
- # the query.
- total_issues_count = issues.opened.unassigned.length + issues.opened.assigned.length + issues.closed.length
- expect(issues.length).to eq(4)
- expect(total_issues_count).to eq(4)
- end
end
context 'attributes visibility' do
diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb
index 8d951ab6f0f..5c14d57cf18 100644
--- a/spec/models/deploy_token_spec.rb
+++ b/spec/models/deploy_token_spec.rb
@@ -8,6 +8,8 @@ describe DeployToken do
it { is_expected.to have_many :project_deploy_tokens }
it { is_expected.to have_many(:projects).through(:project_deploy_tokens) }
+ it_behaves_like 'having unique enum values'
+
describe 'validations' do
let(:username_format_message) { "can contain only letters, digits, '_', '-', '+', and '.'" }