summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb10
-rw-r--r--spec/controllers/concerns/lfs_request_spec.rb2
-rw-r--r--spec/controllers/profiles/keys_controller_spec.rb7
-rw-r--r--spec/factories/clusters/applications/helm.rb5
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb1
-rw-r--r--spec/features/help_pages_spec.rb2
-rw-r--r--spec/features/issues_spec.rb15
-rw-r--r--spec/features/merge_request/user_sees_empty_state_spec.rb12
-rw-r--r--spec/finders/issues_finder_spec.rb36
-rw-r--r--spec/javascripts/clusters/components/applications_spec.js12
-rw-r--r--spec/javascripts/clusters/services/mock_data.js11
-rw-r--r--spec/javascripts/clusters/stores/clusters_store_spec.js7
-rw-r--r--spec/javascripts/helpers/scroll_into_view_promise.js28
-rw-r--r--spec/javascripts/helpers/wait_for_attribute_change.js16
-rw-r--r--spec/javascripts/lazy_loader_spec.js174
-rw-r--r--spec/javascripts/notes/components/noteable_discussion_spec.js44
-rw-r--r--spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb94
-rw-r--r--spec/lib/gitlab/ci/variables/collection/item_spec.rb4
-rw-r--r--spec/lib/gitlab/kubernetes/helm/install_command_spec.rb8
-rw-r--r--spec/lib/gitlab/prometheus/query_variables_spec.rb33
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb3
-rw-r--r--spec/models/clusters/applications/cert_manager_spec.rb79
-rw-r--r--spec/models/clusters/applications/runner_spec.rb6
-rw-r--r--spec/models/clusters/cluster_spec.rb3
-rw-r--r--spec/models/concerns/relative_positioning_spec.rb8
-rw-r--r--spec/requests/api/files_spec.rb8
-rw-r--r--spec/requests/api/issues_spec.rb115
-rw-r--r--spec/requests/api/repositories_spec.rb6
-rw-r--r--spec/requests/api/snippets_spec.rb6
-rw-r--r--spec/support/shared_examples/requests/api/merge_requests_list.rb17
-rw-r--r--spec/tasks/cache/clear/redis_spec.rb5
31 files changed, 569 insertions, 208 deletions
diff --git a/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb b/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb
index d20471ef603..3c9452cc42a 100644
--- a/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb
+++ b/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb
@@ -27,11 +27,11 @@ describe ControllerWithCrossProjectAccessCheck do
if: -> { if_condition }
def index
- render nothing: true
+ head :ok
end
def show
- render nothing: true
+ head :ok
end
def unless_condition
@@ -88,15 +88,15 @@ describe ControllerWithCrossProjectAccessCheck do
if: -> { if_condition }
def index
- render nothing: true
+ head :ok
end
def show
- render nothing: true
+ head :ok
end
def edit
- render nothing: true
+ head :ok
end
def unless_condition
diff --git a/spec/controllers/concerns/lfs_request_spec.rb b/spec/controllers/concerns/lfs_request_spec.rb
index 33b23db302a..76c878ec5d7 100644
--- a/spec/controllers/concerns/lfs_request_spec.rb
+++ b/spec/controllers/concerns/lfs_request_spec.rb
@@ -10,7 +10,7 @@ describe LfsRequest do
def show
storage_project
- render nothing: true
+ head :ok
end
def project
diff --git a/spec/controllers/profiles/keys_controller_spec.rb b/spec/controllers/profiles/keys_controller_spec.rb
index ea26bc83353..685db8488f0 100644
--- a/spec/controllers/profiles/keys_controller_spec.rb
+++ b/spec/controllers/profiles/keys_controller_spec.rb
@@ -62,8 +62,15 @@ describe Profiles::KeysController do
it "responds with text/plain content type" do
get :get_keys, username: user.username
+
expect(response.content_type).to eq("text/plain")
end
+
+ it "responds with attachment content disposition" do
+ get :get_keys, username: user.username
+
+ expect(response.headers['Content-Disposition']).to eq('attachment')
+ end
end
end
end
diff --git a/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb
index ff65c76cf26..7fc3d16e864 100644
--- a/spec/factories/clusters/applications/helm.rb
+++ b/spec/factories/clusters/applications/helm.rb
@@ -49,6 +49,11 @@ FactoryBot.define do
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
end
+ factory :clusters_applications_cert_managers, class: Clusters::Applications::CertManager do
+ email 'admin@example.com'
+ cluster factory: %i(cluster with_installed_helm provided_by_gcp)
+ end
+
factory :clusters_applications_prometheus, class: Clusters::Applications::Prometheus do
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
end
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index 282bf542e77..9ffa75aee47 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -6,6 +6,7 @@ describe 'Dashboard Merge Requests' do
include ProjectForksHelper
let(:current_user) { create :user }
+ let(:user) { current_user }
let(:project) { create(:project) }
let(:public_project) { create(:project, :public, :repository) }
diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb
index 0d04ed612c2..c29dfb01381 100644
--- a/spec/features/help_pages_spec.rb
+++ b/spec/features/help_pages_spec.rb
@@ -4,7 +4,7 @@ describe 'Help Pages' do
describe 'Get the main help page' do
shared_examples_for 'help page' do |prefix: ''|
it 'prefixes links correctly' do
- expect(page).to have_selector(%(div.documentation-index > ul a[href="#{prefix}/help/api/README.md"]))
+ expect(page).to have_selector(%(div.documentation-index > table tbody tr td a[href="#{prefix}/help/api/README.md"]))
end
end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 4d9b8a10e04..5c1ffb76351 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -8,6 +8,17 @@ describe 'Issues' do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
+ shared_examples_for 'empty state with filters' do
+ it 'user sees empty state with filters' do
+ create(:issue, author: user, project: project)
+
+ visit project_issues_path(project, milestone_title: "1.0")
+
+ expect(page).to have_content('Sorry, your filter produced no results')
+ expect(page).to have_content('To widen your search, change or remove filters above')
+ end
+ end
+
describe 'while user is signed out' do
describe 'empty state' do
it 'user sees empty state' do
@@ -17,6 +28,8 @@ describe 'Issues' do
expect(page).to have_content('The Issue Tracker is the place to add things that need to be improved or solved in a project.')
expect(page).to have_content('You can register or sign in to create issues for this project.')
end
+
+ it_behaves_like 'empty state with filters'
end
end
@@ -37,6 +50,8 @@ describe 'Issues' do
expect(page).to have_content('Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.')
expect(page).to have_content('New issue')
end
+
+ it_behaves_like 'empty state with filters'
end
describe 'Edit issue' do
diff --git a/spec/features/merge_request/user_sees_empty_state_spec.rb b/spec/features/merge_request/user_sees_empty_state_spec.rb
index 482f31b02d4..012bfd6e458 100644
--- a/spec/features/merge_request/user_sees_empty_state_spec.rb
+++ b/spec/features/merge_request/user_sees_empty_state_spec.rb
@@ -19,12 +19,20 @@ describe 'Merge request > User sees empty state' do
context 'if there are merge requests' do
before do
create(:merge_request, source_project: project)
-
- visit project_merge_requests_path(project)
end
it 'does not show an empty state' do
+ visit project_merge_requests_path(project)
+
expect(page).not_to have_selector('.empty-state')
end
+
+ it 'shows empty state when filter results empty' do
+ visit project_merge_requests_path(project, milestone_title: "1.0")
+
+ expect(page).to have_selector('.empty-state')
+ expect(page).to have_content('Sorry, your filter produced no results')
+ expect(page).to have_content('To widen your search, change or remove filters above')
+ end
end
end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index c0488c83bd8..515f6f70b99 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -256,19 +256,51 @@ describe IssuesFinder do
create(:label_link, label: label2, target: issue2)
end
- it 'returns the unique issues with any of those labels' do
+ it 'returns the unique issues with all those labels' do
+ expect(issues).to contain_exactly(issue2)
+ end
+ end
+
+ context 'filtering by a label that includes any or none in the title' do
+ let(:params) { { label_name: [label.title, label2.title].join(',') } }
+ let(:label) { create(:label, title: 'any foo', project: project2) }
+ let(:label2) { create(:label, title: 'bar none', project: project2) }
+
+ it 'returns the unique issues with all those labels' do
+ create(:label_link, label: label2, target: issue2)
+
expect(issues).to contain_exactly(issue2)
end
end
context 'filtering by no label' do
- let(:params) { { label_name: Label::None.title } }
+ let(:params) { { label_name: described_class::FILTER_NONE } }
it 'returns issues with no labels' do
expect(issues).to contain_exactly(issue1, issue3, issue4)
end
end
+ context 'filtering by legacy No+Label' do
+ let(:params) { { label_name: Label::NONE } }
+
+ it 'returns issues with no labels' do
+ expect(issues).to contain_exactly(issue1, issue3, issue4)
+ end
+ end
+
+ context 'filtering by any label' do
+ let(:params) { { label_name: described_class::FILTER_ANY } }
+
+ it 'returns issues that have one or more label' do
+ 2.times do
+ create(:label_link, label: create(:label, project: project2), target: issue3)
+ end
+
+ expect(issues).to contain_exactly(issue2, issue3)
+ end
+ end
+
context 'filtering by issue term' do
let(:params) { { search: 'git' } }
diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js
index 0e2cc13fa52..928bf70f3a2 100644
--- a/spec/javascripts/clusters/components/applications_spec.js
+++ b/spec/javascripts/clusters/components/applications_spec.js
@@ -20,6 +20,7 @@ describe('Applications', () => {
applications: {
helm: { title: 'Helm Tiller' },
ingress: { title: 'Ingress' },
+ cert_manager: { title: 'Cert-Manager' },
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub' },
@@ -36,6 +37,10 @@ describe('Applications', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-ingress')).toBeDefined();
});
+ it('renders a row for Cert-Manager', () => {
+ expect(vm.$el.querySelector('.js-cluster-application-row-cert_manager')).toBeDefined();
+ });
+
it('renders a row for Prometheus', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-prometheus')).toBeDefined();
});
@@ -65,6 +70,7 @@ describe('Applications', () => {
externalIp: '0.0.0.0',
},
helm: { title: 'Helm Tiller' },
+ cert_manager: { title: 'Cert-Manager' },
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '' },
@@ -89,6 +95,7 @@ describe('Applications', () => {
status: 'installed',
},
helm: { title: 'Helm Tiller' },
+ cert_manager: { title: 'Cert-Manager' },
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '' },
@@ -109,6 +116,7 @@ describe('Applications', () => {
applications: {
helm: { title: 'Helm Tiller' },
ingress: { title: 'Ingress' },
+ cert_manager: { title: 'Cert-Manager' },
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '' },
@@ -128,6 +136,7 @@ describe('Applications', () => {
applications: {
helm: { title: 'Helm Tiller', status: 'installed' },
ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' },
+ cert_manager: { title: 'Cert-Manager' },
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' },
@@ -145,6 +154,7 @@ describe('Applications', () => {
applications: {
helm: { title: 'Helm Tiller', status: 'installed' },
ingress: { title: 'Ingress', status: 'installed' },
+ cert_manager: { title: 'Cert-Manager' },
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' },
@@ -162,6 +172,7 @@ describe('Applications', () => {
applications: {
helm: { title: 'Helm Tiller', status: 'installed' },
ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' },
+ cert_manager: { title: 'Cert-Manager' },
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', status: 'installed', hostname: '' },
@@ -179,6 +190,7 @@ describe('Applications', () => {
applications: {
helm: { title: 'Helm Tiller' },
ingress: { title: 'Ingress' },
+ cert_manager: { title: 'Cert-Manager' },
runner: { title: 'GitLab Runner' },
prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', status: 'not_installable' },
diff --git a/spec/javascripts/clusters/services/mock_data.js b/spec/javascripts/clusters/services/mock_data.js
index 73abf6504c0..540d7f30858 100644
--- a/spec/javascripts/clusters/services/mock_data.js
+++ b/spec/javascripts/clusters/services/mock_data.js
@@ -38,6 +38,11 @@ const CLUSTERS_MOCK_DATA = {
status: APPLICATION_STATUS.INSTALLING,
status_reason: 'Cannot connect',
},
+ {
+ name: 'cert_manager',
+ status: APPLICATION_STATUS.ERROR,
+ status_reason: 'Cannot connect',
+ },
],
},
},
@@ -77,6 +82,11 @@ const CLUSTERS_MOCK_DATA = {
status: APPLICATION_STATUS.INSTALLABLE,
status_reason: 'Cannot connect',
},
+ {
+ name: 'cert_manager',
+ status: APPLICATION_STATUS.ERROR,
+ status_reason: 'Cannot connect',
+ },
],
},
},
@@ -84,6 +94,7 @@ const CLUSTERS_MOCK_DATA = {
POST: {
'/gitlab-org/gitlab-shell/clusters/1/applications/helm': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/ingress': {},
+ '/gitlab-org/gitlab-shell/clusters/1/applications/cert_manager': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/runner': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/jupyter': {},
diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js
index 34ed36afa5b..6a08d08f33e 100644
--- a/spec/javascripts/clusters/stores/clusters_store_spec.js
+++ b/spec/javascripts/clusters/stores/clusters_store_spec.js
@@ -108,6 +108,13 @@ describe('Clusters Store', () => {
requestReason: null,
hostname: null,
},
+ cert_manager: {
+ title: 'Cert-Manager',
+ status: mockResponseData.applications[6].status,
+ statusReason: mockResponseData.applications[6].status_reason,
+ requestStatus: null,
+ requestReason: null,
+ },
},
});
});
diff --git a/spec/javascripts/helpers/scroll_into_view_promise.js b/spec/javascripts/helpers/scroll_into_view_promise.js
new file mode 100644
index 00000000000..0edea2103da
--- /dev/null
+++ b/spec/javascripts/helpers/scroll_into_view_promise.js
@@ -0,0 +1,28 @@
+export default function scrollIntoViewPromise(intersectionTarget, timeout = 100, maxTries = 5) {
+ return new Promise((resolve, reject) => {
+ let intersectionObserver;
+ let retry = 0;
+
+ const intervalId = setInterval(() => {
+ if (retry >= maxTries) {
+ intersectionObserver.disconnect();
+ clearInterval(intervalId);
+ reject(new Error(`Could not scroll target into viewPort within ${timeout * maxTries} ms`));
+ }
+ retry += 1;
+ intersectionTarget.scrollIntoView();
+ }, timeout);
+
+ intersectionObserver = new IntersectionObserver(entries => {
+ if (entries[0].isIntersecting) {
+ intersectionObserver.disconnect();
+ clearInterval(intervalId);
+ resolve();
+ }
+ });
+
+ intersectionObserver.observe(intersectionTarget);
+
+ intersectionTarget.scrollIntoView();
+ });
+}
diff --git a/spec/javascripts/helpers/wait_for_attribute_change.js b/spec/javascripts/helpers/wait_for_attribute_change.js
new file mode 100644
index 00000000000..8f22d569222
--- /dev/null
+++ b/spec/javascripts/helpers/wait_for_attribute_change.js
@@ -0,0 +1,16 @@
+export default (domElement, attributes, timeout = 1500) =>
+ new Promise((resolve, reject) => {
+ let observer;
+ const timeoutId = setTimeout(() => {
+ observer.disconnect();
+ reject(new Error(`Could not see an attribute update within ${timeout} ms`));
+ }, timeout);
+
+ observer = new MutationObserver(() => {
+ clearTimeout(timeoutId);
+ observer.disconnect();
+ resolve();
+ });
+
+ observer.observe(domElement, { attributes: true, attributeFilter: attributes });
+ });
diff --git a/spec/javascripts/lazy_loader_spec.js b/spec/javascripts/lazy_loader_spec.js
index eac4756e8a9..cbdc1644430 100644
--- a/spec/javascripts/lazy_loader_spec.js
+++ b/spec/javascripts/lazy_loader_spec.js
@@ -1,16 +1,19 @@
import LazyLoader from '~/lazy_loader';
import { TEST_HOST } from './test_constants';
-
-let lazyLoader = null;
+import scrollIntoViewPromise from './helpers/scroll_into_view_promise';
+import waitForPromises from './helpers/wait_for_promises';
+import waitForAttributeChange from './helpers/wait_for_attribute_change';
const execImmediately = callback => {
callback();
};
describe('LazyLoader', function() {
+ let lazyLoader = null;
+
preloadFixtures('issues/issue_with_comment.html.raw');
- describe('with IntersectionObserver disabled', () => {
+ describe('without IntersectionObserver', () => {
beforeEach(function() {
loadFixtures('issues/issue_with_comment.html.raw');
@@ -36,14 +39,15 @@ describe('LazyLoader', function() {
it('should copy value from data-src to src for img 1', function(done) {
const img = document.querySelectorAll('img[data-src]')[0];
const originalDataSrc = img.getAttribute('data-src');
- img.scrollIntoView();
-
- setTimeout(() => {
- expect(LazyLoader.loadImage).toHaveBeenCalled();
- expect(img.getAttribute('src')).toBe(originalDataSrc);
- expect(img).toHaveClass('js-lazy-loaded');
- done();
- }, 50);
+
+ Promise.all([scrollIntoViewPromise(img), waitForAttributeChange(img, ['data-src', 'src'])])
+ .then(() => {
+ expect(LazyLoader.loadImage).toHaveBeenCalled();
+ expect(img.getAttribute('src')).toBe(originalDataSrc);
+ expect(img).toHaveClass('js-lazy-loaded');
+ done();
+ })
+ .catch(done.fail);
});
it('should lazy load dynamically added data-src images', function(done) {
@@ -52,14 +56,18 @@ describe('LazyLoader', function() {
newImg.className = 'lazy';
newImg.setAttribute('data-src', testPath);
document.body.appendChild(newImg);
- newImg.scrollIntoView();
-
- setTimeout(() => {
- expect(LazyLoader.loadImage).toHaveBeenCalled();
- expect(newImg.getAttribute('src')).toBe(testPath);
- expect(newImg).toHaveClass('js-lazy-loaded');
- done();
- }, 50);
+
+ Promise.all([
+ scrollIntoViewPromise(newImg),
+ waitForAttributeChange(newImg, ['data-src', 'src']),
+ ])
+ .then(() => {
+ expect(LazyLoader.loadImage).toHaveBeenCalled();
+ expect(newImg.getAttribute('src')).toBe(testPath);
+ expect(newImg).toHaveClass('js-lazy-loaded');
+ done();
+ })
+ .catch(done.fail);
});
it('should not alter normal images', function(done) {
@@ -67,13 +75,15 @@ describe('LazyLoader', function() {
const testPath = `${TEST_HOST}/img/testimg.png`;
newImg.setAttribute('src', testPath);
document.body.appendChild(newImg);
- newImg.scrollIntoView();
- setTimeout(() => {
- expect(LazyLoader.loadImage).not.toHaveBeenCalled();
- expect(newImg).not.toHaveClass('js-lazy-loaded');
- done();
- }, 50);
+ scrollIntoViewPromise(newImg)
+ .then(waitForPromises)
+ .then(() => {
+ expect(LazyLoader.loadImage).not.toHaveBeenCalled();
+ expect(newImg).not.toHaveClass('js-lazy-loaded');
+ done();
+ })
+ .catch(done.fail);
});
it('should not load dynamically added pictures if content observer is turned off', done => {
@@ -84,13 +94,15 @@ describe('LazyLoader', function() {
newImg.className = 'lazy';
newImg.setAttribute('data-src', testPath);
document.body.appendChild(newImg);
- newImg.scrollIntoView();
- setTimeout(() => {
- expect(LazyLoader.loadImage).not.toHaveBeenCalled();
- expect(newImg).not.toHaveClass('js-lazy-loaded');
- done();
- }, 50);
+ scrollIntoViewPromise(newImg)
+ .then(waitForPromises)
+ .then(() => {
+ expect(LazyLoader.loadImage).not.toHaveBeenCalled();
+ expect(newImg).not.toHaveClass('js-lazy-loaded');
+ done();
+ })
+ .catch(done.fail);
});
it('should load dynamically added pictures if content observer is turned off and on again', done => {
@@ -102,17 +114,22 @@ describe('LazyLoader', function() {
newImg.className = 'lazy';
newImg.setAttribute('data-src', testPath);
document.body.appendChild(newImg);
- newImg.scrollIntoView();
- setTimeout(() => {
- expect(LazyLoader.loadImage).toHaveBeenCalled();
- expect(newImg).toHaveClass('js-lazy-loaded');
- done();
- }, 50);
+ Promise.all([
+ scrollIntoViewPromise(newImg),
+ waitForAttributeChange(newImg, ['data-src', 'src']),
+ ])
+ .then(waitForPromises)
+ .then(() => {
+ expect(LazyLoader.loadImage).toHaveBeenCalled();
+ expect(newImg).toHaveClass('js-lazy-loaded');
+ done();
+ })
+ .catch(done.fail);
});
});
- describe('with IntersectionObserver enabled', () => {
+ describe('with IntersectionObserver', () => {
beforeEach(function() {
loadFixtures('issues/issue_with_comment.html.raw');
@@ -136,14 +153,15 @@ describe('LazyLoader', function() {
it('should copy value from data-src to src for img 1', function(done) {
const img = document.querySelectorAll('img[data-src]')[0];
const originalDataSrc = img.getAttribute('data-src');
- img.scrollIntoView();
-
- setTimeout(() => {
- expect(LazyLoader.loadImage).toHaveBeenCalled();
- expect(img.getAttribute('src')).toBe(originalDataSrc);
- expect(img).toHaveClass('js-lazy-loaded');
- done();
- }, 50);
+
+ Promise.all([scrollIntoViewPromise(img), waitForAttributeChange(img, ['data-src', 'src'])])
+ .then(() => {
+ expect(LazyLoader.loadImage).toHaveBeenCalled();
+ expect(img.getAttribute('src')).toBe(originalDataSrc);
+ expect(img).toHaveClass('js-lazy-loaded');
+ done();
+ })
+ .catch(done.fail);
});
it('should lazy load dynamically added data-src images', function(done) {
@@ -152,14 +170,18 @@ describe('LazyLoader', function() {
newImg.className = 'lazy';
newImg.setAttribute('data-src', testPath);
document.body.appendChild(newImg);
- newImg.scrollIntoView();
-
- setTimeout(() => {
- expect(LazyLoader.loadImage).toHaveBeenCalled();
- expect(newImg.getAttribute('src')).toBe(testPath);
- expect(newImg).toHaveClass('js-lazy-loaded');
- done();
- }, 50);
+
+ Promise.all([
+ scrollIntoViewPromise(newImg),
+ waitForAttributeChange(newImg, ['data-src', 'src']),
+ ])
+ .then(() => {
+ expect(LazyLoader.loadImage).toHaveBeenCalled();
+ expect(newImg.getAttribute('src')).toBe(testPath);
+ expect(newImg).toHaveClass('js-lazy-loaded');
+ done();
+ })
+ .catch(done.fail);
});
it('should not alter normal images', function(done) {
@@ -167,13 +189,15 @@ describe('LazyLoader', function() {
const testPath = `${TEST_HOST}/img/testimg.png`;
newImg.setAttribute('src', testPath);
document.body.appendChild(newImg);
- newImg.scrollIntoView();
- setTimeout(() => {
- expect(LazyLoader.loadImage).not.toHaveBeenCalled();
- expect(newImg).not.toHaveClass('js-lazy-loaded');
- done();
- }, 50);
+ scrollIntoViewPromise(newImg)
+ .then(waitForPromises)
+ .then(() => {
+ expect(LazyLoader.loadImage).not.toHaveBeenCalled();
+ expect(newImg).not.toHaveClass('js-lazy-loaded');
+ done();
+ })
+ .catch(done.fail);
});
it('should not load dynamically added pictures if content observer is turned off', done => {
@@ -184,13 +208,15 @@ describe('LazyLoader', function() {
newImg.className = 'lazy';
newImg.setAttribute('data-src', testPath);
document.body.appendChild(newImg);
- newImg.scrollIntoView();
- setTimeout(() => {
- expect(LazyLoader.loadImage).not.toHaveBeenCalled();
- expect(newImg).not.toHaveClass('js-lazy-loaded');
- done();
- }, 50);
+ scrollIntoViewPromise(newImg)
+ .then(waitForPromises)
+ .then(() => {
+ expect(LazyLoader.loadImage).not.toHaveBeenCalled();
+ expect(newImg).not.toHaveClass('js-lazy-loaded');
+ done();
+ })
+ .catch(done.fail);
});
it('should load dynamically added pictures if content observer is turned off and on again', done => {
@@ -202,13 +228,17 @@ describe('LazyLoader', function() {
newImg.className = 'lazy';
newImg.setAttribute('data-src', testPath);
document.body.appendChild(newImg);
- newImg.scrollIntoView();
- setTimeout(() => {
- expect(LazyLoader.loadImage).toHaveBeenCalled();
- expect(newImg).toHaveClass('js-lazy-loaded');
- done();
- }, 50);
+ Promise.all([
+ scrollIntoViewPromise(newImg),
+ waitForAttributeChange(newImg, ['data-src', 'src']),
+ ])
+ .then(() => {
+ expect(LazyLoader.loadImage).toHaveBeenCalled();
+ expect(newImg).toHaveClass('js-lazy-loaded');
+ done();
+ })
+ .catch(done.fail);
});
});
});
diff --git a/spec/javascripts/notes/components/noteable_discussion_spec.js b/spec/javascripts/notes/components/noteable_discussion_spec.js
index 81cb3e1f74d..4b4403689d9 100644
--- a/spec/javascripts/notes/components/noteable_discussion_spec.js
+++ b/spec/javascripts/notes/components/noteable_discussion_spec.js
@@ -6,6 +6,7 @@ import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data';
import mockDiffFile from '../../diffs/mock_data/diff_file';
const discussionWithTwoUnresolvedNotes = 'merge_requests/resolved_diff_discussion.json';
+const diffDiscussionFixture = 'merge_requests/diff_discussion.json';
describe('noteable_discussion component', () => {
const Component = Vue.extend(noteableDiscussion);
@@ -115,6 +116,49 @@ describe('noteable_discussion component', () => {
.catch(done.fail);
});
});
+
+ describe('isRepliesCollapsed', () => {
+ it('should return false for diff discussions', done => {
+ const diffDiscussion = getJSONFixture(diffDiscussionFixture)[0];
+ vm.$store.dispatch('setInitialNotes', [diffDiscussion]);
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.isRepliesCollapsed).toEqual(false);
+ expect(vm.$el.querySelector('.js-toggle-replies')).not.toBeNull();
+ expect(vm.$el.querySelector('.discussion-reply-holder')).not.toBeNull();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('should return false if discussion does not have a reply', () => {
+ const discussion = { ...discussionMock, resolved: true };
+ discussion.notes = discussion.notes.slice(0, 1);
+ const noRepliesVm = new Component({
+ store,
+ propsData: { discussion },
+ }).$mount();
+
+ expect(noRepliesVm.isRepliesCollapsed).toEqual(false);
+ expect(noRepliesVm.$el.querySelector('.js-toggle-replies')).toBeNull();
+ expect(vm.$el.querySelector('.discussion-reply-holder')).not.toBeNull();
+ noRepliesVm.$destroy();
+ });
+
+ it('should return true for resolved non-diff discussion which has replies', () => {
+ const discussion = { ...discussionMock, resolved: true };
+ const resolvedDiscussionVm = new Component({
+ store,
+ propsData: { discussion },
+ }).$mount();
+
+ expect(resolvedDiscussionVm.isRepliesCollapsed).toEqual(true);
+ expect(resolvedDiscussionVm.$el.querySelector('.js-toggle-replies')).not.toBeNull();
+ expect(vm.$el.querySelector('.discussion-reply-holder')).not.toBeNull();
+ resolvedDiscussionVm.$destroy();
+ });
+ });
});
describe('methods', () => {
diff --git a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
index e5999a1c509..be9b2588c90 100644
--- a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
+++ b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
@@ -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) { described_class.cache_key_for_project(project) }
+ let(:cache_key) { pipeline_status.cache_key }
describe '.load_for_project' do
it "loads the status" do
@@ -14,94 +14,24 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cache do
end
describe 'loading in batches' do
- let(:status) { 'success' }
- let(:sha) { '424d1b73bc0d3cb726eb7dc4ce17a4d48552f8c6' }
- let(:ref) { 'master' }
- let(:pipeline_info) { { sha: sha, status: status, ref: ref } }
- let!(:project_without_status) { create(:project, :repository) }
-
describe '.load_in_batch_for_projects' do
- it 'preloads pipeline_status on projects' do
+ it 'loads pipeline_status on projects' do
described_class.load_in_batch_for_projects([project])
# Don't call the accessor that would lazy load the variable
- expect(project.instance_variable_get('@pipeline_status')).to be_a(described_class)
- end
-
- describe 'without a status in redis_cache' do
- it 'loads the status from a commit when it was not in redis_cache' do
- empty_status = { sha: nil, status: nil, ref: nil }
- fake_pipeline = described_class.new(
- project_without_status,
- pipeline_info: empty_status,
- loaded_from_cache: false
- )
-
- expect(described_class).to receive(:new)
- .with(project_without_status,
- pipeline_info: empty_status,
- loaded_from_cache: false)
- .and_return(fake_pipeline)
- expect(fake_pipeline).to receive(:load_from_project)
- expect(fake_pipeline).to receive(:store_in_cache)
-
- described_class.load_in_batch_for_projects([project_without_status])
- end
-
- it 'only connects to redis twice' do
- expect(Gitlab::Redis::Cache).to receive(:with).exactly(2).and_call_original
-
- described_class.load_in_batch_for_projects([project_without_status])
-
- expect(project_without_status.pipeline_status).not_to be_nil
- end
- end
-
- describe 'when a status was cached in redis_cache' do
- before do
- Gitlab::Redis::Cache.with do |redis|
- redis.mapped_hmset(cache_key,
- { sha: sha, status: status, ref: ref })
- end
- end
-
- it 'loads the correct status' do
- described_class.load_in_batch_for_projects([project])
-
- pipeline_status = project.instance_variable_get('@pipeline_status')
-
- expect(pipeline_status.sha).to eq(sha)
- expect(pipeline_status.status).to eq(status)
- expect(pipeline_status.ref).to eq(ref)
- end
-
- it 'only connects to redis_cache once' do
- expect(Gitlab::Redis::Cache).to receive(:with).exactly(1).and_call_original
+ project_pipeline_status = project.instance_variable_get('@pipeline_status')
- described_class.load_in_batch_for_projects([project])
-
- expect(project.pipeline_status).not_to be_nil
- end
-
- it "doesn't load the status separatly" do
- expect_any_instance_of(described_class).not_to receive(:load_from_project)
- expect_any_instance_of(described_class).not_to receive(:load_from_cache)
-
- described_class.load_in_batch_for_projects([project])
- end
+ expect(project_pipeline_status).to be_a(described_class)
+ expect(project_pipeline_status).to be_loaded
end
- end
- describe '.cached_results_for_projects' do
- it 'loads a status from caching for all projects' do
- Gitlab::Redis::Cache.with do |redis|
- redis.mapped_hmset(cache_key, { sha: sha, status: status, ref: ref })
+ it 'loads 10 projects without hitting Gitaly call limit', :request_store do
+ projects = Gitlab::GitalyClient.allow_n_plus_1_calls do
+ (1..10).map { create(:project, :repository) }
end
+ Gitlab::GitalyClient.reset_counts
- result = [{ loaded_from_cache: false, pipeline_info: { sha: nil, status: nil, ref: nil } },
- { loaded_from_cache: true, pipeline_info: pipeline_info }]
-
- expect(described_class.cached_results_for_projects([project_without_status, project])).to eq(result)
+ expect { described_class.load_in_batch_for_projects(projects) }.not_to raise_error
end
end
end
@@ -198,7 +128,9 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cache do
status_for_empty_commit.load_status
- expect(status_for_empty_commit).to be_loaded
+ expect(status_for_empty_commit.sha).to be_nil
+ expect(status_for_empty_commit.status).to be_nil
+ expect(status_for_empty_commit.ref).to be_nil
end
end
diff --git a/spec/lib/gitlab/ci/variables/collection/item_spec.rb b/spec/lib/gitlab/ci/variables/collection/item_spec.rb
index e1e0582cd11..8bf44acb228 100644
--- a/spec/lib/gitlab/ci/variables/collection/item_spec.rb
+++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb
@@ -36,7 +36,7 @@ describe Gitlab::Ci::Variables::Collection::Item do
shared_examples 'raises error for invalid type' do
it do
expect { described_class.new(key: variable_key, value: variable_value) }
- .to raise_error ArgumentError, /`#{variable_key}` must be of type String, while it was:/
+ .to raise_error ArgumentError, /`#{variable_key}` must be of type String or nil value, while it was:/
end
end
@@ -46,7 +46,7 @@ describe Gitlab::Ci::Variables::Collection::Item do
let(:variable_value) { nil }
let(:expected_value) { nil }
- it_behaves_like 'raises error for invalid type'
+ it_behaves_like 'creates variable'
end
context "when it's an empty string" do
diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
index 39852b7fe29..82ed4d47857 100644
--- a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
@@ -43,6 +43,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
--tls-cert /data/helm/app-name/config/cert.pem
--tls-key /data/helm/app-name/config/key.pem
--version 1.2.3
+ --set rbac.create\\=false,rbac.enabled\\=false
--namespace gitlab-managed-apps
-f /data/helm/app-name/config/values.yaml
EOS
@@ -101,6 +102,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
--tls-cert /data/helm/app-name/config/cert.pem
--tls-key /data/helm/app-name/config/key.pem
--version 1.2.3
+ --set rbac.create\\=false,rbac.enabled\\=false
--namespace gitlab-managed-apps
-f /data/helm/app-name/config/values.yaml
EOS
@@ -126,7 +128,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
<<~EOS.strip
/bin/date
/bin/true
- helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml
+ helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --set rbac.create\\=false,rbac.enabled\\=false --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml
EOS
end
end
@@ -148,7 +150,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
let(:helm_install_command) do
<<~EOS.strip
- helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml
+ helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --set rbac.create\\=false,rbac.enabled\\=false --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml
/bin/date
/bin/false
EOS
@@ -175,6 +177,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
helm install chart-name
--name app-name
--version 1.2.3
+ --set rbac.create\\=false,rbac.enabled\\=false
--namespace gitlab-managed-apps
-f /data/helm/app-name/config/values.yaml
EOS
@@ -204,6 +207,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
--tls-ca-cert /data/helm/app-name/config/ca.pem
--tls-cert /data/helm/app-name/config/cert.pem
--tls-key /data/helm/app-name/config/key.pem
+ --set rbac.create\\=false,rbac.enabled\\=false
--namespace gitlab-managed-apps
-f /data/helm/app-name/config/values.yaml
EOS
diff --git a/spec/lib/gitlab/prometheus/query_variables_spec.rb b/spec/lib/gitlab/prometheus/query_variables_spec.rb
new file mode 100644
index 00000000000..78974cadb69
--- /dev/null
+++ b/spec/lib/gitlab/prometheus/query_variables_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Prometheus::QueryVariables do
+ describe '.call' do
+ set(:environment) { create(:environment) }
+ let(:slug) { environment.slug }
+
+ subject { described_class.call(environment) }
+
+ it { is_expected.to include(ci_environment_slug: slug) }
+
+ it do
+ is_expected.to include(environment_filter:
+ %{container_name!="POD",environment="#{slug}"})
+ end
+
+ context 'without deployment platform' do
+ it { is_expected.to include(kube_namespace: '') }
+ end
+
+ context 'with deplyoment platform' do
+ let(:kube_namespace) { environment.deployment_platform.actual_namespace }
+
+ before do
+ create(:cluster, :provided_by_user, projects: [environment.project])
+ end
+
+ it { is_expected.to include(kube_namespace: kube_namespace) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index b212d2b05f2..5390f237073 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -19,6 +19,7 @@ describe Gitlab::UsageData do
create(:cluster, :provided_by_user, :disabled)
create(:clusters_applications_helm, :installed, cluster: gcp_cluster)
create(:clusters_applications_ingress, :installed, cluster: gcp_cluster)
+ create(:clusters_applications_cert_managers, :installed, cluster: gcp_cluster)
create(:clusters_applications_prometheus, :installed, cluster: gcp_cluster)
create(:clusters_applications_runner, :installed, cluster: gcp_cluster)
create(:clusters_applications_knative, :installed, cluster: gcp_cluster)
@@ -81,6 +82,7 @@ describe Gitlab::UsageData do
clusters_platforms_user
clusters_applications_helm
clusters_applications_ingress
+ clusters_applications_cert_managers
clusters_applications_prometheus
clusters_applications_runner
clusters_applications_knative
@@ -131,6 +133,7 @@ describe Gitlab::UsageData do
expect(count_data[:clusters_platforms_user]).to eq(1)
expect(count_data[:clusters_applications_helm]).to eq(1)
expect(count_data[:clusters_applications_ingress]).to eq(1)
+ expect(count_data[:clusters_applications_cert_managers]).to eq(1)
expect(count_data[:clusters_applications_prometheus]).to eq(1)
expect(count_data[:clusters_applications_runner]).to eq(1)
expect(count_data[:clusters_applications_knative]).to eq(1)
diff --git a/spec/models/clusters/applications/cert_manager_spec.rb b/spec/models/clusters/applications/cert_manager_spec.rb
new file mode 100644
index 00000000000..170c6001eaf
--- /dev/null
+++ b/spec/models/clusters/applications/cert_manager_spec.rb
@@ -0,0 +1,79 @@
+require 'rails_helper'
+
+describe Clusters::Applications::CertManager do
+ let(:cert_manager) { create(:clusters_applications_cert_managers) }
+
+ include_examples 'cluster application core specs', :clusters_applications_cert_managers
+
+ describe '#make_installing!' do
+ before do
+ application.make_installing!
+ end
+
+ context 'application install previously errored with older version' do
+ let(:application) { create(:clusters_applications_cert_managers, :scheduled, version: 'v0.4.0') }
+
+ it 'updates the application version' do
+ expect(application.reload.version).to eq('v0.5.0')
+ end
+ end
+ end
+
+ describe '#install_command' do
+ let(:cluster_issuer_file) { { "cluster_issuer.yaml": "---\napiVersion: certmanager.k8s.io/v1alpha1\nkind: ClusterIssuer\nmetadata:\n name: letsencrypt-prod\nspec:\n acme:\n server: https://acme-v02.api.letsencrypt.org/directory\n email: admin@example.com\n privateKeySecretRef:\n name: letsencrypt-prod\n http01: {}\n" } }
+ subject { cert_manager.install_command }
+
+ it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand) }
+
+ it 'should be initialized with cert_manager arguments' do
+ expect(subject.name).to eq('certmanager')
+ expect(subject.chart).to eq('stable/cert-manager')
+ expect(subject.version).to eq('v0.5.0')
+ expect(subject).not_to be_rbac
+ expect(subject.files).to eq(cert_manager.files.merge(cluster_issuer_file))
+ expect(subject.postinstall).to eq(['/usr/bin/kubectl create -f /data/helm/certmanager/config/cluster_issuer.yaml'])
+ end
+
+ context 'for a specific user' do
+ before do
+ cert_manager.email = 'abc@xyz.com'
+ cluster_issuer_file[:'cluster_issuer.yaml'].gsub! 'admin@example.com', 'abc@xyz.com'
+ end
+
+ it 'should use his/her email to register issuer with certificate provider' do
+ expect(subject.files).to eq(cert_manager.files.merge(cluster_issuer_file))
+ end
+ end
+
+ context 'on a rbac enabled cluster' do
+ before do
+ cert_manager.cluster.platform_kubernetes.rbac!
+ end
+
+ it { is_expected.to be_rbac }
+ end
+
+ context 'application failed to install previously' do
+ let(:cert_manager) { create(:clusters_applications_cert_managers, :errored, version: '0.0.1') }
+
+ it 'should be initialized with the locked version' do
+ expect(subject.version).to eq('v0.5.0')
+ end
+ end
+ end
+
+ describe '#files' do
+ let(:application) { cert_manager }
+ let(:values) { subject[:'values.yaml'] }
+
+ subject { application.files }
+
+ it 'should include cert_manager specific keys in the values.yaml file' do
+ expect(values).to include('ingressShim')
+ end
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:email) }
+ end
+end
diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb
index 052cfdbc4b1..a8b28a335d6 100644
--- a/spec/models/clusters/applications/runner_spec.rb
+++ b/spec/models/clusters/applications/runner_spec.rb
@@ -18,7 +18,7 @@ describe Clusters::Applications::Runner do
let(:application) { create(:clusters_applications_runner, :scheduled, version: '0.1.30') }
it 'updates the application version' do
- expect(application.reload.version).to eq('0.1.35')
+ expect(application.reload.version).to eq('0.1.38')
end
end
end
@@ -46,7 +46,7 @@ describe Clusters::Applications::Runner do
it 'should be initialized with 4 arguments' do
expect(subject.name).to eq('runner')
expect(subject.chart).to eq('runner/gitlab-runner')
- expect(subject.version).to eq('0.1.35')
+ expect(subject.version).to eq('0.1.38')
expect(subject).not_to be_rbac
expect(subject.repository).to eq('https://charts.gitlab.io')
expect(subject.files).to eq(gitlab_runner.files)
@@ -64,7 +64,7 @@ describe Clusters::Applications::Runner do
let(:gitlab_runner) { create(:clusters_applications_runner, :errored, runner: ci_runner, version: '0.1.13') }
it 'should be initialized with the locked version' do
- expect(subject.version).to eq('0.1.35')
+ expect(subject.version).to eq('0.1.38')
end
end
end
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index 98d7e799d67..eb68ebccdcb 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -311,13 +311,14 @@ describe Clusters::Cluster do
context 'when applications are created' do
let!(:helm) { create(:clusters_applications_helm, cluster: cluster) }
let!(:ingress) { create(:clusters_applications_ingress, cluster: cluster) }
+ let!(:cert_manager) { create(:clusters_applications_cert_managers, cluster: cluster) }
let!(:prometheus) { create(:clusters_applications_prometheus, cluster: cluster) }
let!(:runner) { create(:clusters_applications_runner, cluster: cluster) }
let!(:jupyter) { create(:clusters_applications_jupyter, cluster: cluster) }
let!(:knative) { create(:clusters_applications_knative, cluster: cluster) }
it 'returns a list of created applications' do
- is_expected.to contain_exactly(helm, ingress, prometheus, runner, jupyter, knative)
+ is_expected.to contain_exactly(helm, ingress, cert_manager, prometheus, runner, jupyter, knative)
end
end
end
diff --git a/spec/models/concerns/relative_positioning_spec.rb b/spec/models/concerns/relative_positioning_spec.rb
index 66c1f47d12b..ac8da30b6c9 100644
--- a/spec/models/concerns/relative_positioning_spec.rb
+++ b/spec/models/concerns/relative_positioning_spec.rb
@@ -14,6 +14,14 @@ describe RelativePositioning do
expect(issue.prev_relative_position).to eq nil
expect(issue1.next_relative_position).to eq nil
end
+
+ it 'does not perform any moves if all issues have their relative_position set' do
+ issue.update!(relative_position: 1)
+
+ expect(issue).not_to receive(:save)
+
+ Issue.move_to_end([issue])
+ end
end
describe '#max_relative_position' do
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index a2b41d56b8b..334dbb1c34c 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -178,6 +178,14 @@ describe API::Files do
expect(response).to have_gitlab_http_status(200)
end
+ it 'forces attachment content disposition' do
+ url = route(file_path) + "/raw"
+
+ get api(url, current_user), params
+
+ expect(headers['Content-Disposition']).to match(/^attachment/)
+ end
+
context 'when mandatory params are not given' do
it_behaves_like '400 response' do
let(:request) { get api(route("any%2Ffile"), current_user) }
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 3d532dd83c7..1827da61e2d 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -300,17 +300,31 @@ describe API::Issues do
expect(json_response.first['state']).to eq('opened')
end
- it 'returns unlabeled issues for "No Label" label' do
- get api("/issues", user), labels: 'No Label'
+ it 'returns an empty array if no issue matches labels and state filters' do
+ get api("/issues", user), labels: label.title, state: :closed
+
+ expect_paginated_array_response(size: 0)
+ end
+
+ it 'returns an array of issues with any label' do
+ get api("/issues", user), labels: IssuesFinder::FILTER_ANY
expect_paginated_array_response(size: 1)
- expect(json_response.first['labels']).to be_empty
+ expect(json_response.first['id']).to eq(issue.id)
end
- it 'returns an empty array if no issue matches labels and state filters' do
- get api("/issues?labels=#{label.title}&state=closed", user)
+ it 'returns an array of issues with no label' do
+ get api("/issues", user), labels: IssuesFinder::FILTER_NONE
- expect_paginated_array_response(size: 0)
+ expect_paginated_array_response(size: 1)
+ expect(json_response.first['id']).to eq(closed_issue.id)
+ end
+
+ it 'returns an array of issues with no label when using the legacy No+Label filter' do
+ get api("/issues", user), labels: "No Label"
+
+ expect_paginated_array_response(size: 1)
+ expect(json_response.first['id']).to eq(closed_issue.id)
end
it 'returns an empty array if no issue matches milestone' do
@@ -492,58 +506,58 @@ describe API::Issues do
end
it 'returns group issues without confidential issues for non project members' do
- get api("#{base_url}?state=opened", non_member)
+ get api(base_url, non_member), state: :opened
expect_paginated_array_response(size: 1)
expect(json_response.first['title']).to eq(group_issue.title)
end
it 'returns group confidential issues for author' do
- get api("#{base_url}?state=opened", author)
+ get api(base_url, author), state: :opened
expect_paginated_array_response(size: 2)
end
it 'returns group confidential issues for assignee' do
- get api("#{base_url}?state=opened", assignee)
+ get api(base_url, assignee), state: :opened
expect_paginated_array_response(size: 2)
end
it 'returns group issues with confidential issues for project members' do
- get api("#{base_url}?state=opened", user)
+ get api(base_url, user), state: :opened
expect_paginated_array_response(size: 2)
end
it 'returns group confidential issues for admin' do
- get api("#{base_url}?state=opened", admin)
+ get api(base_url, admin), state: :opened
expect_paginated_array_response(size: 2)
end
it 'returns an array of labeled group issues' do
- get api("#{base_url}?labels=#{group_label.title}", user)
+ get api(base_url, user), labels: group_label.title
expect_paginated_array_response(size: 1)
expect(json_response.first['labels']).to eq([group_label.title])
end
it 'returns an array of labeled group issues where all labels match' do
- get api("#{base_url}?labels=#{group_label.title},foo,bar", user)
+ get api(base_url, user), labels: "#{group_label.title},foo,bar"
expect_paginated_array_response(size: 0)
end
it 'returns issues matching given search string for title' do
- get api("#{base_url}?search=#{group_issue.title}", user)
+ get api(base_url, user), search: group_issue.title
expect_paginated_array_response(size: 1)
expect(json_response.first['id']).to eq(group_issue.id)
end
it 'returns issues matching given search string for description' do
- get api("#{base_url}?search=#{group_issue.description}", user)
+ get api(base_url, user), search: group_issue.description
expect_paginated_array_response(size: 1)
expect(json_response.first['id']).to eq(group_issue.id)
@@ -556,7 +570,7 @@ describe API::Issues do
create(:label_link, label: label_b, target: group_issue)
create(:label_link, label: label_c, target: group_issue)
- get api("#{base_url}", user), labels: "#{group_label.title},#{label_b.title},#{label_c.title}"
+ get api(base_url, user), labels: "#{group_label.title},#{label_b.title},#{label_c.title}"
expect_paginated_array_response(size: 1)
expect(json_response.first['labels']).to eq([label_c.title, label_b.title, group_label.title])
@@ -576,40 +590,55 @@ describe API::Issues do
end
it 'returns an empty array if no group issue matches labels' do
- get api("#{base_url}?labels=foo,bar", user)
+ get api(base_url, user), labels: 'foo,bar'
expect_paginated_array_response(size: 0)
end
+ it 'returns an array of group issues with any label' do
+ get api(base_url, user), labels: IssuesFinder::FILTER_ANY
+
+ expect_paginated_array_response(size: 1)
+ expect(json_response.first['id']).to eq(group_issue.id)
+ end
+
+ it 'returns an array of group issues with no label' do
+ get api(base_url, user), labels: IssuesFinder::FILTER_NONE
+
+ response_ids = json_response.map { |issue| issue['id'] }
+
+ expect_paginated_array_response(size: 2)
+ expect(response_ids).to contain_exactly(group_closed_issue.id, group_confidential_issue.id)
+ end
+
it 'returns an empty array if no issue matches milestone' do
- get api("#{base_url}?milestone=#{group_empty_milestone.title}", user)
+ get api(base_url, user), milestone: group_empty_milestone.title
expect_paginated_array_response(size: 0)
end
it 'returns an empty array if milestone does not exist' do
- get api("#{base_url}?milestone=foo", user)
+ get api(base_url, user), milestone: 'foo'
expect_paginated_array_response(size: 0)
end
it 'returns an array of issues in given milestone' do
- get api("#{base_url}?state=opened&milestone=#{group_milestone.title}", user)
+ get api(base_url, user), state: :opened, milestone: group_milestone.title
expect_paginated_array_response(size: 1)
expect(json_response.first['id']).to eq(group_issue.id)
end
it 'returns an array of issues matching state in milestone' do
- get api("#{base_url}?milestone=#{group_milestone.title}"\
- '&state=closed', user)
+ get api(base_url, user), milestone: group_milestone.title, state: :closed
expect_paginated_array_response(size: 1)
expect(json_response.first['id']).to eq(group_closed_issue.id)
end
it 'returns an array of issues with no milestone' do
- get api("#{base_url}?milestone=#{no_milestone_title}", user)
+ get api(base_url, user), milestone: no_milestone_title
expect(response).to have_gitlab_http_status(200)
@@ -645,7 +674,7 @@ describe API::Issues do
end
it 'sorts by updated_at ascending when requested' do
- get api("#{base_url}?order_by=updated_at&sort=asc", user)
+ get api(base_url, user), order_by: :updated_at, sort: :asc
response_dates = json_response.map { |issue| issue['updated_at'] }
@@ -748,7 +777,7 @@ describe API::Issues do
end
it 'returns an array of labeled project issues' do
- get api("#{base_url}/issues?labels=#{label.title}", user)
+ get api("#{base_url}/issues", user), labels: label.title
expect_paginated_array_response(size: 1)
expect(json_response.first['labels']).to eq([label.title])
@@ -800,26 +829,42 @@ describe API::Issues do
expect_paginated_array_response(size: 0)
end
+ it 'returns an array of project issues with any label' do
+ get api("#{base_url}/issues", user), labels: IssuesFinder::FILTER_ANY
+
+ expect_paginated_array_response(size: 1)
+ expect(json_response.first['id']).to eq(issue.id)
+ end
+
+ it 'returns an array of project issues with no label' do
+ get api("#{base_url}/issues", user), labels: IssuesFinder::FILTER_NONE
+
+ response_ids = json_response.map { |issue| issue['id'] }
+
+ expect_paginated_array_response(size: 2)
+ expect(response_ids).to contain_exactly(closed_issue.id, confidential_issue.id)
+ end
+
it 'returns an empty array if no project issue matches labels' do
- get api("#{base_url}/issues?labels=foo,bar", user)
+ get api("#{base_url}/issues", user), labels: 'foo,bar'
expect_paginated_array_response(size: 0)
end
it 'returns an empty array if no issue matches milestone' do
- get api("#{base_url}/issues?milestone=#{empty_milestone.title}", user)
+ get api("#{base_url}/issues", user), milestone: empty_milestone.title
expect_paginated_array_response(size: 0)
end
it 'returns an empty array if milestone does not exist' do
- get api("#{base_url}/issues?milestone=foo", user)
+ get api("#{base_url}/issues", user), milestone: :foo
expect_paginated_array_response(size: 0)
end
it 'returns an array of issues in given milestone' do
- get api("#{base_url}/issues?milestone=#{milestone.title}", user)
+ get api("#{base_url}/issues", user), milestone: milestone.title
expect_paginated_array_response(size: 2)
expect(json_response.first['id']).to eq(issue.id)
@@ -827,21 +872,21 @@ describe API::Issues do
end
it 'returns an array of issues matching state in milestone' do
- get api("#{base_url}/issues?milestone=#{milestone.title}&state=closed", user)
+ get api("#{base_url}/issues", user), milestone: milestone.title, state: :closed
expect_paginated_array_response(size: 1)
expect(json_response.first['id']).to eq(closed_issue.id)
end
it 'returns an array of issues with no milestone' do
- get api("#{base_url}/issues?milestone=#{no_milestone_title}", user)
+ get api("#{base_url}/issues", user), milestone: no_milestone_title
expect_paginated_array_response(size: 1)
expect(json_response.first['id']).to eq(confidential_issue.id)
end
it 'returns an array of issues with any milestone' do
- get api("#{base_url}/issues?milestone=#{any_milestone_title}", user)
+ get api("#{base_url}/issues", user), milestone: any_milestone_title
response_ids = json_response.map { |issue| issue['id'] }
@@ -859,7 +904,7 @@ describe API::Issues do
end
it 'sorts ascending when requested' do
- get api("#{base_url}/issues?sort=asc", user)
+ get api("#{base_url}/issues", user), sort: :asc
response_dates = json_response.map { |issue| issue['created_at'] }
@@ -868,7 +913,7 @@ describe API::Issues do
end
it 'sorts by updated_at descending when requested' do
- get api("#{base_url}/issues?order_by=updated_at", user)
+ get api("#{base_url}/issues", user), order_by: :updated_at
response_dates = json_response.map { |issue| issue['updated_at'] }
@@ -877,7 +922,7 @@ describe API::Issues do
end
it 'sorts by updated_at ascending when requested' do
- get api("#{base_url}/issues?order_by=updated_at&sort=asc", user)
+ get api("#{base_url}/issues", user), order_by: :updated_at, sort: :asc
response_dates = json_response.map { |issue| issue['updated_at'] }
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index fa38751fe58..de141377793 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -168,6 +168,12 @@ describe API::Repositories do
expect(response).to have_gitlab_http_status(200)
end
+ it 'forces attachment content disposition' do
+ get api(route, current_user)
+
+ expect(headers['Content-Disposition']).to match(/^attachment/)
+ end
+
context 'when sha does not exist' do
it_behaves_like '404 response' do
let(:request) { get api(route.sub(sample_blob.oid, '123456'), current_user) }
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index 6da769cb3ed..c546ba3e127 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -94,6 +94,12 @@ describe API::Snippets do
expect(response.body).to eq(snippet.content)
end
+ it 'forces attachment content disposition' do
+ get api("/snippets/#{snippet.id}/raw", user)
+
+ expect(headers['Content-Disposition']).to match(/^attachment/)
+ end
+
it 'returns 404 for invalid snippet id' do
get api("/snippets/1234/raw", user)
diff --git a/spec/support/shared_examples/requests/api/merge_requests_list.rb b/spec/support/shared_examples/requests/api/merge_requests_list.rb
index 668a390b5d2..92d4dd598d5 100644
--- a/spec/support/shared_examples/requests/api/merge_requests_list.rb
+++ b/spec/support/shared_examples/requests/api/merge_requests_list.rb
@@ -186,6 +186,23 @@ shared_examples 'merge requests list' do
expect(json_response.length).to eq(0)
end
+ it 'returns an array of merge requests with any label when filtering by any label' do
+ get api(endpoint_path, user), labels: IssuesFinder::FILTER_ANY
+
+ expect_paginated_array_response
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(merge_request.id)
+ end
+
+ it 'returns an array of merge requests without a label when filtering by no label' do
+ get api(endpoint_path, user), labels: IssuesFinder::FILTER_NONE
+
+ response_ids = json_response.map { |merge_request| merge_request['id'] }
+
+ expect_paginated_array_response
+ expect(response_ids).to contain_exactly(merge_request_closed.id, merge_request_merged.id, merge_request_locked.id)
+ end
+
it 'returns an array of labeled merge requests that are merged for a milestone' do
bug_label = create(:label, title: 'bug', color: '#FFAABB', project: project)
diff --git a/spec/tasks/cache/clear/redis_spec.rb b/spec/tasks/cache/clear/redis_spec.rb
index cca2b864e9b..97c8c943f3a 100644
--- a/spec/tasks/cache/clear/redis_spec.rb
+++ b/spec/tasks/cache/clear/redis_spec.rb
@@ -6,7 +6,10 @@ describe 'clearing redis cache' do
end
describe 'clearing pipeline status cache' do
- let(:pipeline_status) { create(:ci_pipeline).project.pipeline_status }
+ let(:pipeline_status) do
+ project = create(:project, :repository)
+ create(:ci_pipeline, project: project).project.pipeline_status
+ end
before do
allow(pipeline_status).to receive(:loaded).and_return(nil)