diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-04 15:09:46 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-04 15:09:46 +0300 |
commit | 72797f4a602d0061636df39df89e11896de2a524 (patch) | |
tree | fbd4397be74910e44aaafda4093ea1e6f499d445 /spec | |
parent | 5b4eca2afd809fbfba3bdcacabe547025ebe7f43 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
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 |