summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-04-13 15:09:20 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-04-13 15:09:20 +0000
commitb77fb04678a4e76d025048e9846adc2ac709414a (patch)
treec65f719e326e1d33d313b5e9d8b3f72366ad7bd2 /spec
parent75ee59f7a108cf0c57e1e66e3ef5e439bae24fcd (diff)
downloadgitlab-ce-b77fb04678a4e76d025048e9846adc2ac709414a.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/frontend/logs/components/environment_logs_spec.js5
-rw-r--r--spec/frontend/logs/components/log_advanced_filters_spec.js101
-rw-r--r--spec/frontend/logs/components/tokens/token_with_loading_state_spec.js68
-rw-r--r--spec/frontend/logs/stores/actions_spec.js80
-rw-r--r--spec/policies/group_policy_spec.rb22
-rw-r--r--spec/policies/project_policy_spec.rb144
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb7
-rw-r--r--spec/support/shared_examples/policies/wiki_policies_shared_examples.rb152
8 files changed, 372 insertions, 207 deletions
diff --git a/spec/frontend/logs/components/environment_logs_spec.js b/spec/frontend/logs/components/environment_logs_spec.js
index d097610cb0a..9046253bdc6 100644
--- a/spec/frontend/logs/components/environment_logs_spec.js
+++ b/spec/frontend/logs/components/environment_logs_spec.js
@@ -10,7 +10,6 @@ import {
mockPods,
mockLogsResult,
mockTrace,
- mockPodName,
mockEnvironmentsEndpoint,
mockDocumentationPath,
} from '../mock_data';
@@ -302,11 +301,11 @@ describe('EnvironmentLogs', () => {
});
it('refresh button, trace is refreshed', () => {
- expect(dispatch).not.toHaveBeenCalledWith(`${module}/showPodLogs`, expect.anything());
+ expect(dispatch).not.toHaveBeenCalledWith(`${module}/fetchLogs`, undefined);
findLogControlButtons().vm.$emit('refresh');
- expect(dispatch).toHaveBeenCalledWith(`${module}/showPodLogs`, mockPodName);
+ expect(dispatch).toHaveBeenCalledWith(`${module}/fetchLogs`, undefined);
});
});
});
diff --git a/spec/frontend/logs/components/log_advanced_filters_spec.js b/spec/frontend/logs/components/log_advanced_filters_spec.js
index a6fbc40c2c6..adcd6b4fb07 100644
--- a/spec/frontend/logs/components/log_advanced_filters_spec.js
+++ b/spec/frontend/logs/components/log_advanced_filters_spec.js
@@ -1,8 +1,9 @@
-import { GlIcon, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { defaultTimeRange } from '~/vue_shared/constants';
+import { GlFilteredSearch } from '@gitlab/ui';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
import { createStore } from '~/logs/stores';
+import { TOKEN_TYPE_POD_NAME } from '~/logs/constants';
import { mockPods, mockSearch } from '../mock_data';
import LogAdvancedFilters from '~/logs/components/log_advanced_filters.vue';
@@ -15,26 +16,19 @@ describe('LogAdvancedFilters', () => {
let wrapper;
let state;
- const findPodsDropdown = () => wrapper.find({ ref: 'podsDropdown' });
- const findPodsNoPodsText = () => wrapper.find({ ref: 'noPodsMsg' });
- const findPodsDropdownItems = () =>
- findPodsDropdown()
- .findAll(GlDropdownItem)
- .filter(item => !item.is('[disabled]'));
- const findPodsDropdownItemsSelected = () =>
- findPodsDropdownItems()
- .filter(item => {
- return !item.find(GlIcon).classes('invisible');
- })
- .at(0);
- const findSearchBox = () => wrapper.find({ ref: 'searchBox' });
+ const findFilteredSearch = () => wrapper.find(GlFilteredSearch);
const findTimeRangePicker = () => wrapper.find({ ref: 'dateTimePicker' });
+ const getSearchToken = type =>
+ findFilteredSearch()
+ .props('availableTokens')
+ .filter(token => token.type === type)[0];
const mockStateLoading = () => {
state.timeRange.selected = defaultTimeRange;
state.timeRange.current = convertToFixedRange(defaultTimeRange);
state.pods.options = [];
state.pods.current = null;
+ state.logs.isLoading = true;
};
const mockStateWithData = () => {
@@ -42,6 +36,7 @@ describe('LogAdvancedFilters', () => {
state.timeRange.current = convertToFixedRange(defaultTimeRange);
state.pods.options = mockPods;
state.pods.current = null;
+ state.logs.isLoading = false;
};
const initWrapper = (propsData = {}) => {
@@ -76,11 +71,18 @@ describe('LogAdvancedFilters', () => {
expect(wrapper.isVueInstance()).toBe(true);
expect(wrapper.isEmpty()).toBe(false);
- expect(findPodsDropdown().exists()).toBe(true);
- expect(findSearchBox().exists()).toBe(true);
+ expect(findFilteredSearch().exists()).toBe(true);
expect(findTimeRangePicker().exists()).toBe(true);
});
+ it('displays search tokens', () => {
+ expect(getSearchToken(TOKEN_TYPE_POD_NAME)).toMatchObject({
+ title: 'Pod name',
+ unique: true,
+ operators: [expect.objectContaining({ value: '=' })],
+ });
+ });
+
describe('disabled state', () => {
beforeEach(() => {
mockStateLoading();
@@ -90,9 +92,7 @@ describe('LogAdvancedFilters', () => {
});
it('displays disabled filters', () => {
- expect(findPodsDropdown().props('text')).toBe('All pods');
- expect(findPodsDropdown().attributes('disabled')).toBeTruthy();
- expect(findSearchBox().attributes('disabled')).toBeTruthy();
+ expect(findFilteredSearch().attributes('disabled')).toBeTruthy();
expect(findTimeRangePicker().attributes('disabled')).toBeTruthy();
});
});
@@ -103,16 +103,17 @@ describe('LogAdvancedFilters', () => {
initWrapper();
});
- it('displays a enabled filters', () => {
- expect(findPodsDropdown().props('text')).toBe('All pods');
- expect(findPodsDropdown().attributes('disabled')).toBeFalsy();
- expect(findSearchBox().attributes('disabled')).toBeFalsy();
+ it('displays a disabled search', () => {
+ expect(findFilteredSearch().attributes('disabled')).toBeTruthy();
+ });
+
+ it('displays an enable date filter', () => {
expect(findTimeRangePicker().attributes('disabled')).toBeFalsy();
});
- it('displays an empty pods dropdown', () => {
- expect(findPodsNoPodsText().exists()).toBe(true);
- expect(findPodsDropdownItems()).toHaveLength(0);
+ it('displays no pod options when no pods are available, so suggestions can be displayed', () => {
+ expect(getSearchToken(TOKEN_TYPE_POD_NAME).options).toBe(null);
+ expect(getSearchToken(TOKEN_TYPE_POD_NAME).loading).toBe(true);
});
});
@@ -122,20 +123,24 @@ describe('LogAdvancedFilters', () => {
initWrapper();
});
- it('displays an enabled pods dropdown', () => {
- expect(findPodsDropdown().attributes('disabled')).toBeFalsy();
- expect(findPodsDropdown().props('text')).toBe('All pods');
+ it('displays a single token for pods', () => {
+ initWrapper();
+
+ const tokens = findFilteredSearch().props('availableTokens');
+
+ expect(tokens).toHaveLength(1);
+ expect(tokens[0].type).toBe(TOKEN_TYPE_POD_NAME);
});
- it('displays options in a pods dropdown', () => {
- const items = findPodsDropdownItems();
- expect(items).toHaveLength(mockPods.length + 1);
+ it('displays a enabled filters', () => {
+ expect(findFilteredSearch().attributes('disabled')).toBeFalsy();
+ expect(findTimeRangePicker().attributes('disabled')).toBeFalsy();
});
- it('displays "all pods" selected in a pods dropdown', () => {
- const selected = findPodsDropdownItemsSelected();
+ it('displays options in the pods token', () => {
+ const { options } = getSearchToken(TOKEN_TYPE_POD_NAME);
- expect(selected.text()).toBe('All pods');
+ expect(options).toHaveLength(mockPods.length);
});
it('displays options in date time picker', () => {
@@ -146,30 +151,16 @@ describe('LogAdvancedFilters', () => {
});
describe('when the user interacts', () => {
- it('clicks on a all options, showPodLogs is dispatched with null', () => {
- const items = findPodsDropdownItems();
- items.at(0).vm.$emit('click');
-
- expect(dispatch).toHaveBeenCalledWith(`${module}/showPodLogs`, null);
- });
-
- it('clicks on a pod name, showPodLogs is dispatched with pod name', () => {
- const items = findPodsDropdownItems();
- const index = 2; // any pod
+ it('clicks on the search button, showFilteredLogs is dispatched', () => {
+ findFilteredSearch().vm.$emit('submit', null);
- items.at(index + 1).vm.$emit('click'); // skip "All pods" option
-
- expect(dispatch).toHaveBeenCalledWith(`${module}/showPodLogs`, mockPods[index]);
+ expect(dispatch).toHaveBeenCalledWith(`${module}/showFilteredLogs`, null);
});
- it('clicks on search, a serches is done', () => {
- expect(findSearchBox().attributes('disabled')).toBeFalsy();
-
- // input a query and click `search`
- findSearchBox().vm.$emit('input', mockSearch);
- findSearchBox().vm.$emit('submit');
+ it('clicks on the search button, showFilteredLogs is dispatched with null', () => {
+ findFilteredSearch().vm.$emit('submit', [mockSearch]);
- expect(dispatch).toHaveBeenCalledWith(`${module}/setSearch`, mockSearch);
+ expect(dispatch).toHaveBeenCalledWith(`${module}/showFilteredLogs`, [mockSearch]);
});
it('selects a new time range', () => {
diff --git a/spec/frontend/logs/components/tokens/token_with_loading_state_spec.js b/spec/frontend/logs/components/tokens/token_with_loading_state_spec.js
new file mode 100644
index 00000000000..d98d7d05c92
--- /dev/null
+++ b/spec/frontend/logs/components/tokens/token_with_loading_state_spec.js
@@ -0,0 +1,68 @@
+import { GlFilteredSearchToken, GlLoadingIcon } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+
+import TokenWithLoadingState from '~/logs/components/tokens/token_with_loading_state.vue';
+
+describe('TokenWithLoadingState', () => {
+ let wrapper;
+
+ const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
+ const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+
+ const initWrapper = (props = {}, options) => {
+ wrapper = shallowMount(TokenWithLoadingState, {
+ propsData: props,
+ ...options,
+ });
+ };
+
+ beforeEach(() => {});
+
+ it('passes entire config correctly', () => {
+ const config = {
+ icon: 'pod',
+ type: 'pod',
+ title: 'Pod name',
+ unique: true,
+ };
+
+ initWrapper({ config });
+
+ expect(findFilteredSearchToken().props('config')).toEqual(config);
+ });
+
+ describe('suggestions are replaced', () => {
+ let mockNoOptsText;
+ let config;
+ let stubs;
+
+ beforeEach(() => {
+ mockNoOptsText = 'No suggestions available';
+ config = {
+ loading: false,
+ noOptionsText: mockNoOptsText,
+ };
+ stubs = {
+ GlFilteredSearchToken: {
+ template: `<div><slot name="suggestions"></slot></div>`,
+ },
+ };
+ });
+
+ it('renders a loading icon', () => {
+ config.loading = true;
+
+ initWrapper({ config }, { stubs });
+
+ expect(findLoadingIcon().exists()).toBe(true);
+ expect(wrapper.text()).toBe('');
+ });
+
+ it('renders an empty results message', () => {
+ initWrapper({ config }, { stubs });
+
+ expect(findLoadingIcon().exists()).toBe(false);
+ expect(wrapper.text()).toBe(mockNoOptsText);
+ });
+ });
+});
diff --git a/spec/frontend/logs/stores/actions_spec.js b/spec/frontend/logs/stores/actions_spec.js
index 882673af984..6199c400e16 100644
--- a/spec/frontend/logs/stores/actions_spec.js
+++ b/spec/frontend/logs/stores/actions_spec.js
@@ -6,7 +6,7 @@ import { convertToFixedRange } from '~/lib/utils/datetime_range';
import logsPageState from '~/logs/stores/state';
import {
setInitData,
- setSearch,
+ showFilteredLogs,
showPodLogs,
fetchEnvironments,
fetchLogs,
@@ -31,6 +31,7 @@ import {
mockCursor,
mockNextCursor,
} from '../mock_data';
+import { TOKEN_TYPE_POD_NAME } from '~/logs/constants';
jest.mock('~/flash');
jest.mock('~/lib/utils/datetime_range');
@@ -93,13 +94,80 @@ describe('Logs Store actions', () => {
));
});
- describe('setSearch', () => {
- it('should commit search mutation', () =>
+ describe('showFilteredLogs', () => {
+ it('empty search should filter with defaults', () =>
testAction(
- setSearch,
- mockSearch,
+ showFilteredLogs,
+ undefined,
state,
- [{ type: types.SET_SEARCH, payload: mockSearch }],
+ [
+ { type: types.SET_CURRENT_POD_NAME, payload: null },
+ { type: types.SET_SEARCH, payload: '' },
+ ],
+ [{ type: 'fetchLogs' }],
+ ));
+
+ it('text search should filter with a search term', () =>
+ testAction(
+ showFilteredLogs,
+ [mockSearch],
+ state,
+ [
+ { type: types.SET_CURRENT_POD_NAME, payload: null },
+ { type: types.SET_SEARCH, payload: mockSearch },
+ ],
+ [{ type: 'fetchLogs' }],
+ ));
+
+ it('pod search should filter with a search term', () =>
+ testAction(
+ showFilteredLogs,
+ [{ type: TOKEN_TYPE_POD_NAME, value: { data: mockPodName, operator: '=' } }],
+ state,
+ [
+ { type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
+ { type: types.SET_SEARCH, payload: '' },
+ ],
+ [{ type: 'fetchLogs' }],
+ ));
+
+ it('pod search should filter with a pod selection and a search term', () =>
+ testAction(
+ showFilteredLogs,
+ [{ type: TOKEN_TYPE_POD_NAME, value: { data: mockPodName, operator: '=' } }, mockSearch],
+ state,
+ [
+ { type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
+ { type: types.SET_SEARCH, payload: mockSearch },
+ ],
+ [{ type: 'fetchLogs' }],
+ ));
+
+ it('pod search should filter with a pod selection and two search terms', () =>
+ testAction(
+ showFilteredLogs,
+ ['term1', 'term2'],
+ state,
+ [
+ { type: types.SET_CURRENT_POD_NAME, payload: null },
+ { type: types.SET_SEARCH, payload: `term1 term2` },
+ ],
+ [{ type: 'fetchLogs' }],
+ ));
+
+ it('pod search should filter with a pod selection and a search terms before and after', () =>
+ testAction(
+ showFilteredLogs,
+ [
+ 'term1',
+ { type: TOKEN_TYPE_POD_NAME, value: { data: mockPodName, operator: '=' } },
+ 'term2',
+ ],
+ state,
+ [
+ { type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
+ { type: types.SET_SEARCH, payload: `term1 term2` },
+ ],
[{ type: 'fetchLogs' }],
));
});
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index 5a9ca9f7b7e..13f1bcb389a 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -655,4 +655,26 @@ describe GroupPolicy do
end
end
end
+
+ it_behaves_like 'model with wiki policies' do
+ let(:container) { create(:group) }
+
+ def set_access_level(access_level)
+ allow(container).to receive(:wiki_access_level).and_return(access_level)
+ end
+
+ before do
+ stub_feature_flags(group_wiki: true)
+ end
+
+ context 'when the feature flag is disabled' do
+ before do
+ stub_feature_flags(group_wiki: false)
+ end
+
+ it 'does not include the wiki permissions' do
+ expect_disallowed(*permissions)
+ end
+ end
+ end
end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index d098369e124..db643e3a31f 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -121,147 +121,11 @@ describe ProjectPolicy do
expect(Ability).not_to be_allowed(user, :read_issue, project)
end
- context 'wiki feature' do
- let(:permissions) { %i(read_wiki create_wiki update_wiki admin_wiki download_wiki_code) }
+ it_behaves_like 'model with wiki policies' do
+ let(:container) { project }
- subject { described_class.new(owner, project) }
-
- context 'when the feature is disabled' do
- before do
- project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED)
- end
-
- it 'does not include the wiki permissions' do
- expect_disallowed(*permissions)
- end
-
- context 'when there is an external wiki' do
- it 'does not include the wiki permissions' do
- allow(project).to receive(:has_external_wiki?).and_return(true)
-
- expect_disallowed(*permissions)
- end
- end
- end
-
- describe 'read_wiki' do
- subject { described_class.new(user, project) }
-
- member_roles = %i[guest developer]
- stranger_roles = %i[anonymous non_member]
-
- user_roles = stranger_roles + member_roles
-
- # When a user is anonymous, their `current_user == nil`
- let(:user) { create(:user) unless user_role == :anonymous }
-
- before do
- project.visibility = project_visibility
- project.project_feature.update_attribute(:wiki_access_level, wiki_access_level)
- project.add_user(user, user_role) if member_roles.include?(user_role)
- end
-
- title = ->(project_visibility, wiki_access_level, user_role) do
- [
- "project is #{Gitlab::VisibilityLevel.level_name project_visibility}",
- "wiki is #{ProjectFeature.str_from_access_level wiki_access_level}",
- "user is #{user_role}"
- ].join(', ')
- end
-
- describe 'Situations where :read_wiki is always false' do
- where(case_names: title,
- project_visibility: Gitlab::VisibilityLevel.options.values,
- wiki_access_level: [ProjectFeature::DISABLED],
- user_role: user_roles)
-
- with_them do
- it { is_expected.to be_disallowed(:read_wiki) }
- end
- end
-
- describe 'Situations where :read_wiki is always true' do
- where(case_names: title,
- project_visibility: [Gitlab::VisibilityLevel::PUBLIC],
- wiki_access_level: [ProjectFeature::ENABLED],
- user_role: user_roles)
-
- with_them do
- it { is_expected.to be_allowed(:read_wiki) }
- end
- end
-
- describe 'Situations where :read_wiki requires project membership' do
- context 'the wiki is private, and the user is a member' do
- where(case_names: title,
- project_visibility: [Gitlab::VisibilityLevel::PUBLIC,
- Gitlab::VisibilityLevel::INTERNAL],
- wiki_access_level: [ProjectFeature::PRIVATE],
- user_role: member_roles)
-
- with_them do
- it { is_expected.to be_allowed(:read_wiki) }
- end
- end
-
- context 'the wiki is private, and the user is not member' do
- where(case_names: title,
- project_visibility: [Gitlab::VisibilityLevel::PUBLIC,
- Gitlab::VisibilityLevel::INTERNAL],
- wiki_access_level: [ProjectFeature::PRIVATE],
- user_role: stranger_roles)
-
- with_them do
- it { is_expected.to be_disallowed(:read_wiki) }
- end
- end
-
- context 'the wiki is enabled, and the user is a member' do
- where(case_names: title,
- project_visibility: [Gitlab::VisibilityLevel::PRIVATE],
- wiki_access_level: [ProjectFeature::ENABLED],
- user_role: member_roles)
-
- with_them do
- it { is_expected.to be_allowed(:read_wiki) }
- end
- end
-
- context 'the wiki is enabled, and the user is not a member' do
- where(case_names: title,
- project_visibility: [Gitlab::VisibilityLevel::PRIVATE],
- wiki_access_level: [ProjectFeature::ENABLED],
- user_role: stranger_roles)
-
- with_them do
- it { is_expected.to be_disallowed(:read_wiki) }
- end
- end
- end
-
- describe 'Situations where :read_wiki prohibits anonymous access' do
- context 'the user is not anonymous' do
- where(case_names: title,
- project_visibility: [Gitlab::VisibilityLevel::INTERNAL],
- wiki_access_level: [ProjectFeature::ENABLED, ProjectFeature::PUBLIC],
- user_role: user_roles.reject { |u| u == :anonymous })
-
- with_them do
- it { is_expected.to be_allowed(:read_wiki) }
- end
- end
-
- context 'the user is not anonymous' do
- where(case_names: title,
- project_visibility: [Gitlab::VisibilityLevel::INTERNAL],
- wiki_access_level: [ProjectFeature::ENABLED, ProjectFeature::PUBLIC],
- user_role: %i[anonymous])
-
- with_them do
- it { is_expected.to be_disallowed(:read_wiki) }
- end
- end
- end
+ def set_access_level(access_level)
+ project.project_feature.update_attribute(:wiki_access_level, access_level)
end
end
diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
index 4f81a71f586..c2797c49c02 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -14,16 +14,17 @@ RSpec.shared_context 'GroupPolicy context' do
%i[
read_label read_group upload_file read_namespace read_group_activity
read_group_issues read_group_boards read_group_labels read_group_milestones
- read_group_merge_requests
+ read_group_merge_requests read_wiki
]
end
let(:read_group_permissions) { %i[read_label read_list read_milestone read_board] }
- let(:reporter_permissions) { %i[admin_label read_container_image read_metrics_dashboard_annotation] }
- let(:developer_permissions) { %i[admin_milestone create_metrics_dashboard_annotation delete_metrics_dashboard_annotation update_metrics_dashboard_annotation] }
+ let(:reporter_permissions) { %i[admin_label read_container_image read_metrics_dashboard_annotation download_wiki_code] }
+ let(:developer_permissions) { %i[admin_milestone create_metrics_dashboard_annotation delete_metrics_dashboard_annotation update_metrics_dashboard_annotation create_wiki] }
let(:maintainer_permissions) do
%i[
create_projects
read_cluster create_cluster update_cluster admin_cluster add_cluster
+ admin_wiki
]
end
let(:owner_permissions) do
diff --git a/spec/support/shared_examples/policies/wiki_policies_shared_examples.rb b/spec/support/shared_examples/policies/wiki_policies_shared_examples.rb
new file mode 100644
index 00000000000..b91500ffd9c
--- /dev/null
+++ b/spec/support/shared_examples/policies/wiki_policies_shared_examples.rb
@@ -0,0 +1,152 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'model with wiki policies' do
+ let(:container) { raise NotImplementedError }
+ let(:permissions) { %i(read_wiki create_wiki update_wiki admin_wiki download_wiki_code) }
+
+ # TODO: Remove this helper once we implement group features
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/208412
+ def set_access_level(access_level)
+ raise NotImplementedError
+ end
+
+ subject { described_class.new(owner, container) }
+
+ context 'when the feature is disabled' do
+ before do
+ set_access_level(ProjectFeature::DISABLED)
+ end
+
+ it 'does not include the wiki permissions' do
+ expect_disallowed(*permissions)
+ end
+
+ context 'when there is an external wiki' do
+ it 'does not include the wiki permissions' do
+ allow(container).to receive(:has_external_wiki?).and_return(true)
+
+ expect_disallowed(*permissions)
+ end
+ end
+ end
+
+ describe 'read_wiki' do
+ subject { described_class.new(user, container) }
+
+ member_roles = %i[guest developer]
+ stranger_roles = %i[anonymous non_member]
+
+ user_roles = stranger_roles + member_roles
+
+ # When a user is anonymous, their `current_user == nil`
+ let(:user) { create(:user) unless user_role == :anonymous }
+
+ before do
+ container.visibility = container_visibility
+ set_access_level(wiki_access_level)
+ container.add_user(user, user_role) if member_roles.include?(user_role)
+ end
+
+ title = ->(container_visibility, wiki_access_level, user_role) do
+ [
+ "container is #{Gitlab::VisibilityLevel.level_name container_visibility}",
+ "wiki is #{ProjectFeature.str_from_access_level wiki_access_level}",
+ "user is #{user_role}"
+ ].join(', ')
+ end
+
+ describe 'Situations where :read_wiki is always false' do
+ where(case_names: title,
+ container_visibility: Gitlab::VisibilityLevel.options.values,
+ wiki_access_level: [ProjectFeature::DISABLED],
+ user_role: user_roles)
+
+ with_them do
+ it { is_expected.to be_disallowed(:read_wiki) }
+ end
+ end
+
+ describe 'Situations where :read_wiki is always true' do
+ where(case_names: title,
+ container_visibility: [Gitlab::VisibilityLevel::PUBLIC],
+ wiki_access_level: [ProjectFeature::ENABLED],
+ user_role: user_roles)
+
+ with_them do
+ it { is_expected.to be_allowed(:read_wiki) }
+ end
+ end
+
+ describe 'Situations where :read_wiki requires membership' do
+ context 'the wiki is private, and the user is a member' do
+ where(case_names: title,
+ container_visibility: [Gitlab::VisibilityLevel::PUBLIC,
+ Gitlab::VisibilityLevel::INTERNAL],
+ wiki_access_level: [ProjectFeature::PRIVATE],
+ user_role: member_roles)
+
+ with_them do
+ it { is_expected.to be_allowed(:read_wiki) }
+ end
+ end
+
+ context 'the wiki is private, and the user is not member' do
+ where(case_names: title,
+ container_visibility: [Gitlab::VisibilityLevel::PUBLIC,
+ Gitlab::VisibilityLevel::INTERNAL],
+ wiki_access_level: [ProjectFeature::PRIVATE],
+ user_role: stranger_roles)
+
+ with_them do
+ it { is_expected.to be_disallowed(:read_wiki) }
+ end
+ end
+
+ context 'the wiki is enabled, and the user is a member' do
+ where(case_names: title,
+ container_visibility: [Gitlab::VisibilityLevel::PRIVATE],
+ wiki_access_level: [ProjectFeature::ENABLED],
+ user_role: member_roles)
+
+ with_them do
+ it { is_expected.to be_allowed(:read_wiki) }
+ end
+ end
+
+ context 'the wiki is enabled, and the user is not a member' do
+ where(case_names: title,
+ container_visibility: [Gitlab::VisibilityLevel::PRIVATE],
+ wiki_access_level: [ProjectFeature::ENABLED],
+ user_role: stranger_roles)
+
+ with_them do
+ it { is_expected.to be_disallowed(:read_wiki) }
+ end
+ end
+ end
+
+ describe 'Situations where :read_wiki prohibits anonymous access' do
+ context 'the user is not anonymous' do
+ where(case_names: title,
+ container_visibility: [Gitlab::VisibilityLevel::INTERNAL],
+ wiki_access_level: [ProjectFeature::ENABLED, ProjectFeature::PUBLIC],
+ user_role: user_roles.reject { |u| u == :anonymous })
+
+ with_them do
+ it { is_expected.to be_allowed(:read_wiki) }
+ end
+ end
+
+ context 'the user is anonymous' do
+ where(case_names: title,
+ container_visibility: [Gitlab::VisibilityLevel::INTERNAL],
+ wiki_access_level: [ProjectFeature::ENABLED, ProjectFeature::PUBLIC],
+ user_role: %i[anonymous])
+
+ with_them do
+ it { is_expected.to be_disallowed(:read_wiki) }
+ end
+ end
+ end
+ end
+end