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>2019-12-17 18:08:15 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2019-12-17 18:08:15 +0300
commitc2b98d3dbd47ab92c79c702276fe9130d9a28036 (patch)
treebf4071f551fdc12c22b23b2bb66483064e7b9ea9 /spec
parentbadb9c1deacbea601b02f88811b7e123589d9251 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/service_hook_logs_controller_spec.rb41
-rw-r--r--spec/factories/services.rb7
-rw-r--r--spec/frontend/monitoring/components/__snapshots__/empty_state_spec.js.snap (renamed from spec/frontend/monitoring/__snapshots__/dashboard_state_spec.js.snap)0
-rw-r--r--spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap79
-rw-r--r--spec/frontend/monitoring/components/empty_state_spec.js (renamed from spec/frontend/monitoring/dashboard_state_spec.js)0
-rw-r--r--spec/frontend/monitoring/components/group_empty_state_spec.js34
-rw-r--r--spec/frontend/monitoring/store/actions_spec.js2
-rw-r--r--spec/frontend/monitoring/store/getters_spec.js120
-rw-r--r--spec/frontend/monitoring/store/mutations_spec.js26
-rw-r--r--spec/javascripts/ide/stores/actions_spec.js42
-rw-r--r--spec/javascripts/monitoring/components/dashboard_spec.js11
-rw-r--r--spec/models/blob_spec.rb17
-rw-r--r--spec/models/concerns/safe_url_spec.rb52
-rw-r--r--spec/models/hooks/web_hook_log_spec.rb19
-rw-r--r--spec/models/readme_blob_spec.rb16
-rw-r--r--spec/policies/blob_policy_spec.rb31
-rw-r--r--spec/policies/wiki_page_policy_spec.rb31
-rw-r--r--spec/presenters/hooks/project_hook_presenter_spec.rb29
-rw-r--r--spec/presenters/hooks/service_hook_presenter_spec.rb30
-rw-r--r--spec/presenters/web_hook_log_presenter_spec.rb47
-rw-r--r--spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb63
-rw-r--r--spec/services/web_hook_service_spec.rb11
-rw-r--r--spec/views/projects/services/edit.html.haml_spec.rb31
23 files changed, 703 insertions, 36 deletions
diff --git a/spec/controllers/projects/service_hook_logs_controller_spec.rb b/spec/controllers/projects/service_hook_logs_controller_spec.rb
new file mode 100644
index 00000000000..ca57b0579a8
--- /dev/null
+++ b/spec/controllers/projects/service_hook_logs_controller_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::ServiceHookLogsController do
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+ let(:service) { create(:drone_ci_service, project: project) }
+ let(:log) { create(:web_hook_log, web_hook: service.service_hook) }
+ let(:log_params) do
+ {
+ namespace_id: project.namespace,
+ project_id: project,
+ service_id: service.to_param,
+ id: log.id
+ }
+ end
+
+ before do
+ sign_in(user)
+ project.add_maintainer(user)
+ end
+
+ describe 'GET #show' do
+ subject { get :show, params: log_params }
+
+ it do
+ expect(response).to be_successful
+ end
+ end
+
+ describe 'POST #retry' do
+ subject { post :retry, params: log_params }
+
+ it 'executes the hook and redirects to the service form' do
+ expect_any_instance_of(ServiceHook).to receive(:execute)
+ expect_any_instance_of(described_class).to receive(:set_hook_execution_notice)
+ expect(subject).to redirect_to(edit_project_service_path(project, service))
+ end
+ end
+end
diff --git a/spec/factories/services.rb b/spec/factories/services.rb
index f9c77dbf87f..b6bb30d1f93 100644
--- a/spec/factories/services.rb
+++ b/spec/factories/services.rb
@@ -44,6 +44,13 @@ FactoryBot.define do
end
end
+ factory :drone_ci_service do
+ project
+ active { true }
+ drone_url { 'https://bamboo.example.com' }
+ token { 'test' }
+ end
+
factory :jira_service do
project
active { true }
diff --git a/spec/frontend/monitoring/__snapshots__/dashboard_state_spec.js.snap b/spec/frontend/monitoring/components/__snapshots__/empty_state_spec.js.snap
index 5f24bab600c..5f24bab600c 100644
--- a/spec/frontend/monitoring/__snapshots__/dashboard_state_spec.js.snap
+++ b/spec/frontend/monitoring/components/__snapshots__/empty_state_spec.js.snap
diff --git a/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap b/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap
new file mode 100644
index 00000000000..7f37a83d291
--- /dev/null
+++ b/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap
@@ -0,0 +1,79 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`GroupEmptyState Renders an empty state for BAD_QUERY 1`] = `
+<glemptystate-stub
+ compact="true"
+ primarybuttonlink="/path/to/settings"
+ primarybuttontext="Verify configuration"
+ svgpath="/path/to/empty-group-illustration.svg"
+ title="Query cannot be processed"
+/>
+`;
+
+exports[`GroupEmptyState Renders an empty state for BAD_QUERY 2`] = `"The Prometheus server responded with \\"bad request\\". Please check your queries are correct and are supported in your Prometheus version. <a href=\\"/path/to/docs\\">More information</a>"`;
+
+exports[`GroupEmptyState Renders an empty state for CONNECTION_FAILED 1`] = `
+<glemptystate-stub
+ compact="true"
+ description="We couldn't reach the Prometheus server. Either the server no longer exists or the configuration details need updating."
+ primarybuttonlink="/path/to/settings"
+ primarybuttontext="Verify configuration"
+ svgpath="/path/to/empty-group-illustration.svg"
+ title="Connection failed"
+/>
+`;
+
+exports[`GroupEmptyState Renders an empty state for CONNECTION_FAILED 2`] = `undefined`;
+
+exports[`GroupEmptyState Renders an empty state for FOO STATE 1`] = `
+<glemptystate-stub
+ compact="true"
+ description="An error occurred while loading the data. Please try again."
+ svgpath="/path/to/empty-group-illustration.svg"
+ title="An error has occurred"
+/>
+`;
+
+exports[`GroupEmptyState Renders an empty state for FOO STATE 2`] = `undefined`;
+
+exports[`GroupEmptyState Renders an empty state for LOADING 1`] = `
+<glemptystate-stub
+ compact="true"
+ description="Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available."
+ svgpath="/path/to/empty-group-illustration.svg"
+ title="Waiting for performance data"
+/>
+`;
+
+exports[`GroupEmptyState Renders an empty state for LOADING 2`] = `undefined`;
+
+exports[`GroupEmptyState Renders an empty state for NO_DATA 1`] = `
+<glemptystate-stub
+ compact="true"
+ svgpath="/path/to/empty-group-illustration.svg"
+ title="No data to display"
+/>
+`;
+
+exports[`GroupEmptyState Renders an empty state for NO_DATA 2`] = `"The data source is connected, but there is no data to display. <a href=\\"/path/to/docs\\">More information</a>"`;
+
+exports[`GroupEmptyState Renders an empty state for TIMEOUT 1`] = `
+<glemptystate-stub
+ compact="true"
+ svgpath="/path/to/empty-group-illustration.svg"
+ title="Connection timed out"
+/>
+`;
+
+exports[`GroupEmptyState Renders an empty state for TIMEOUT 2`] = `"Charts can't be displayed as the request for data has timed out. <a href=\\"/path/to/docs\\">More information</a>"`;
+
+exports[`GroupEmptyState Renders an empty state for UNKNOWN_ERROR 1`] = `
+<glemptystate-stub
+ compact="true"
+ description="An error occurred while loading the data. Please try again."
+ svgpath="/path/to/empty-group-illustration.svg"
+ title="An error has occurred"
+/>
+`;
+
+exports[`GroupEmptyState Renders an empty state for UNKNOWN_ERROR 2`] = `undefined`;
diff --git a/spec/frontend/monitoring/dashboard_state_spec.js b/spec/frontend/monitoring/components/empty_state_spec.js
index e985e5fb443..e985e5fb443 100644
--- a/spec/frontend/monitoring/dashboard_state_spec.js
+++ b/spec/frontend/monitoring/components/empty_state_spec.js
diff --git a/spec/frontend/monitoring/components/group_empty_state_spec.js b/spec/frontend/monitoring/components/group_empty_state_spec.js
new file mode 100644
index 00000000000..e8ef8192067
--- /dev/null
+++ b/spec/frontend/monitoring/components/group_empty_state_spec.js
@@ -0,0 +1,34 @@
+import { shallowMount } from '@vue/test-utils';
+import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
+import { metricStates } from '~/monitoring/constants';
+
+function createComponent(props) {
+ return shallowMount(GroupEmptyState, {
+ propsData: {
+ ...props,
+ documentationPath: '/path/to/docs',
+ settingsPath: '/path/to/settings',
+ svgPath: '/path/to/empty-group-illustration.svg',
+ },
+ });
+}
+
+describe('GroupEmptyState', () => {
+ const supportedStates = [
+ metricStates.NO_DATA,
+ metricStates.TIMEOUT,
+ metricStates.CONNECTION_FAILED,
+ metricStates.BAD_QUERY,
+ metricStates.LOADING,
+ metricStates.UNKNOWN_ERROR,
+ 'FOO STATE', // does not fail with unknown states
+ ];
+
+ test.each(supportedStates)('Renders an empty state for %s', selectedState => {
+ const wrapper = createComponent({ selectedState });
+
+ expect(wrapper.element).toMatchSnapshot();
+ // slot is not rendered by the stub, test it separately
+ expect(wrapper.vm.currentState.slottedDescription).toMatchSnapshot();
+ });
+});
diff --git a/spec/frontend/monitoring/store/actions_spec.js b/spec/frontend/monitoring/store/actions_spec.js
index 92d469270c9..f38bd4384e2 100644
--- a/spec/frontend/monitoring/store/actions_spec.js
+++ b/spec/frontend/monitoring/store/actions_spec.js
@@ -529,7 +529,7 @@ describe('Monitoring store actions', () => {
},
},
{
- type: types.RECEIVE_METRIC_RESULT_ERROR,
+ type: types.RECEIVE_METRIC_RESULT_FAILURE,
payload: {
metricId: metric.metric_id,
error,
diff --git a/spec/frontend/monitoring/store/getters_spec.js b/spec/frontend/monitoring/store/getters_spec.js
index 3b6f33ed8b1..9e325fe3cf9 100644
--- a/spec/frontend/monitoring/store/getters_spec.js
+++ b/spec/frontend/monitoring/store/getters_spec.js
@@ -1,7 +1,7 @@
import * as getters from '~/monitoring/stores/getters';
-
import mutations from '~/monitoring/stores/mutations';
import * as types from '~/monitoring/stores/mutation_types';
+import { metricStates } from '~/monitoring/constants';
import {
metricsGroupsAPIResponse,
mockedEmptyResult,
@@ -10,6 +10,124 @@ import {
} from '../mock_data';
describe('Monitoring store Getters', () => {
+ describe('getMetricStates', () => {
+ let setupState;
+ let state;
+ let getMetricStates;
+
+ beforeEach(() => {
+ setupState = (initState = {}) => {
+ state = initState;
+ getMetricStates = getters.getMetricStates(state);
+ };
+ });
+
+ it('has method-style access', () => {
+ setupState();
+
+ expect(getMetricStates).toEqual(expect.any(Function));
+ });
+
+ it('when dashboard has no panel groups, returns empty', () => {
+ setupState({
+ dashboard: {
+ panel_groups: [],
+ },
+ });
+
+ expect(getMetricStates()).toEqual([]);
+ });
+
+ describe('when the dashboard is set', () => {
+ let groups;
+ beforeEach(() => {
+ setupState({
+ dashboard: { panel_groups: [] },
+ });
+ mutations[types.RECEIVE_METRICS_DATA_SUCCESS](state, metricsGroupsAPIResponse);
+ groups = state.dashboard.panel_groups;
+ });
+
+ it('no loaded metric returns empty', () => {
+ expect(getMetricStates()).toEqual([]);
+ });
+
+ it('on an empty metric with no result, returns NO_DATA', () => {
+ mutations[types.RECEIVE_METRICS_DATA_SUCCESS](state, metricsGroupsAPIResponse);
+ mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedEmptyResult);
+
+ expect(getMetricStates()).toEqual([metricStates.NO_DATA]);
+ });
+
+ it('on a metric with a result, returns OK', () => {
+ mutations[types.RECEIVE_METRICS_DATA_SUCCESS](state, metricsGroupsAPIResponse);
+ mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedQueryResultPayload);
+
+ expect(getMetricStates()).toEqual([metricStates.OK]);
+ });
+
+ it('on a metric with an error, returns an error', () => {
+ mutations[types.RECEIVE_METRICS_DATA_SUCCESS](state, metricsGroupsAPIResponse);
+ mutations[types.RECEIVE_METRIC_RESULT_FAILURE](state, {
+ metricId: groups[0].panels[0].metrics[0].metricId,
+ });
+
+ expect(getMetricStates()).toEqual([metricStates.UNKNOWN_ERROR]);
+ });
+
+ it('on multiple metrics with results, returns OK', () => {
+ mutations[types.RECEIVE_METRICS_DATA_SUCCESS](state, metricsGroupsAPIResponse);
+ mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedQueryResultPayload);
+ mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedQueryResultPayloadCoresTotal);
+
+ expect(getMetricStates()).toEqual([metricStates.OK]);
+
+ // Filtered by groups
+ expect(getMetricStates(state.dashboard.panel_groups[0].key)).toEqual([]);
+ expect(getMetricStates(state.dashboard.panel_groups[1].key)).toEqual([metricStates.OK]);
+ });
+ it('on multiple metrics errors', () => {
+ mutations[types.RECEIVE_METRICS_DATA_SUCCESS](state, metricsGroupsAPIResponse);
+
+ mutations[types.RECEIVE_METRIC_RESULT_FAILURE](state, {
+ metricId: groups[0].panels[0].metrics[0].metricId,
+ });
+ mutations[types.RECEIVE_METRIC_RESULT_FAILURE](state, {
+ metricId: groups[1].panels[0].metrics[0].metricId,
+ });
+ mutations[types.RECEIVE_METRIC_RESULT_FAILURE](state, {
+ metricId: groups[1].panels[1].metrics[0].metricId,
+ });
+
+ // Entire dashboard fails
+ expect(getMetricStates()).toEqual([metricStates.UNKNOWN_ERROR]);
+ expect(getMetricStates(groups[0].key)).toEqual([metricStates.UNKNOWN_ERROR]);
+ expect(getMetricStates(groups[1].key)).toEqual([metricStates.UNKNOWN_ERROR]);
+ });
+
+ it('on multiple metrics with errors', () => {
+ mutations[types.RECEIVE_METRICS_DATA_SUCCESS](state, metricsGroupsAPIResponse);
+
+ // An success in 1 group
+ mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedQueryResultPayload);
+ // An error in 2 groups
+ mutations[types.RECEIVE_METRIC_RESULT_FAILURE](state, {
+ metricId: groups[0].panels[0].metrics[0].metricId,
+ });
+ mutations[types.RECEIVE_METRIC_RESULT_FAILURE](state, {
+ metricId: groups[1].panels[1].metrics[0].metricId,
+ });
+
+ expect(getMetricStates()).toEqual([metricStates.OK, metricStates.UNKNOWN_ERROR]);
+ expect(getMetricStates(groups[0].key)).toEqual([metricStates.UNKNOWN_ERROR]);
+ expect(getMetricStates(groups[1].key)).toEqual([
+ metricStates.OK,
+ metricStates.UNKNOWN_ERROR,
+ ]);
+ });
+ });
+ });
+
describe('metricsWithData', () => {
let metricsWithData;
let setupState;
diff --git a/spec/frontend/monitoring/store/mutations_spec.js b/spec/frontend/monitoring/store/mutations_spec.js
index 8da172ec634..60107a03674 100644
--- a/spec/frontend/monitoring/store/mutations_spec.js
+++ b/spec/frontend/monitoring/store/mutations_spec.js
@@ -3,7 +3,7 @@ import httpStatusCodes from '~/lib/utils/http_status';
import mutations from '~/monitoring/stores/mutations';
import * as types from '~/monitoring/stores/mutation_types';
import state from '~/monitoring/stores/state';
-import { metricsErrors } from '~/monitoring/constants';
+import { metricStates } from '~/monitoring/constants';
import {
metricsGroupsAPIResponse,
deploymentData,
@@ -120,7 +120,7 @@ describe('Monitoring mutations', () => {
expect.objectContaining({
loading: true,
result: null,
- error: null,
+ state: metricStates.LOADING,
}),
);
});
@@ -153,20 +153,20 @@ describe('Monitoring mutations', () => {
expect(getMetric()).toEqual(
expect.objectContaining({
loading: false,
- error: null,
+ state: metricStates.OK,
}),
);
});
});
- describe('RECEIVE_METRIC_RESULT_ERROR', () => {
+ describe('RECEIVE_METRIC_RESULT_FAILURE', () => {
beforeEach(() => {
mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, dashboardGroups);
});
it('maintains the loading state when a metric fails', () => {
expect(stateCopy.showEmptyState).toBe(true);
- mutations[types.RECEIVE_METRIC_RESULT_ERROR](stateCopy, {
+ mutations[types.RECEIVE_METRIC_RESULT_FAILURE](stateCopy, {
metricId,
error: 'an error',
});
@@ -175,7 +175,7 @@ describe('Monitoring mutations', () => {
});
it('stores a timeout error in a metric', () => {
- mutations[types.RECEIVE_METRIC_RESULT_ERROR](stateCopy, {
+ mutations[types.RECEIVE_METRIC_RESULT_FAILURE](stateCopy, {
metricId,
error: { message: 'BACKOFF_TIMEOUT' },
});
@@ -184,13 +184,13 @@ describe('Monitoring mutations', () => {
expect.objectContaining({
loading: false,
result: null,
- error: metricsErrors.TIMEOUT,
+ state: metricStates.TIMEOUT,
}),
);
});
it('stores a connection failed error in a metric', () => {
- mutations[types.RECEIVE_METRIC_RESULT_ERROR](stateCopy, {
+ mutations[types.RECEIVE_METRIC_RESULT_FAILURE](stateCopy, {
metricId,
error: {
response: {
@@ -202,13 +202,13 @@ describe('Monitoring mutations', () => {
expect.objectContaining({
loading: false,
result: null,
- error: metricsErrors.CONNECTION_FAILED,
+ state: metricStates.CONNECTION_FAILED,
}),
);
});
it('stores a bad data error in a metric', () => {
- mutations[types.RECEIVE_METRIC_RESULT_ERROR](stateCopy, {
+ mutations[types.RECEIVE_METRIC_RESULT_FAILURE](stateCopy, {
metricId,
error: {
response: {
@@ -221,13 +221,13 @@ describe('Monitoring mutations', () => {
expect.objectContaining({
loading: false,
result: null,
- error: metricsErrors.BAD_DATA,
+ state: metricStates.BAD_QUERY,
}),
);
});
it('stores an unknown error in a metric', () => {
- mutations[types.RECEIVE_METRIC_RESULT_ERROR](stateCopy, {
+ mutations[types.RECEIVE_METRIC_RESULT_FAILURE](stateCopy, {
metricId,
error: null, // no reason in response
});
@@ -236,7 +236,7 @@ describe('Monitoring mutations', () => {
expect.objectContaining({
loading: false,
result: null,
- error: metricsErrors.UNKNOWN_ERROR,
+ state: metricStates.UNKNOWN_ERROR,
}),
);
});
diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js
index 708c5ea75e0..0ee114cb70d 100644
--- a/spec/javascripts/ide/stores/actions_spec.js
+++ b/spec/javascripts/ide/stores/actions_spec.js
@@ -92,26 +92,58 @@ describe('Multi-file store actions', () => {
.catch(done.fail);
});
- it('closes the temp file if it was open', done => {
+ it('closes the temp file and deletes it if it was open', done => {
f.tempFile = true;
testAction(
discardAllChanges,
undefined,
store.state,
+ [{ type: types.REMOVE_ALL_CHANGES_FILES }],
[
- { type: types.DISCARD_FILE_CHANGES, payload: 'discardAll' },
- { type: types.REMOVE_ALL_CHANGES_FILES },
+ { type: 'closeFile', payload: jasmine.objectContaining({ path: 'discardAll' }) },
+ { type: 'deleteEntry', payload: 'discardAll' },
],
+ done,
+ );
+ });
+
+ it('renames the file to its original name and closes it if it was open', done => {
+ Object.assign(f, {
+ prevPath: 'parent/path/old_name',
+ prevName: 'old_name',
+ prevParentPath: 'parent/path',
+ });
+
+ testAction(
+ discardAllChanges,
+ undefined,
+ store.state,
+ [{ type: types.REMOVE_ALL_CHANGES_FILES }],
[
+ { type: 'closeFile', payload: jasmine.objectContaining({ path: 'discardAll' }) },
{
- type: 'closeFile',
- payload: jasmine.objectContaining({ path: 'discardAll' }),
+ type: 'renameEntry',
+ payload: { path: 'discardAll', name: 'old_name', parentPath: 'parent/path' },
},
],
done,
);
});
+
+ it('discards file changes on all other files', done => {
+ testAction(
+ discardAllChanges,
+ undefined,
+ store.state,
+ [
+ { type: types.DISCARD_FILE_CHANGES, payload: 'discardAll' },
+ { type: types.REMOVE_ALL_CHANGES_FILES },
+ ],
+ [],
+ done,
+ );
+ });
});
describe('closeAllFiles', () => {
diff --git a/spec/javascripts/monitoring/components/dashboard_spec.js b/spec/javascripts/monitoring/components/dashboard_spec.js
index 37a811f153f..b29bac21820 100644
--- a/spec/javascripts/monitoring/components/dashboard_spec.js
+++ b/spec/javascripts/monitoring/components/dashboard_spec.js
@@ -4,7 +4,8 @@ import { GlToast } from '@gitlab/ui';
import VueDraggable from 'vuedraggable';
import MockAdapter from 'axios-mock-adapter';
import Dashboard from '~/monitoring/components/dashboard.vue';
-import EmptyState from '~/monitoring/components/empty_state.vue';
+import { metricStates } from '~/monitoring/constants';
+import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
import * as types from '~/monitoring/stores/mutation_types';
import { createStore } from '~/monitoring/stores';
import axios from '~/lib/utils/axios_utils';
@@ -401,7 +402,7 @@ describe('Dashboard', () => {
});
beforeEach(done => {
- createComponentWrapper({ hasMetrics: true }, { attachToDocument: true });
+ createComponentWrapper({ hasMetrics: true });
setupComponentStore(wrapper.vm);
wrapper.vm.$nextTick(done);
@@ -411,16 +412,16 @@ describe('Dashboard', () => {
const emptyGroup = wrapper.findAll({ ref: 'empty-group' });
expect(emptyGroup).toHaveLength(1);
- expect(emptyGroup.is(EmptyState)).toBe(true);
+ expect(emptyGroup.is(GroupEmptyState)).toBe(true);
});
- it('group empty area displays a "noDataGroup"', () => {
+ it('group empty area displays a NO_DATA state', () => {
expect(
wrapper
.findAll({ ref: 'empty-group' })
.at(0)
.props('selectedState'),
- ).toEqual('noDataGroup');
+ ).toEqual(metricStates.NO_DATA);
});
});
diff --git a/spec/models/blob_spec.rb b/spec/models/blob_spec.rb
index 9e55fbcce20..2c141cae98d 100644
--- a/spec/models/blob_spec.rb
+++ b/spec/models/blob_spec.rb
@@ -421,4 +421,21 @@ describe Blob do
end
end
end
+
+ describe 'policy' do
+ let(:project) { build(:project) }
+ subject { described_class.new(fake_blob(path: 'foo'), project) }
+
+ it 'works with policy' do
+ expect(Ability.allowed?(project.creator, :read_blob, subject)).to be_truthy
+ end
+
+ context 'when project is nil' do
+ subject { described_class.new(fake_blob(path: 'foo')) }
+
+ it 'does not err' do
+ expect(Ability.allowed?(project.creator, :read_blob, subject)).to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/models/concerns/safe_url_spec.rb b/spec/models/concerns/safe_url_spec.rb
new file mode 100644
index 00000000000..3244410181e
--- /dev/null
+++ b/spec/models/concerns/safe_url_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe SafeUrl do
+ describe '#safe_url' do
+ class TestClass
+ include SafeUrl
+
+ attr_reader :url
+
+ def initialize(url)
+ @url = url
+ end
+ end
+
+ let(:test_class) { TestClass.new(url) }
+ let(:url) { 'http://example.com' }
+
+ subject { test_class.safe_url }
+
+ it { is_expected.to eq(url) }
+
+ context 'when URL contains credentials' do
+ let(:url) { 'http://foo:bar@example.com' }
+
+ it { is_expected.to eq('http://*****:*****@example.com')}
+
+ context 'when username is whitelisted' do
+ subject { test_class.safe_url(usernames_whitelist: usernames_whitelist) }
+
+ let(:usernames_whitelist) { %w[foo] }
+
+ it 'does expect the whitelisted username not to be masked' do
+ is_expected.to eq('http://foo:*****@example.com')
+ end
+ end
+ end
+
+ context 'when URL is empty' do
+ let(:url) { nil }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when URI raises an error' do
+ let(:url) { 123 }
+
+ it { is_expected.to be_nil }
+ end
+ end
+end
diff --git a/spec/models/hooks/web_hook_log_spec.rb b/spec/models/hooks/web_hook_log_spec.rb
index 85934b81086..22aad2fab0a 100644
--- a/spec/models/hooks/web_hook_log_spec.rb
+++ b/spec/models/hooks/web_hook_log_spec.rb
@@ -29,6 +29,25 @@ describe WebHookLog do
end
end
+ describe '#save' do
+ let(:web_hook_log) { build(:web_hook_log, url: url) }
+ let(:url) { 'http://example.com' }
+
+ subject { web_hook_log.save! }
+
+ it { is_expected.to eq(true) }
+
+ context 'with basic auth credentials' do
+ let(:url) { 'http://test:123@example.com'}
+
+ it 'obfuscates the basic auth credentials' do
+ subject
+
+ expect(web_hook_log.url).to eq('http://*****:*****@example.com')
+ end
+ end
+ end
+
describe '#success?' do
let(:web_hook_log) { build(:web_hook_log, response_status: status) }
diff --git a/spec/models/readme_blob_spec.rb b/spec/models/readme_blob_spec.rb
new file mode 100644
index 00000000000..f07713bd908
--- /dev/null
+++ b/spec/models/readme_blob_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ReadmeBlob do
+ include FakeBlobHelpers
+
+ describe 'policy' do
+ let(:project) { build(:project, :repository) }
+ subject { described_class.new(fake_blob(path: 'README.md'), project.repository) }
+
+ it 'works with policy' do
+ expect(Ability.allowed?(project.creator, :read_blob, subject)).to be_truthy
+ end
+ end
+end
diff --git a/spec/policies/blob_policy_spec.rb b/spec/policies/blob_policy_spec.rb
new file mode 100644
index 00000000000..20c8a55f437
--- /dev/null
+++ b/spec/policies/blob_policy_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe BlobPolicy do
+ include_context 'ProjectPolicyTable context'
+ include ProjectHelpers
+ using RSpec::Parameterized::TableSyntax
+
+ let(:project) { create(:project, :repository, project_level) }
+ let(:user) { create_user_from_membership(project, membership) }
+ let(:blob) { project.repository.blob_at(SeedRepo::FirstCommit::ID, 'README.md') }
+
+ subject(:policy) { described_class.new(user, blob) }
+
+ where(:project_level, :feature_access_level, :membership, :expected_count) do
+ permission_table_for_guest_feature_access_and_non_private_project_only
+ end
+
+ with_them do
+ it "grants permission" do
+ update_feature_access_level(project, feature_access_level)
+
+ if expected_count == 1
+ expect(policy).to be_allowed(:read_blob)
+ else
+ expect(policy).to be_disallowed(:read_blob)
+ end
+ end
+ end
+end
diff --git a/spec/policies/wiki_page_policy_spec.rb b/spec/policies/wiki_page_policy_spec.rb
new file mode 100644
index 00000000000..e550ccf6d65
--- /dev/null
+++ b/spec/policies/wiki_page_policy_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe WikiPagePolicy do
+ include_context 'ProjectPolicyTable context'
+ include ProjectHelpers
+ using RSpec::Parameterized::TableSyntax
+
+ let(:project) { create(:project, :wiki_repo, project_level) }
+ let(:user) { create_user_from_membership(project, membership) }
+ let(:wiki_page) { create(:wiki_page, wiki: project.wiki) }
+
+ subject(:policy) { described_class.new(user, wiki_page) }
+
+ where(:project_level, :feature_access_level, :membership, :expected_count) do
+ permission_table_for_guest_feature_access
+ end
+
+ with_them do
+ it "grants permission" do
+ update_feature_access_level(project, feature_access_level)
+
+ if expected_count == 1
+ expect(policy).to be_allowed(:read_wiki_page)
+ else
+ expect(policy).to be_disallowed(:read_wiki_page)
+ end
+ end
+ end
+end
diff --git a/spec/presenters/hooks/project_hook_presenter_spec.rb b/spec/presenters/hooks/project_hook_presenter_spec.rb
new file mode 100644
index 00000000000..773e8ccf51e
--- /dev/null
+++ b/spec/presenters/hooks/project_hook_presenter_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ProjectHookPresenter do
+ let(:web_hook_log) { create(:web_hook_log) }
+ let(:project) { web_hook_log.web_hook.project }
+ let(:web_hook) { web_hook_log.web_hook }
+
+ describe '#logs_details_path' do
+ subject { web_hook.present.logs_details_path(web_hook_log) }
+
+ let(:expected_path) do
+ "/#{project.namespace.path}/#{project.name}/hooks/#{web_hook.id}/hook_logs/#{web_hook_log.id}"
+ end
+
+ it { is_expected.to eq(expected_path) }
+ end
+
+ describe '#logs_retry_path' do
+ subject { web_hook.present.logs_details_path(web_hook_log) }
+
+ let(:expected_path) do
+ "/#{project.namespace.path}/#{project.name}/hooks/#{web_hook.id}/hook_logs/#{web_hook_log.id}"
+ end
+
+ it { is_expected.to eq(expected_path) }
+ end
+end
diff --git a/spec/presenters/hooks/service_hook_presenter_spec.rb b/spec/presenters/hooks/service_hook_presenter_spec.rb
new file mode 100644
index 00000000000..bea57768e3e
--- /dev/null
+++ b/spec/presenters/hooks/service_hook_presenter_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ServiceHookPresenter do
+ let(:web_hook_log) { create(:web_hook_log, web_hook: service_hook) }
+ let(:service_hook) { create(:service_hook, service: service) }
+ let(:service) { create(:drone_ci_service, project: project) }
+ let(:project) { create(:project) }
+
+ describe '#logs_details_path' do
+ subject { service_hook.present.logs_details_path(web_hook_log) }
+
+ let(:expected_path) do
+ "/#{project.namespace.path}/#{project.name}/-/services/#{service.to_param}/hook_logs/#{web_hook_log.id}"
+ end
+
+ it { is_expected.to eq(expected_path) }
+ end
+
+ describe '#logs_retry_path' do
+ subject { service_hook.present.logs_retry_path(web_hook_log) }
+
+ let(:expected_path) do
+ "/#{project.namespace.path}/#{project.name}/-/services/#{service.to_param}/hook_logs/#{web_hook_log.id}/retry"
+ end
+
+ it { is_expected.to eq(expected_path) }
+ end
+end
diff --git a/spec/presenters/web_hook_log_presenter_spec.rb b/spec/presenters/web_hook_log_presenter_spec.rb
new file mode 100644
index 00000000000..8812a0ba594
--- /dev/null
+++ b/spec/presenters/web_hook_log_presenter_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe WebHookLogPresenter do
+ include Gitlab::Routing.url_helpers
+
+ describe '#details_path' do
+ let(:web_hook_log) { create(:web_hook_log, web_hook: web_hook) }
+ let(:project) { create(:project) }
+
+ subject { web_hook_log.present.details_path }
+
+ context 'project hook' do
+ let(:web_hook) { create(:project_hook, project: project) }
+
+ it { is_expected.to eq(project_hook_hook_log_path(project, web_hook, web_hook_log)) }
+ end
+
+ context 'service hook' do
+ let(:web_hook) { create(:service_hook, service: service) }
+ let(:service) { create(:drone_ci_service, project: project) }
+
+ it { is_expected.to eq(project_service_hook_log_path(project, service, web_hook_log)) }
+ end
+ end
+
+ describe '#retry_path' do
+ let(:web_hook_log) { create(:web_hook_log, web_hook: web_hook) }
+ let(:project) { create(:project) }
+
+ subject { web_hook_log.present.retry_path }
+
+ context 'project hook' do
+ let(:web_hook) { create(:project_hook, project: project) }
+
+ it { is_expected.to eq(retry_project_hook_hook_log_path(project, web_hook, web_hook_log)) }
+ end
+
+ context 'service hook' do
+ let(:web_hook) { create(:service_hook, service: service) }
+ let(:service) { create(:drone_ci_service, project: project) }
+
+ it { is_expected.to eq(retry_project_service_hook_log_path(project, service, web_hook_log)) }
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb
new file mode 100644
index 00000000000..0e8fe4987b9
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Mark snippet as spam' do
+ include GraphqlHelpers
+
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:other_user) { create(:user) }
+ let_it_be(:snippet) { create(:personal_snippet) }
+ let_it_be(:user_agent_detail) { create(:user_agent_detail, subject: snippet) }
+ let(:current_user) { snippet.author }
+ let(:mutation) do
+ variables = {
+ id: snippet.to_global_id.to_s
+ }
+
+ graphql_mutation(:mark_as_spam_snippet, variables)
+ end
+
+ def mutation_response
+ graphql_mutation_response(:mark_as_spam_snippet)
+ end
+
+ shared_examples 'does not mark the snippet as spam' do
+ it do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.not_to change { snippet.reload.user_agent_detail.submitted }
+ end
+ end
+
+ context 'when the user does not have permission' do
+ let(:current_user) { other_user }
+
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
+
+ it_behaves_like 'does not mark the snippet as spam'
+ end
+
+ context 'when the user has permission' do
+ context 'when user can not mark snippet as spam' do
+ it_behaves_like 'does not mark the snippet as spam'
+ end
+
+ context 'when user can mark snippet as spam' do
+ let(:current_user) { admin }
+
+ before do
+ stub_application_setting(akismet_enabled: true)
+ end
+
+ it 'marks snippet as spam' do
+ expect_next_instance_of(SpamService) do |instance|
+ expect(instance).to receive(:mark_as_spam!)
+ end
+
+ post_graphql_mutation(mutation, current_user: current_user)
+ end
+ end
+ end
+end
diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb
index 2a4368868d5..d8f13bc2e61 100644
--- a/spec/services/web_hook_service_spec.rb
+++ b/spec/services/web_hook_service_spec.rb
@@ -203,17 +203,6 @@ describe WebHookService do
expect(hook_log.internal_error_message).to be_nil
end
end
-
- context 'should not log ServiceHooks' do
- let(:service_hook) { create(:service_hook) }
- let(:service_instance) { described_class.new(service_hook, data, 'service_hook') }
-
- before do
- stub_full_request(service_hook.url, method: :post).to_return(status: 200, body: 'Success')
- end
-
- it { expect { service_instance.execute }.not_to change(WebHookLog, :count) }
- end
end
end
diff --git a/spec/views/projects/services/edit.html.haml_spec.rb b/spec/views/projects/services/edit.html.haml_spec.rb
new file mode 100644
index 00000000000..12e1cda2c00
--- /dev/null
+++ b/spec/views/projects/services/edit.html.haml_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'projects/services/edit' do
+ let(:service) { create(:drone_ci_service, project: project) }
+ let(:project) { create(:project) }
+
+ before do
+ assign :project, project
+ assign :service, service
+ end
+
+ it do
+ render
+
+ expect(rendered).not_to have_text('Recent Deliveries')
+ end
+
+ context 'service using WebHooks' do
+ before do
+ assign(:web_hook_logs, [])
+ end
+
+ it do
+ render
+
+ expect(rendered).to have_text('Recent Deliveries')
+ end
+ end
+end