diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-11-18 21:14:27 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-11-18 21:14:27 +0300 |
commit | f6c037b73c90ac251625c17720e6b8307c1698cc (patch) | |
tree | 26a287a09ae414da75f7e383dd1eaae5d3704ca5 /spec | |
parent | bf0d6692fc4c16205cc49b8b87b7b7e0daa97c63 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
13 files changed, 258 insertions, 135 deletions
diff --git a/spec/frontend/content_editor/services/markdown_serializer_spec.js b/spec/frontend/content_editor/services/markdown_serializer_spec.js index cfd93c2df10..c41d9016e52 100644 --- a/spec/frontend/content_editor/services/markdown_serializer_spec.js +++ b/spec/frontend/content_editor/services/markdown_serializer_spec.js @@ -28,7 +28,6 @@ import TableHeader from '~/content_editor/extensions/table_header'; import TableRow from '~/content_editor/extensions/table_row'; import TaskItem from '~/content_editor/extensions/task_item'; import TaskList from '~/content_editor/extensions/task_list'; -import Text from '~/content_editor/extensions/text'; import markdownSerializer from '~/content_editor/services/markdown_serializer'; import { createTestEditor, createDocBuilder } from '../test_utils'; @@ -58,7 +57,6 @@ const tiptapEditor = createTestEditor({ Link, ListItem, OrderedList, - Paragraph, Strike, Table, TableCell, @@ -66,7 +64,6 @@ const tiptapEditor = createTestEditor({ TableRow, TaskItem, TaskList, - Text, ], }); diff --git a/spec/frontend/dropzone_input_spec.js b/spec/frontend/dropzone_input_spec.js index 12e10f7c5f4..11414e8890d 100644 --- a/spec/frontend/dropzone_input_spec.js +++ b/spec/frontend/dropzone_input_spec.js @@ -32,6 +32,8 @@ describe('dropzone_input', () => { }); describe('handlePaste', () => { + let form; + const triggerPasteEvent = (clipboardData = {}) => { const event = $.Event('paste'); const origEvent = new Event('paste'); @@ -45,11 +47,15 @@ describe('dropzone_input', () => { beforeEach(() => { loadFixtures('issues/new-issue.html'); - const form = $('#new_issue'); + form = $('#new_issue'); form.data('uploads-path', TEST_UPLOAD_PATH); dropzoneInput(form); }); + afterEach(() => { + form = null; + }); + it('pastes Markdown tables', () => { jest.spyOn(PasteMarkdownTable.prototype, 'isTable'); jest.spyOn(PasteMarkdownTable.prototype, 'convertToTableMarkdown'); @@ -86,6 +92,27 @@ describe('dropzone_input', () => { expect(axiosMock.history.post[0].data.get('file').name).toHaveLength(246); }); + it('disables generated image file when clipboardData have both image and text', () => { + const TEST_PLAIN_TEXT = 'This wording is a plain text.'; + triggerPasteEvent({ + types: ['text/plain', 'Files'], + getData: () => TEST_PLAIN_TEXT, + items: [ + { + kind: 'text', + type: 'text/plain', + }, + { + kind: 'file', + type: 'image/png', + getAsFile: () => new Blob(), + }, + ], + }); + + expect(form.find('.js-gfm-input')[0].value).toBe(''); + }); + it('display original file name in comment box', async () => { const axiosMock = new MockAdapter(axios); triggerPasteEvent({ diff --git a/spec/frontend/issuable_list/components/issuable_item_spec.js b/spec/frontend/issuable_list/components/issuable_item_spec.js index ac3bf7f3269..a4d90613ca6 100644 --- a/spec/frontend/issuable_list/components/issuable_item_spec.js +++ b/spec/frontend/issuable_list/components/issuable_item_spec.js @@ -1,19 +1,25 @@ import { GlLink, GlLabel, GlIcon, GlFormCheckbox, GlSprintf } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; import { useFakeDate } from 'helpers/fake_date'; +import { shallowMountExtended as shallowMount } from 'helpers/vue_test_utils_helper'; import IssuableItem from '~/issuable_list/components/issuable_item.vue'; import IssuableAssignees from '~/vue_shared/components/issue/issue_assignees.vue'; import { mockIssuable, mockRegularLabel, mockScopedLabel } from '../mock_data'; -const createComponent = ({ issuableSymbol = '#', issuable = mockIssuable, slots = {} } = {}) => +const createComponent = ({ + issuableSymbol = '#', + issuable = mockIssuable, + enableLabelPermalinks = true, + showCheckbox = true, + slots = {}, +} = {}) => shallowMount(IssuableItem, { propsData: { issuableSymbol, issuable, - enableLabelPermalinks: true, + enableLabelPermalinks, showDiscussions: true, - showCheckbox: false, + showCheckbox, }, slots, stubs: { @@ -34,7 +40,6 @@ describe('IssuableItem', () => { beforeEach(() => { gon.gitlab_url = MOCK_GITLAB_URL; - wrapper = createComponent(); }); afterEach(() => { @@ -45,6 +50,8 @@ describe('IssuableItem', () => { describe('computed', () => { describe('author', () => { it('returns `issuable.author` reference', () => { + wrapper = createComponent(); + expect(wrapper.vm.author).toEqual(mockIssuable.author); }); }); @@ -59,7 +66,7 @@ describe('IssuableItem', () => { `( 'returns $returnValue when value of `issuable.author.id` is $authorId', async ({ authorId, returnValue }) => { - wrapper.setProps({ + wrapper = createComponent({ issuable: { ...mockIssuable, author: { @@ -86,7 +93,7 @@ describe('IssuableItem', () => { `( 'returns $returnValue when `issuable.webUrl` is $urlType', async ({ issuableWebUrl, returnValue }) => { - wrapper.setProps({ + wrapper = createComponent({ issuable: { ...mockIssuable, webUrl: issuableWebUrl, @@ -102,11 +109,13 @@ describe('IssuableItem', () => { describe('labels', () => { it('returns `issuable.labels.nodes` reference when it is available', () => { + wrapper = createComponent(); + expect(wrapper.vm.labels).toEqual(mockLabels); }); it('returns `issuable.labels` reference when it is available', async () => { - wrapper.setProps({ + wrapper = createComponent({ issuable: { ...mockIssuable, labels: mockLabels, @@ -119,7 +128,7 @@ describe('IssuableItem', () => { }); it('returns empty array when none of `issuable.labels.nodes` or `issuable.labels` are available', async () => { - wrapper.setProps({ + wrapper = createComponent({ issuable: { ...mockIssuable, labels: null, @@ -134,12 +143,16 @@ describe('IssuableItem', () => { describe('assignees', () => { it('returns `issuable.assignees` reference when it is available', () => { + wrapper = createComponent(); + expect(wrapper.vm.assignees).toBe(mockIssuable.assignees); }); }); describe('updatedAt', () => { it('returns string containing timeago string based on `issuable.updatedAt`', () => { + wrapper = createComponent(); + expect(wrapper.vm.updatedAt).toContain('updated'); expect(wrapper.vm.updatedAt).toContain('ago'); }); @@ -155,7 +168,7 @@ describe('IssuableItem', () => { `( 'returns $returnValue when issuable.userDiscussionsCount is $userDiscussionsCount', ({ userDiscussionsCount, returnValue }) => { - const wrapperWithDiscussions = createComponent({ + wrapper = createComponent({ issuableSymbol: '#', issuable: { ...mockIssuable, @@ -163,9 +176,7 @@ describe('IssuableItem', () => { }, }); - expect(wrapperWithDiscussions.vm.showDiscussions).toBe(returnValue); - - wrapperWithDiscussions.destroy(); + expect(wrapper.findByTestId('issuable-discussions').exists()).toBe(returnValue); }, ); }); @@ -180,6 +191,8 @@ describe('IssuableItem', () => { `( 'return $returnValue when provided label param is a $labelType label', ({ label, returnValue }) => { + wrapper = createComponent(); + expect(wrapper.vm.scopedLabel(label)).toBe(returnValue); }, ); @@ -191,19 +204,23 @@ describe('IssuableItem', () => { ${{ title: 'foo' }} | ${'title'} | ${'foo'} ${{ name: 'foo' }} | ${'name'} | ${'foo'} `('returns string value of `label.$propWithTitle`', ({ label, returnValue }) => { + wrapper = createComponent(); + expect(wrapper.vm.labelTitle(label)).toBe(returnValue); }); }); describe('labelTarget', () => { it('returns target string for a provided label param when `enableLabelPermalinks` is true', () => { + wrapper = createComponent(); + expect(wrapper.vm.labelTarget(mockRegularLabel)).toBe( '?label_name[]=Documentation%20Update', ); }); it('returns string "#" for a provided label param when `enableLabelPermalinks` is false', async () => { - wrapper.setProps({ + wrapper = createComponent({ enableLabelPermalinks: false, }); @@ -223,7 +240,7 @@ describe('IssuableItem', () => { `( 'renders issuable title correctly when `gitlabWebUrl` is `$gitlabWebUrl` and webUrl is `$webUrl`', async ({ webUrl, gitlabWebUrl, expectedHref, expectedTarget }) => { - wrapper.setProps({ + wrapper = createComponent({ issuable: { ...mockIssuable, webUrl, @@ -243,7 +260,7 @@ describe('IssuableItem', () => { ); it('renders checkbox when `showCheckbox` prop is true', async () => { - wrapper.setProps({ + wrapper = createComponent({ showCheckbox: true, }); @@ -262,7 +279,7 @@ describe('IssuableItem', () => { }); it('renders issuable title with `target` set as "_blank" when issuable.webUrl is external', async () => { - wrapper.setProps({ + wrapper = createComponent({ issuable: { ...mockIssuable, webUrl: 'http://jira.atlassian.net/browse/IG-1', @@ -277,7 +294,7 @@ describe('IssuableItem', () => { }); it('renders issuable confidential icon when issuable is confidential', async () => { - wrapper.setProps({ + wrapper = createComponent({ issuable: { ...mockIssuable, confidential: true, @@ -296,7 +313,21 @@ describe('IssuableItem', () => { }); }); + it('renders spam icon when issuable is hidden', async () => { + wrapper = createComponent({ issuable: { ...mockIssuable, hidden: true } }); + + const hiddenIcon = wrapper.findComponent(GlIcon); + + expect(hiddenIcon.props('name')).toBe('spam'); + expect(hiddenIcon.attributes()).toMatchObject({ + title: 'This issue is hidden because its author has been banned', + arialabel: 'Hidden', + }); + }); + it('renders task status', () => { + wrapper = createComponent(); + const taskStatus = wrapper.find('[data-testid="task-status"]'); const expected = `${mockIssuable.taskCompletionStatus.completedCount} of ${mockIssuable.taskCompletionStatus.count} tasks completed`; @@ -304,6 +335,8 @@ describe('IssuableItem', () => { }); it('renders issuable reference', () => { + wrapper = createComponent(); + const referenceEl = wrapper.find('[data-testid="issuable-reference"]'); expect(referenceEl.exists()).toBe(true); @@ -311,7 +344,7 @@ describe('IssuableItem', () => { }); it('renders issuable reference via slot', () => { - const wrapperWithRefSlot = createComponent({ + wrapper = createComponent({ issuableSymbol: '#', issuable: mockIssuable, slots: { @@ -320,15 +353,15 @@ describe('IssuableItem', () => { `, }, }); - const referenceEl = wrapperWithRefSlot.find('.js-reference'); + const referenceEl = wrapper.find('.js-reference'); expect(referenceEl.exists()).toBe(true); expect(referenceEl.text()).toBe(`${mockIssuable.iid}`); - - wrapperWithRefSlot.destroy(); }); it('renders issuable createdAt info', () => { + wrapper = createComponent(); + const createdAtEl = wrapper.find('[data-testid="issuable-created-at"]'); expect(createdAtEl.exists()).toBe(true); @@ -337,6 +370,8 @@ describe('IssuableItem', () => { }); it('renders issuable author info', () => { + wrapper = createComponent(); + const authorEl = wrapper.find('[data-testid="issuable-author"]'); expect(authorEl.exists()).toBe(true); @@ -351,7 +386,7 @@ describe('IssuableItem', () => { }); it('renders issuable author info via slot', () => { - const wrapperWithAuthorSlot = createComponent({ + wrapper = createComponent({ issuableSymbol: '#', issuable: mockIssuable, slots: { @@ -360,16 +395,14 @@ describe('IssuableItem', () => { `, }, }); - const authorEl = wrapperWithAuthorSlot.find('.js-author'); + const authorEl = wrapper.find('.js-author'); expect(authorEl.exists()).toBe(true); expect(authorEl.text()).toBe(mockAuthor.name); - - wrapperWithAuthorSlot.destroy(); }); it('renders timeframe via slot', () => { - const wrapperWithTimeframeSlot = createComponent({ + wrapper = createComponent({ issuableSymbol: '#', issuable: mockIssuable, slots: { @@ -378,15 +411,15 @@ describe('IssuableItem', () => { `, }, }); - const timeframeEl = wrapperWithTimeframeSlot.find('.js-timeframe'); + const timeframeEl = wrapper.find('.js-timeframe'); expect(timeframeEl.exists()).toBe(true); expect(timeframeEl.text()).toBe('Jan 1, 2020 - Mar 31, 2020'); - - wrapperWithTimeframeSlot.destroy(); }); it('renders gl-label component for each label present within `issuable` prop', () => { + wrapper = createComponent(); + const labelsEl = wrapper.findAll(GlLabel); expect(labelsEl.exists()).toBe(true); @@ -402,7 +435,7 @@ describe('IssuableItem', () => { }); it('renders issuable status via slot', () => { - const wrapperWithStatusSlot = createComponent({ + wrapper = createComponent({ issuableSymbol: '#', issuable: mockIssuable, slots: { @@ -411,15 +444,15 @@ describe('IssuableItem', () => { `, }, }); - const statusEl = wrapperWithStatusSlot.find('.js-status'); + const statusEl = wrapper.find('.js-status'); expect(statusEl.exists()).toBe(true); expect(statusEl.text()).toBe(`${mockIssuable.state}`); - - wrapperWithStatusSlot.destroy(); }); it('renders discussions count', () => { + wrapper = createComponent(); + const discussionsEl = wrapper.find('[data-testid="issuable-discussions"]'); expect(discussionsEl.exists()).toBe(true); @@ -432,6 +465,8 @@ describe('IssuableItem', () => { }); it('renders issuable-assignees component', () => { + wrapper = createComponent(); + const assigneesEl = wrapper.find(IssuableAssignees); expect(assigneesEl.exists()).toBe(true); @@ -443,6 +478,8 @@ describe('IssuableItem', () => { }); it('renders issuable updatedAt info', () => { + wrapper = createComponent(); + const updatedAtEl = wrapper.find('[data-testid="issuable-updated-at"]'); expect(updatedAtEl.attributes('title')).toBe('Sep 10, 2020 11:41am UTC'); diff --git a/spec/frontend/issues_list/components/issues_list_app_spec.js b/spec/frontend/issues_list/components/issues_list_app_spec.js index 3f52c7b4afe..c398acf081b 100644 --- a/spec/frontend/issues_list/components/issues_list_app_spec.js +++ b/spec/frontend/issues_list/components/issues_list_app_spec.js @@ -17,7 +17,7 @@ import { locationSearch, urlParams, } from 'jest/issues_list/mock_data'; -import createFlash from '~/flash'; +import createFlash, { FLASH_TYPES } from '~/flash'; import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils'; import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue'; import IssuableByEmail from '~/issuable/components/issuable_by_email.vue'; @@ -29,6 +29,8 @@ import { CREATED_DESC, DUE_DATE_OVERDUE, PARAM_DUE_DATE, + RELATIVE_POSITION, + RELATIVE_POSITION_ASC, TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_AUTHOR, TOKEN_TYPE_CONFIDENTIAL, @@ -314,6 +316,29 @@ describe('IssuesListApp component', () => { }, }); }); + + describe('when issue repositioning is disabled and the sort is manual', () => { + beforeEach(() => { + setWindowLocation(`?sort=${RELATIVE_POSITION}`); + wrapper = mountComponent({ provide: { isIssueRepositioningDisabled: true } }); + }); + + it('changes the sort to the default of created descending', () => { + expect(findIssuableList().props()).toMatchObject({ + initialSortBy: CREATED_DESC, + urlParams: { + sort: urlSortParams[CREATED_DESC], + }, + }); + }); + + it('shows an alert to tell the user that manual reordering is disabled', () => { + expect(createFlash).toHaveBeenCalledWith({ + message: IssuesListApp.i18n.issueRepositioningMessage, + type: FLASH_TYPES.NOTICE, + }); + }); + }); }); describe('state', () => { @@ -762,6 +787,30 @@ describe('IssuesListApp component', () => { }); }, ); + + describe('when issue repositioning is disabled', () => { + const initialSort = CREATED_DESC; + + beforeEach(() => { + setWindowLocation(`?sort=${initialSort}`); + wrapper = mountComponent({ provide: { isIssueRepositioningDisabled: true } }); + + findIssuableList().vm.$emit('sort', RELATIVE_POSITION_ASC); + }); + + it('does not update the sort to manual', () => { + expect(findIssuableList().props('urlParams')).toMatchObject({ + sort: urlSortParams[initialSort], + }); + }); + + it('shows an alert to tell the user that manual reordering is disabled', () => { + expect(createFlash).toHaveBeenCalledWith({ + message: IssuesListApp.i18n.issueRepositioningMessage, + type: FLASH_TYPES.NOTICE, + }); + }); + }); }); describe('when "update-legacy-bulk-edit" event is emitted by IssuableList', () => { diff --git a/spec/frontend/issues_list/mock_data.js b/spec/frontend/issues_list/mock_data.js index 19a8af4d9c2..d408316a063 100644 --- a/spec/frontend/issues_list/mock_data.js +++ b/spec/frontend/issues_list/mock_data.js @@ -22,6 +22,7 @@ export const getIssuesQueryResponse = { createdAt: '2021-05-22T04:08:01Z', downvotes: 2, dueDate: '2021-05-29', + hidden: false, humanTimeEstimate: null, mergeRequestsCount: false, moved: false, diff --git a/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap b/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap index b576f1b2553..7e3ff734981 100644 --- a/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap +++ b/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap @@ -6,7 +6,7 @@ exports[`packages_list_row renders 1`] = ` data-qa-selector="package_row" > <div - class="gl-display-flex gl-align-items-center gl-py-3 gl-px-5" + class="gl-display-flex gl-align-items-center gl-py-3" > <!----> diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/version_row_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/version_row_spec.js.snap index c95538546c1..7aa42a1f1e5 100644 --- a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/version_row_spec.js.snap +++ b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/version_row_spec.js.snap @@ -5,7 +5,7 @@ exports[`VersionRow renders 1`] = ` class="gl-display-flex gl-flex-direction-column gl-border-b-solid gl-border-t-solid gl-border-t-1 gl-border-b-1 gl-border-t-transparent gl-border-b-gray-100" > <div - class="gl-display-flex gl-align-items-center gl-py-3 gl-px-5" + class="gl-display-flex gl-align-items-center gl-py-3" > <!----> diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap index 2f2be797251..bb65db807f4 100644 --- a/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap +++ b/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap @@ -6,7 +6,7 @@ exports[`packages_list_row renders 1`] = ` data-qa-selector="package_row" > <div - class="gl-display-flex gl-align-items-center gl-py-3 gl-px-5" + class="gl-display-flex gl-align-items-center gl-py-3" > <!----> diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index 43b27dded3b..31e19080efe 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -302,6 +302,7 @@ RSpec.describe IssuesHelper do allow(helper).to receive(:can?).and_return(true) allow(helper).to receive(:image_path).and_return('#') allow(helper).to receive(:import_csv_namespace_project_issues_path).and_return('#') + allow(helper).to receive(:issue_repositioning_disabled?).and_return(true) allow(helper).to receive(:url_for).and_return('#') expected = { @@ -318,6 +319,7 @@ RSpec.describe IssuesHelper do has_any_issues: project_issues(project).exists?.to_s, import_csv_issues_path: '#', initial_email: project.new_issuable_address(current_user, 'issue'), + is_issue_repositioning_disabled: 'true', is_project: 'true', is_signed_in: current_user.present?.to_s, jira_integration_path: help_page_url('integration/jira/issues', anchor: 'view-jira-issues'), diff --git a/spec/lib/gitlab/sidekiq_enq_spec.rb b/spec/lib/gitlab/sidekiq_enq_spec.rb deleted file mode 100644 index 6903f01bf5f..00000000000 --- a/spec/lib/gitlab/sidekiq_enq_spec.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::SidekiqEnq, :clean_gitlab_redis_queues do - let(:retry_set) { Sidekiq::Scheduled::SETS.first } - let(:schedule_set) { Sidekiq::Scheduled::SETS.last } - - around do |example| - freeze_time { example.run } - end - - shared_examples 'finds jobs that are due and enqueues them' do - before do - Sidekiq.redis do |redis| - redis.zadd(retry_set, (Time.current - 1.day).to_f.to_s, '{"jid": 1}') - redis.zadd(retry_set, Time.current.to_f.to_s, '{"jid": 2}') - redis.zadd(retry_set, (Time.current + 1.day).to_f.to_s, '{"jid": 3}') - - redis.zadd(schedule_set, (Time.current - 1.day).to_f.to_s, '{"jid": 4}') - redis.zadd(schedule_set, Time.current.to_f.to_s, '{"jid": 5}') - redis.zadd(schedule_set, (Time.current + 1.day).to_f.to_s, '{"jid": 6}') - end - end - - it 'enqueues jobs that are due' do - expect(Sidekiq::Client).to receive(:push).with({ 'jid' => 1 }) - expect(Sidekiq::Client).to receive(:push).with({ 'jid' => 2 }) - expect(Sidekiq::Client).to receive(:push).with({ 'jid' => 4 }) - expect(Sidekiq::Client).to receive(:push).with({ 'jid' => 5 }) - - Gitlab::SidekiqEnq.new.enqueue_jobs - - Sidekiq.redis do |redis| - expect(redis.zscan_each(retry_set).map(&:first)).to contain_exactly('{"jid": 3}') - expect(redis.zscan_each(schedule_set).map(&:first)).to contain_exactly('{"jid": 6}') - end - end - end - - context 'when atomic_sidekiq_scheduler is disabled' do - before do - stub_feature_flags(atomic_sidekiq_scheduler: false) - end - - it_behaves_like 'finds jobs that are due and enqueues them' - - context 'when ZRANGEBYSCORE returns a job that is already removed by another process' do - before do - Sidekiq.redis do |redis| - redis.zadd(schedule_set, Time.current.to_f.to_s, '{"jid": 1}') - - allow(redis).to receive(:zrangebyscore).and_wrap_original do |m, *args, **kwargs| - m.call(*args, **kwargs).tap do |jobs| - redis.zrem(schedule_set, jobs.first) if args[0] == schedule_set && jobs.first - end - end - end - end - - it 'calls ZREM but does not enqueue the job' do - Sidekiq.redis do |redis| - expect(redis).to receive(:zrem).with(schedule_set, '{"jid": 1}').twice.and_call_original - end - expect(Sidekiq::Client).not_to receive(:push) - - Gitlab::SidekiqEnq.new.enqueue_jobs - end - end - end - - context 'when atomic_sidekiq_scheduler is enabled' do - before do - stub_feature_flags(atomic_sidekiq_scheduler: true) - end - - context 'when Lua script is not yet loaded' do - before do - Gitlab::Redis::Queues.with { |redis| redis.script(:flush) } - end - - it_behaves_like 'finds jobs that are due and enqueues them' - end - - context 'when Lua script is already loaded' do - before do - Gitlab::SidekiqEnq.new.enqueue_jobs - end - - it_behaves_like 'finds jobs that are due and enqueues them' - end - end -end diff --git a/spec/lib/sidebars/projects/menus/shimo_menu_spec.rb b/spec/lib/sidebars/projects/menus/shimo_menu_spec.rb new file mode 100644 index 00000000000..534267a329e --- /dev/null +++ b/spec/lib/sidebars/projects/menus/shimo_menu_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Sidebars::Projects::Menus::ShimoMenu do + let_it_be_with_reload(:project) { create(:project) } + + let(:context) { Sidebars::Projects::Context.new(current_user: project.owner, container: project) } + + subject(:shimo_menu) { described_class.new(context) } + + describe '#render?' do + context 'without a valid Shimo integration' do + it "doesn't render the menu" do + expect(shimo_menu.render?).to be_falsey + end + end + + context 'with a valid Shimo integration' do + let_it_be_with_reload(:shimo_integration) { create(:shimo_integration, project: project) } + + context 'when integration is active' do + it 'renders the menu' do + expect(shimo_menu.render?).to eq true + end + + it 'renders menu link' do + expected_url = Rails.application.routes.url_helpers.project_integrations_shimo_path(project) + expect(shimo_menu.link).to eq expected_url + end + end + + context 'when integration is inactive' do + before do + shimo_integration.update!(active: false) + end + + it "doesn't render the menu" do + expect(shimo_menu.render?).to eq false + end + end + end + end +end diff --git a/spec/models/integrations/shimo_spec.rb b/spec/models/integrations/shimo_spec.rb index 25df8d2b249..41f3f3c0c16 100644 --- a/spec/models/integrations/shimo_spec.rb +++ b/spec/models/integrations/shimo_spec.rb @@ -38,4 +38,26 @@ RSpec.describe ::Integrations::Shimo do end end end + + describe 'Caching has_shimo on project_settings' do + let(:project) { create(:project) } + + subject { project.project_setting.has_shimo? } + + it 'sets the property to true when integration is active' do + create(:shimo_integration, project: project, active: true) + + is_expected.to be(true) + end + + it 'sets the property to false when integration is not active' do + create(:shimo_integration, project: project, active: false) + + is_expected.to be(false) + end + + it 'creates a project_setting record if one was not already created' do + expect { create(:shimo_integration) }.to change(ProjectSetting, :count).by(1) + end + end end diff --git a/spec/requests/projects/integrations/shimos_controller_spec.rb b/spec/requests/projects/integrations/shimos_controller_spec.rb new file mode 100644 index 00000000000..7322143f87e --- /dev/null +++ b/spec/requests/projects/integrations/shimos_controller_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Projects::Integrations::ShimosController do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user, developer_projects: [project]) } + let_it_be(:shimo_integration) { create(:shimo_integration, project: project) } + + before do + sign_in(user) + end + + describe 'GET #show' do + context 'when Shimo integration is inactive' do + before do + shimo_integration.update!(active: false) + end + + it 'returns 404 status' do + get project_integrations_shimo_path(project) + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when Shimo integration is active' do + it 'renders the "show" template' do + get project_integrations_shimo_path(project) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template(:show) + expect(response.body).to include shimo_integration.external_wiki_url + end + end + end +end |