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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-05-04 15:09:46 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-05-04 15:09:46 +0300
commit72797f4a602d0061636df39df89e11896de2a524 (patch)
treefbd4397be74910e44aaafda4093ea1e6f499d445 /spec
parent5b4eca2afd809fbfba3bdcacabe547025ebe7f43 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb54
-rw-r--r--spec/frontend/monitoring/components/dashboard_spec.js29
-rw-r--r--spec/frontend/notes/components/discussion_counter_spec.js9
-rw-r--r--spec/frontend/sidebar/assignees_realtime_spec.js100
-rw-r--r--spec/frontend/sidebar/sidebar_assignees_spec.js46
-rw-r--r--spec/graphql/types/user_type_spec.rb2
-rw-r--r--spec/policies/design_management/design_policy_spec.rb174
7 files changed, 372 insertions, 42 deletions
diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
index b8a5a4036a5..0e30df518d7 100644
--- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
+++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
@@ -43,7 +43,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
context 'single thread' do
it 'shows text with how many threads' do
page.within '.line-resolve-all-container' do
- expect(page).to have_content('0/1 thread resolved')
+ expect(page).to have_content('1 unresolved thread')
end
end
@@ -60,7 +60,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('1/1 thread resolved')
+ expect(page).to have_content('All threads resolved')
expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -77,7 +77,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('1/1 thread resolved')
+ expect(page).to have_content('All threads resolved')
expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -89,7 +89,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('0/1 thread resolved')
+ expect(page).to have_content('1 unresolved thread')
end
end
@@ -162,7 +162,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('1/1 thread resolved')
+ expect(page).to have_content('All threads resolved')
end
end
@@ -174,7 +174,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('0/1 thread resolved')
+ expect(page).to have_content('1 unresolved thread')
expect(page).not_to have_selector('.line-resolve-btn.is-active')
end
end
@@ -189,7 +189,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('0/1 thread resolved')
+ expect(page).to have_content('1 unresolved thread')
end
end
end
@@ -203,7 +203,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('1/1 thread resolved')
+ expect(page).to have_content('All threads resolved')
expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -218,7 +218,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('1/1 thread resolved')
+ expect(page).to have_content('All threads resolved')
expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -275,7 +275,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
expect(page).to have_content('Last updated')
page.within '.line-resolve-all-container' do
- expect(page).to have_content('0/1 thread resolved')
+ expect(page).to have_content('1 unresolved thread')
end
end
@@ -292,7 +292,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('1/1 thread resolved')
+ expect(page).to have_content('All threads resolved')
end
end
end
@@ -305,7 +305,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
it 'shows text with how many threads' do
page.within '.line-resolve-all-container' do
- expect(page).to have_content('0/2 threads resolved')
+ expect(page).to have_content('2 unresolved threads')
end
end
@@ -313,7 +313,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
click_button('Resolve thread', match: :first)
page.within '.line-resolve-all-container' do
- expect(page).to have_content('1/2 threads resolved')
+ expect(page).to have_content('1 unresolved thread')
end
end
@@ -323,7 +323,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('2/2 threads resolved')
+ expect(page).to have_content('All threads resolved')
expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -336,7 +336,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('2/2 threads resolved')
+ expect(page).to have_content('All threads resolved')
expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -392,7 +392,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
context 'changes tab' do
it 'shows text with how many threads' do
page.within '.line-resolve-all-container' do
- expect(page).to have_content('0/1 thread resolved')
+ expect(page).to have_content('1 unresolved thread')
end
end
@@ -408,7 +408,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('1/1 thread resolved')
+ expect(page).to have_content('All threads resolved')
expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -423,7 +423,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('1/1 thread resolved')
+ expect(page).to have_content('All threads resolved')
expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -435,7 +435,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('0/1 thread resolved')
+ expect(page).to have_content('1 unresolved thread')
end
end
@@ -449,7 +449,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('1/1 thread resolved')
+ expect(page).to have_content('All threads resolved')
expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -466,7 +466,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('0/1 thread resolved')
+ expect(page).to have_content('1 unresolved thread')
end
end
end
@@ -489,7 +489,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('0/1 thread resolved')
+ expect(page).to have_content('1 unresolved thread')
end
end
@@ -519,7 +519,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('1/1 thread resolved')
+ expect(page).to have_content('All threads resolved')
expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
@@ -538,7 +538,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
page.within '.line-resolve-all-container' do
- expect(page).to have_content('0/1 thread resolved')
+ expect(page).to have_content('1 unresolved thread')
end
end
end
@@ -550,17 +550,17 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
it 'shows resolved icon' do
- expect(page).to have_content '1/1 thread resolved'
+ expect(page).to have_content 'All threads resolved'
click_button 'Toggle thread'
expect(page).to have_selector('.line-resolve-btn.is-active')
end
it 'does not allow user to click resolve button' do
- expect(page).to have_selector('.line-resolve-btn.is-disabled')
+ expect(page).to have_selector('.line-resolve-btn.is-active')
click_button 'Toggle thread'
- expect(page).to have_selector('.line-resolve-btn.is-disabled')
+ expect(page).to have_selector('.line-resolve-btn.is-active')
end
end
end
diff --git a/spec/frontend/monitoring/components/dashboard_spec.js b/spec/frontend/monitoring/components/dashboard_spec.js
index 6ac5248759b..24883e9055e 100644
--- a/spec/frontend/monitoring/components/dashboard_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_spec.js
@@ -1,5 +1,6 @@
import { shallowMount, mount } from '@vue/test-utils';
import Tracking from '~/tracking';
+import { ESC_KEY, ESC_KEY_IE11 } from '~/lib/utils/keys';
import { GlModal, GlDropdownItem, GlDeprecatedButton } from '@gitlab/ui';
import VueDraggable from 'vuedraggable';
import MockAdapter from 'axios-mock-adapter';
@@ -248,6 +249,8 @@ describe('Dashboard', () => {
let group;
let panel;
+ const mockKeyup = key => window.dispatchEvent(new KeyboardEvent('keyup', { key }));
+
const MockPanel = {
template: `<div><slot name="topLeft"/></div>`,
};
@@ -265,6 +268,9 @@ describe('Dashboard', () => {
group,
panel,
});
+
+ jest.spyOn(store, 'dispatch');
+
return wrapper.vm.$nextTick();
});
@@ -289,17 +295,30 @@ describe('Dashboard', () => {
});
it('restores full dashboard by clicking `back`', () => {
- const backBtn = wrapper.find({ ref: 'goBackBtn' });
- expect(backBtn.exists()).toBe(true);
-
- jest.spyOn(store, 'dispatch');
- backBtn.vm.$emit('click');
+ wrapper.find({ ref: 'goBackBtn' }).vm.$emit('click');
expect(store.dispatch).toHaveBeenCalledWith(
'monitoringDashboard/clearExpandedPanel',
undefined,
);
});
+
+ it('restores dashboard from full screen by typing the Escape key', () => {
+ mockKeyup(ESC_KEY);
+ expect(store.dispatch).toHaveBeenCalledWith(
+ `monitoringDashboard/clearExpandedPanel`,
+ undefined,
+ );
+ });
+
+ it('restores dashboard from full screen by typing the Escape key on IE11', () => {
+ mockKeyup(ESC_KEY_IE11);
+
+ expect(store.dispatch).toHaveBeenCalledWith(
+ `monitoringDashboard/clearExpandedPanel`,
+ undefined,
+ );
+ });
});
});
diff --git a/spec/frontend/notes/components/discussion_counter_spec.js b/spec/frontend/notes/components/discussion_counter_spec.js
index 77603c16f82..04535aa17c5 100644
--- a/spec/frontend/notes/components/discussion_counter_spec.js
+++ b/spec/frontend/notes/components/discussion_counter_spec.js
@@ -75,15 +75,14 @@ describe('DiscussionCounter component', () => {
});
it.each`
- title | resolved | isActive | icon | groupLength
- ${'not allResolved'} | ${false} | ${false} | ${'check-circle'} | ${3}
- ${'allResolved'} | ${true} | ${true} | ${'check-circle-filled'} | ${1}
- `('renders correctly if $title', ({ resolved, isActive, icon, groupLength }) => {
+ title | resolved | isActive | groupLength
+ ${'not allResolved'} | ${false} | ${false} | ${3}
+ ${'allResolved'} | ${true} | ${true} | ${1}
+ `('renders correctly if $title', ({ resolved, isActive, groupLength }) => {
updateStore({ resolvable: true, resolved });
wrapper = shallowMount(DiscussionCounter, { store, localVue });
expect(wrapper.find(`.is-active`).exists()).toBe(isActive);
- expect(wrapper.find({ name: icon }).exists()).toBe(true);
expect(wrapper.findAll('[role="group"').length).toBe(groupLength);
});
});
diff --git a/spec/frontend/sidebar/assignees_realtime_spec.js b/spec/frontend/sidebar/assignees_realtime_spec.js
new file mode 100644
index 00000000000..d6a6ca18fe8
--- /dev/null
+++ b/spec/frontend/sidebar/assignees_realtime_spec.js
@@ -0,0 +1,100 @@
+import { shallowMount } from '@vue/test-utils';
+import ActionCable from '@rails/actioncable';
+import AssigneesRealtime from '~/sidebar/components/assignees/assignees_realtime.vue';
+import SidebarMediator from '~/sidebar/sidebar_mediator';
+import Mock from './mock_data';
+import query from '~/issuable_sidebar/queries/issue_sidebar.query.graphql';
+
+jest.mock('@rails/actioncable', () => {
+ const mockConsumer = { subscriptions: { create: jest.fn() } };
+ return {
+ createConsumer: jest.fn().mockReturnValue(mockConsumer),
+ };
+});
+
+describe('Assignees Realtime', () => {
+ let wrapper;
+ let mediator;
+
+ const createComponent = () => {
+ wrapper = shallowMount(AssigneesRealtime, {
+ propsData: {
+ issuableIid: '1',
+ mediator,
+ projectPath: 'path/to/project',
+ },
+ mocks: {
+ $apollo: {
+ query,
+ queries: {
+ project: {
+ refetch: jest.fn(),
+ },
+ },
+ },
+ },
+ });
+ };
+
+ beforeEach(() => {
+ mediator = new SidebarMediator(Mock.mediator);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ SidebarMediator.singleton = null;
+ });
+
+ describe('when handleFetchResult is called from smart query', () => {
+ it('sets assignees to the store', () => {
+ const data = {
+ project: {
+ issue: {
+ assignees: {
+ nodes: [{ id: 'gid://gitlab/Environments/123', avatarUrl: 'url' }],
+ },
+ },
+ },
+ };
+ const expected = [{ id: 123, avatar_url: 'url', avatarUrl: 'url' }];
+ createComponent();
+
+ wrapper.vm.handleFetchResult({ data });
+
+ expect(mediator.store.assignees).toEqual(expected);
+ });
+ });
+
+ describe('when mounted', () => {
+ it('calls create subscription', () => {
+ const cable = ActionCable.createConsumer();
+
+ createComponent();
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(cable.subscriptions.create).toHaveBeenCalledTimes(1);
+ expect(cable.subscriptions.create).toHaveBeenCalledWith(
+ {
+ channel: 'IssuesChannel',
+ iid: wrapper.props('issuableIid'),
+ project_path: wrapper.props('projectPath'),
+ },
+ { received: wrapper.vm.received },
+ );
+ });
+ });
+ });
+
+ describe('when subscription is recieved', () => {
+ it('refetches the GraphQL project query', () => {
+ createComponent();
+
+ wrapper.vm.received({ event: 'updated' });
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.$apollo.queries.project.refetch).toHaveBeenCalledTimes(1);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/sidebar_assignees_spec.js b/spec/frontend/sidebar/sidebar_assignees_spec.js
index c1876066a21..88e2d2c9514 100644
--- a/spec/frontend/sidebar/sidebar_assignees_spec.js
+++ b/spec/frontend/sidebar/sidebar_assignees_spec.js
@@ -3,6 +3,7 @@ import AxiosMockAdapter from 'axios-mock-adapter';
import axios from 'axios';
import SidebarAssignees from '~/sidebar/components/assignees/sidebar_assignees.vue';
import Assigness from '~/sidebar/components/assignees/assignees.vue';
+import AssigneesRealtime from '~/sidebar/components/assignees/assignees_realtime.vue';
import SidebarMediator from '~/sidebar/sidebar_mediator';
import SidebarService from '~/sidebar/services/sidebar_service';
import SidebarStore from '~/sidebar/stores/sidebar_store';
@@ -12,12 +13,19 @@ describe('sidebar assignees', () => {
let wrapper;
let mediator;
let axiosMock;
-
- const createComponent = () => {
+ const createComponent = (realTimeIssueSidebar = false, props) => {
wrapper = shallowMount(SidebarAssignees, {
propsData: {
+ issuableIid: '1',
mediator,
field: '',
+ projectPath: 'projectPath',
+ ...props,
+ },
+ provide: {
+ glFeatures: {
+ realTimeIssueSidebar,
+ },
},
// Attaching to document is required because this component emits something from the parent element :/
attachToDocument: true,
@@ -30,8 +38,6 @@ describe('sidebar assignees', () => {
jest.spyOn(mediator, 'saveAssignees');
jest.spyOn(mediator, 'assignYourself');
-
- createComponent();
});
afterEach(() => {
@@ -45,6 +51,8 @@ describe('sidebar assignees', () => {
});
it('calls the mediator when saves the assignees', () => {
+ createComponent();
+
expect(mediator.saveAssignees).not.toHaveBeenCalled();
wrapper.vm.saveAssignees();
@@ -53,6 +61,8 @@ describe('sidebar assignees', () => {
});
it('calls the mediator when "assignSelf" method is called', () => {
+ createComponent();
+
expect(mediator.assignYourself).not.toHaveBeenCalled();
expect(mediator.store.assignees.length).toBe(0);
@@ -63,6 +73,8 @@ describe('sidebar assignees', () => {
});
it('hides assignees until fetched', () => {
+ createComponent();
+
expect(wrapper.find(Assigness).exists()).toBe(false);
wrapper.vm.store.isFetching.assignees = false;
@@ -71,4 +83,30 @@ describe('sidebar assignees', () => {
expect(wrapper.find(Assigness).exists()).toBe(true);
});
});
+
+ describe('when realTimeIssueSidebar is turned on', () => {
+ describe('when issuableType is issue', () => {
+ it('finds AssigneesRealtime componeont', () => {
+ createComponent(true);
+
+ expect(wrapper.find(AssigneesRealtime).exists()).toBe(true);
+ });
+ });
+
+ describe('when issuableType is MR', () => {
+ it('does not find AssigneesRealtime componeont', () => {
+ createComponent(true, { issuableType: 'MR' });
+
+ expect(wrapper.find(AssigneesRealtime).exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('when realTimeIssueSidebar is turned off', () => {
+ it('does not find AssigneesRealtime', () => {
+ createComponent(false, { issuableType: 'issue' });
+
+ expect(wrapper.find(AssigneesRealtime).exists()).toBe(false);
+ });
+ });
});
diff --git a/spec/graphql/types/user_type_spec.rb b/spec/graphql/types/user_type_spec.rb
index 8c9fad86a2d..2b0b8844df5 100644
--- a/spec/graphql/types/user_type_spec.rb
+++ b/spec/graphql/types/user_type_spec.rb
@@ -9,7 +9,7 @@ describe GitlabSchema.types['User'] do
it 'has the expected fields' do
expected_fields = %w[
- id user_permissions snippets name username avatarUrl webUrl todos
+ id user_permissions snippets name username avatarUrl webUrl todos state
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/policies/design_management/design_policy_spec.rb b/spec/policies/design_management/design_policy_spec.rb
new file mode 100644
index 00000000000..154a9f5ad6b
--- /dev/null
+++ b/spec/policies/design_management/design_policy_spec.rb
@@ -0,0 +1,174 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe DesignManagement::DesignPolicy do
+ include DesignManagementTestHelpers
+
+ include_context 'ProjectPolicy context'
+
+ let(:guest_design_abilities) { %i[read_design] }
+ let(:developer_design_abilities) do
+ %i[create_design destroy_design]
+ end
+ let(:design_abilities) { guest_design_abilities + developer_design_abilities }
+
+ let(:issue) { create(:issue, project: project) }
+ let(:design) { create(:design, issue: issue) }
+
+ subject(:design_policy) { described_class.new(current_user, design) }
+
+ shared_examples_for "design abilities not available" do
+ context "for owners" do
+ let(:current_user) { owner }
+
+ it { is_expected.to be_disallowed(*design_abilities) }
+ end
+
+ context "for admins" do
+ let(:current_user) { admin }
+
+ it { is_expected.to be_disallowed(*design_abilities) }
+ end
+
+ context "for maintainers" do
+ let(:current_user) { maintainer }
+
+ it { is_expected.to be_disallowed(*design_abilities) }
+ end
+
+ context "for developers" do
+ let(:current_user) { developer }
+
+ it { is_expected.to be_disallowed(*design_abilities) }
+ end
+
+ context "for reporters" do
+ let(:current_user) { reporter }
+
+ it { is_expected.to be_disallowed(*design_abilities) }
+ end
+
+ context "for guests" do
+ let(:current_user) { guest }
+
+ it { is_expected.to be_disallowed(*design_abilities) }
+ end
+
+ context "for anonymous users" do
+ let(:current_user) { nil }
+
+ it { is_expected.to be_disallowed(*design_abilities) }
+ end
+ end
+
+ shared_examples_for "design abilities available for members" do
+ context "for owners" do
+ let(:current_user) { owner }
+
+ it { is_expected.to be_allowed(*design_abilities) }
+ end
+
+ context "for admins" do
+ let(:current_user) { admin }
+
+ it { is_expected.to be_allowed(*design_abilities) }
+ end
+
+ context "for maintainers" do
+ let(:current_user) { maintainer }
+
+ it { is_expected.to be_allowed(*design_abilities) }
+ end
+
+ context "for developers" do
+ let(:current_user) { developer }
+
+ it { is_expected.to be_allowed(*design_abilities) }
+ end
+
+ context "for reporters" do
+ let(:current_user) { reporter }
+
+ it { is_expected.to be_allowed(*guest_design_abilities) }
+ it { is_expected.to be_disallowed(*developer_design_abilities) }
+ end
+ end
+
+ shared_examples_for "read-only design abilities" do
+ it { is_expected.to be_allowed(:read_design) }
+ it { is_expected.to be_disallowed(:create_design, :destroy_design) }
+ end
+
+ context "when DesignManagement is not enabled" do
+ before do
+ enable_design_management(false)
+ end
+
+ it_behaves_like "design abilities not available"
+ end
+
+ context "when the feature is available" do
+ before do
+ enable_design_management
+ end
+
+ it_behaves_like "design abilities available for members"
+
+ context "for guests in private projects" do
+ let(:project) { create(:project, :private) }
+ let(:current_user) { guest }
+
+ it { is_expected.to be_allowed(*guest_design_abilities) }
+ it { is_expected.to be_disallowed(*developer_design_abilities) }
+ end
+
+ context "for anonymous users in public projects" do
+ let(:current_user) { nil }
+
+ it { is_expected.to be_allowed(*guest_design_abilities) }
+ it { is_expected.to be_disallowed(*developer_design_abilities) }
+ end
+
+ context "when the issue is confidential" do
+ let(:issue) { create(:issue, :confidential, project: project) }
+
+ it_behaves_like "design abilities available for members"
+
+ context "for guests" do
+ let(:current_user) { guest }
+
+ it { is_expected.to be_disallowed(*design_abilities) }
+ end
+
+ context "for anonymous users" do
+ let(:current_user) { nil }
+
+ it { is_expected.to be_disallowed(*design_abilities) }
+ end
+ end
+
+ context "when the issue is locked" do
+ let(:current_user) { owner }
+ let(:issue) { create(:issue, :locked, project: project) }
+
+ it_behaves_like "read-only design abilities"
+ end
+
+ context "when the issue has moved" do
+ let(:current_user) { owner }
+ let(:issue) { create(:issue, project: project, moved_to: create(:issue)) }
+
+ it_behaves_like "read-only design abilities"
+ end
+
+ context "when the project is archived" do
+ let(:current_user) { owner }
+
+ before do
+ project.update!(archived: true)
+ end
+
+ it_behaves_like "read-only design abilities"
+ end
+ end
+end