diff options
Diffstat (limited to 'spec')
23 files changed, 314 insertions, 189 deletions
diff --git a/spec/factories/ci/pending_builds.rb b/spec/factories/ci/pending_builds.rb index 90779ae8ab9..fbd76e07d8e 100644 --- a/spec/factories/ci/pending_builds.rb +++ b/spec/factories/ci/pending_builds.rb @@ -6,5 +6,7 @@ FactoryBot.define do project protected { build.protected } instance_runners_enabled { true } + namespace { project.namespace } + minutes_exceeded { false } end end diff --git a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb index 9e1b0135932..af5ba14e310 100644 --- a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb +++ b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb @@ -64,7 +64,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do context 'when enabled after it was previously canceled' do before do click_button "Merge when pipeline succeeds" - click_link "Cancel" + click_button "Cancel auto-merge" wait_for_requests @@ -87,7 +87,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do before do merge_request.merge_params['force_remove_source_branch'] = '0' merge_request.save! - click_link "Cancel" + click_button "Cancel auto-merge" end it_behaves_like 'Merge when pipeline succeeds activator' @@ -114,7 +114,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do end it 'allows to cancel the automatic merge' do - click_link "Cancel" + click_button "Cancel auto-merge" expect(page).to have_button "Merge when pipeline succeeds" @@ -124,7 +124,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do end it 'allows to delete source branch' do - click_link "Delete source branch" + click_button "Delete source branch" expect(page).to have_content "The source branch will be deleted" end diff --git a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb index 906eef775ab..2a49109d360 100644 --- a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb +++ b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb @@ -151,7 +151,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', context 'when detached merge request pipeline is pending' do it 'waits the head pipeline' do expect(page).to have_content('to be merged automatically when the pipeline succeeds') - expect(page).to have_link('Cancel') + expect(page).to have_button('Cancel auto-merge') end end @@ -178,7 +178,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', it 'waits the head pipeline' do expect(page).to have_content('to be merged automatically when the pipeline succeeds') - expect(page).to have_link('Cancel') + expect(page).to have_button('Cancel auto-merge') end end end @@ -377,7 +377,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', context 'when detached merge request pipeline is pending' do it 'waits the head pipeline' do expect(page).to have_content('to be merged automatically when the pipeline succeeds') - expect(page).to have_link('Cancel') + expect(page).to have_button('Cancel auto-merge') end end @@ -403,7 +403,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', it 'waits the head pipeline' do expect(page).to have_content('to be merged automatically when the pipeline succeeds') - expect(page).to have_link('Cancel') + expect(page).to have_button('Cancel auto-merge') end end end diff --git a/spec/frontend/__helpers__/mock_dom_observer.js b/spec/frontend/__helpers__/mock_dom_observer.js index 1b93b81535d..dd26b594ad9 100644 --- a/spec/frontend/__helpers__/mock_dom_observer.js +++ b/spec/frontend/__helpers__/mock_dom_observer.js @@ -52,7 +52,7 @@ class MockIntersectionObserver extends MockObserver { * const { trigger: triggerMutate } = useMockMutationObserver(); * * it('test', () => { - * trigger(el, { options: { childList: true }, entry: { } }); + * triggerMutate(el, { options: { childList: true }, entry: { } }); * }); * }) * ``` @@ -60,33 +60,31 @@ class MockIntersectionObserver extends MockObserver { * @param {String} key */ const useMockObserver = (key, createMock) => { - let mockObserver; + let mockObservers = []; let origObserver; beforeEach(() => { origObserver = global[key]; global[key] = jest.fn().mockImplementation((...args) => { - mockObserver = createMock(...args); + const mockObserver = createMock(...args); + mockObservers.push(mockObserver); return mockObserver; }); }); afterEach(() => { - mockObserver = null; + mockObservers.forEach((x) => x.disconnect()); + mockObservers = []; global[key] = origObserver; }); const trigger = (...args) => { - if (!mockObserver) { - return; - } - - mockObserver.$_triggerObserve(...args); + mockObservers.forEach((observer) => { + observer.$_triggerObserve(...args); + }); }; - const observersCount = () => mockObserver.$_observers.length; - - return { trigger, observersCount }; + return { trigger }; }; export const useMockIntersectionObserver = () => diff --git a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js index 1c4dde39585..e6a6e01c41c 100644 --- a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js +++ b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js @@ -6,7 +6,6 @@ import VueApollo from 'vue-apollo'; import createHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql'; import updateHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql'; import createMockApollo from 'helpers/mock_apollo_helper'; -import { useMockIntersectionObserver } from 'helpers/mock_dom_observer'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; import IntegrationsList from '~/alerts_settings/components/alerts_integrations_list.vue'; @@ -57,7 +56,6 @@ describe('AlertsSettingsWrapper', () => { let wrapper; let fakeApollo; let destroyIntegrationHandler; - useMockIntersectionObserver(); const httpMappingData = { payloadExample: '{"test: : "field"}', diff --git a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap index 03ae77d4977..57023c55878 100644 --- a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap +++ b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap @@ -61,6 +61,7 @@ exports[`Design management design index page renders design index 1`] = ` <participants-stub class="gl-mb-4" + lazy="true" numberoflessparticipants="7" participants="[object Object]" /> @@ -221,6 +222,7 @@ exports[`Design management design index page with error GlAlert is rendered in c <participants-stub class="gl-mb-4" + lazy="true" numberoflessparticipants="7" participants="[object Object]" /> diff --git a/spec/frontend/environment.js b/spec/frontend/environment.js index 29aa416149c..cf47a1cd7bb 100644 --- a/spec/frontend/environment.js +++ b/spec/frontend/environment.js @@ -88,13 +88,32 @@ class CustomEnvironment extends JSDOMEnvironment { }), }); - this.global.PerformanceObserver = class { + /** + * JSDom doesn't have an own observer implementation, so this a Noop Observer. + * If you are testing functionality, related to observers, have a look at __helpers__/mock_dom_observer.js + * + * JSDom actually implements a _proper_ MutationObserver, so no need to mock it! + */ + class NoopObserver { /* eslint-disable no-useless-constructor, no-unused-vars, no-empty-function, class-methods-use-this */ constructor(callback) {} disconnect() {} observe(element, initObject) {} + unobserve(element) {} + takeRecords() { + return []; + } /* eslint-enable no-useless-constructor, no-unused-vars, no-empty-function, class-methods-use-this */ - }; + } + + ['IntersectionObserver', 'PerformanceObserver', 'ResizeObserver'].forEach((observer) => { + if (this.global[observer]) { + throw new Error( + `We overwrite an existing Observer in jsdom (${observer}), are you sure you want to do that?`, + ); + } + this.global[observer] = NoopObserver; + }); } async teardown() { diff --git a/spec/frontend/issue_show/components/app_spec.js b/spec/frontend/issue_show/components/app_spec.js index 4c06f2dca1b..babe3a66578 100644 --- a/spec/frontend/issue_show/components/app_spec.js +++ b/spec/frontend/issue_show/components/app_spec.js @@ -2,7 +2,6 @@ import { GlIntersectionObserver } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; import { nextTick } from 'vue'; -import { useMockIntersectionObserver } from 'helpers/mock_dom_observer'; import '~/behaviors/markdown/render_gfm'; import IssuableApp from '~/issue_show/components/app.vue'; import DescriptionComponent from '~/issue_show/components/description.vue'; @@ -30,8 +29,6 @@ jest.mock('~/issue_show/event_hub'); const REALTIME_REQUEST_STACK = [initialRequest, secondRequest]; describe('Issuable output', () => { - useMockIntersectionObserver(); - let mock; let realtimeRequestCount = 0; let wrapper; diff --git a/spec/frontend/issue_show/issue_spec.js b/spec/frontend/issue_show/issue_spec.js index d043693b863..76989413edb 100644 --- a/spec/frontend/issue_show/issue_spec.js +++ b/spec/frontend/issue_show/issue_spec.js @@ -1,5 +1,4 @@ import MockAdapter from 'axios-mock-adapter'; -import { useMockIntersectionObserver } from 'helpers/mock_dom_observer'; import waitForPromises from 'helpers/wait_for_promises'; import { initIssuableApp } from '~/issue_show/issue'; import * as parseData from '~/issue_show/utils/parse_data'; @@ -10,8 +9,6 @@ import { appProps } from './mock_data/mock_data'; const mock = new MockAdapter(axios); mock.onGet().reply(200); -useMockIntersectionObserver(); - jest.mock('~/lib/utils/poll'); const setupHTML = (initialData) => { diff --git a/spec/frontend/popovers/components/popovers_spec.js b/spec/frontend/popovers/components/popovers_spec.js index 0c164d97564..25c509346d1 100644 --- a/spec/frontend/popovers/components/popovers_spec.js +++ b/spec/frontend/popovers/components/popovers_spec.js @@ -4,7 +4,7 @@ import { useMockMutationObserver } from 'helpers/mock_dom_observer'; import Popovers from '~/popovers/components/popovers.vue'; describe('popovers/components/popovers.vue', () => { - const { trigger: triggerMutate, observersCount } = useMockMutationObserver(); + const { trigger: triggerMutate } = useMockMutationObserver(); let wrapper; const buildWrapper = (...targets) => { @@ -120,10 +120,13 @@ describe('popovers/components/popovers.vue', () => { it('disconnects mutation observer on beforeDestroy', async () => { await buildWrapper(createPopoverTarget()); + const { observer } = wrapper.vm; + jest.spyOn(observer, 'disconnect'); - expect(observersCount()).toBe(1); + expect(observer.disconnect).toHaveBeenCalledTimes(0); wrapper.destroy(); - expect(observersCount()).toBe(0); + + expect(observer.disconnect).toHaveBeenCalledTimes(1); }); }); diff --git a/spec/frontend/tooltips/components/tooltips_spec.js b/spec/frontend/tooltips/components/tooltips_spec.js index c44918ceaf3..9b703b74a1a 100644 --- a/spec/frontend/tooltips/components/tooltips_spec.js +++ b/spec/frontend/tooltips/components/tooltips_spec.js @@ -4,7 +4,7 @@ import { useMockMutationObserver } from 'helpers/mock_dom_observer'; import Tooltips from '~/tooltips/components/tooltips.vue'; describe('tooltips/components/tooltips.vue', () => { - const { trigger: triggerMutate, observersCount } = useMockMutationObserver(); + const { trigger: triggerMutate } = useMockMutationObserver(); let wrapper; const buildWrapper = () => { @@ -211,11 +211,14 @@ describe('tooltips/components/tooltips.vue', () => { it('disconnects mutation observer on beforeDestroy', () => { buildWrapper(); wrapper.vm.addTooltips([createTooltipTarget()]); + const { observer } = wrapper.vm; + jest.spyOn(observer, 'disconnect'); - expect(observersCount()).toBe(1); + expect(observer.disconnect).toHaveBeenCalledTimes(0); wrapper.destroy(); - expect(observersCount()).toBe(0); + + expect(observer.disconnect).toHaveBeenCalledTimes(1); }); it('exposes hidden event', async () => { diff --git a/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap b/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap index ac20487c55f..5981d2d7849 100644 --- a/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap +++ b/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap @@ -4,8 +4,10 @@ exports[`MRWidgetAutoMergeEnabled when graphql is disabled template should have <div class="mr-widget-body media" > - <status-icon-stub - status="success" + <gl-icon-stub + class="gl-text-blue-500 gl-mr-3 gl-mt-1" + name="status_scheduled" + size="24" /> <div @@ -17,55 +19,31 @@ exports[`MRWidgetAutoMergeEnabled when graphql is disabled template should have <span class="gl-mr-3" > - <span - class="js-status-text-before-author" - data-testid="beforeStatusText" - > - Set by - </span> - - <mr-widget-author-stub - author="[object Object]" - showauthorname="true" + <gl-sprintf-stub + data-testid="statusText" + message="Set by %{merge_author} to be merged automatically when the pipeline succeeds" /> - - <span - class="js-status-text-after-author" - data-testid="afterStatusText" - > - to be merged automatically when the pipeline succeeds - </span> </span> - <a - class="btn btn-sm btn-default js-cancel-auto-merge" + <gl-button-stub + buttontextclasses="" + category="primary" + class="js-cancel-auto-merge" data-qa-selector="cancel_auto_merge_button" data-testid="cancelAutomaticMergeButton" - href="#" - role="button" + icon="" + size="small" + variant="default" > - <!----> - Cancel + Cancel auto-merge - </a> + </gl-button-stub> </h4> <section class="mr-info-list" > - <p> - - The changes will be merged into - - <a - class="label-branch" - href="/foo/bar" - > - foo - </a> - </p> - <p class="gl-display-flex" > @@ -75,17 +53,19 @@ exports[`MRWidgetAutoMergeEnabled when graphql is disabled template should have The source branch will not be deleted </span> - <a - class="btn btn-sm btn-default js-remove-source-branch" + <gl-button-stub + buttontextclasses="" + category="primary" + class="js-remove-source-branch" data-testid="removeSourceBranchButton" - href="#" - role="button" + icon="" + size="small" + variant="default" > - <!----> Delete source branch - </a> + </gl-button-stub> </p> </section> </div> @@ -96,8 +76,10 @@ exports[`MRWidgetAutoMergeEnabled when graphql is enabled template should have c <div class="mr-widget-body media" > - <status-icon-stub - status="success" + <gl-icon-stub + class="gl-text-blue-500 gl-mr-3 gl-mt-1" + name="status_scheduled" + size="24" /> <div @@ -109,55 +91,31 @@ exports[`MRWidgetAutoMergeEnabled when graphql is enabled template should have c <span class="gl-mr-3" > - <span - class="js-status-text-before-author" - data-testid="beforeStatusText" - > - Set by - </span> - - <mr-widget-author-stub - author="[object Object]" - showauthorname="true" + <gl-sprintf-stub + data-testid="statusText" + message="Set by %{merge_author} to be merged automatically when the pipeline succeeds" /> - - <span - class="js-status-text-after-author" - data-testid="afterStatusText" - > - to be merged automatically when the pipeline succeeds - </span> </span> - <a - class="btn btn-sm btn-default js-cancel-auto-merge" + <gl-button-stub + buttontextclasses="" + category="primary" + class="js-cancel-auto-merge" data-qa-selector="cancel_auto_merge_button" data-testid="cancelAutomaticMergeButton" - href="#" - role="button" + icon="" + size="small" + variant="default" > - <!----> - Cancel + Cancel auto-merge - </a> + </gl-button-stub> </h4> <section class="mr-info-list" > - <p> - - The changes will be merged into - - <a - class="label-branch" - href="/foo/bar" - > - foo - </a> - </p> - <p class="gl-display-flex" > @@ -167,17 +125,19 @@ exports[`MRWidgetAutoMergeEnabled when graphql is enabled template should have c The source branch will not be deleted </span> - <a - class="btn btn-sm btn-default js-remove-source-branch" + <gl-button-stub + buttontextclasses="" + category="primary" + class="js-remove-source-branch" data-testid="removeSourceBranchButton" - href="#" - role="button" + icon="" + size="small" + variant="default" > - <!----> Delete source branch - </a> + </gl-button-stub> </p> </section> </div> diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_enabled_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_enabled_spec.js index 0110a76e722..4c1534574f5 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_enabled_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_auto_merge_enabled_spec.js @@ -72,6 +72,8 @@ const defaultMrProps = () => ({ autoMergeStrategy: MWPS_MERGE_STRATEGY, }); +const getStatusText = () => wrapper.findByTestId('statusText').attributes('message'); + describe('MRWidgetAutoMergeEnabled', () => { let oldWindowGl; @@ -167,30 +169,6 @@ describe('MRWidgetAutoMergeEnabled', () => { }); }); - describe('statusTextBeforeAuthor', () => { - it('should return "Set by" if the MWPS is selected', () => { - factory({ - ...defaultMrProps(), - autoMergeStrategy: MWPS_MERGE_STRATEGY, - }); - - expect(wrapper.findByTestId('beforeStatusText').text()).toBe('Set by'); - }); - }); - - describe('statusTextAfterAuthor', () => { - it('should return "to be merged automatically..." if MWPS is selected', () => { - factory({ - ...defaultMrProps(), - autoMergeStrategy: MWPS_MERGE_STRATEGY, - }); - - expect(wrapper.findByTestId('afterStatusText').text()).toBe( - 'to be merged automatically when the pipeline succeeds', - ); - }); - }); - describe('cancelButtonText', () => { it('should return "Cancel" if MWPS is selected', () => { factory({ @@ -198,7 +176,9 @@ describe('MRWidgetAutoMergeEnabled', () => { autoMergeStrategy: MWPS_MERGE_STRATEGY, }); - expect(wrapper.findByTestId('cancelAutomaticMergeButton').text()).toBe('Cancel'); + expect(wrapper.findByTestId('cancelAutomaticMergeButton').text()).toBe( + 'Cancel auto-merge', + ); }); }); }); @@ -279,7 +259,7 @@ describe('MRWidgetAutoMergeEnabled', () => { await nextTick(); - expect(wrapper.find('.js-cancel-auto-merge').attributes('disabled')).toBe('disabled'); + expect(wrapper.find('.js-cancel-auto-merge').props('loading')).toBe(true); }); it('should show source branch will be deleted text when it source branch set to remove', () => { @@ -313,7 +293,7 @@ describe('MRWidgetAutoMergeEnabled', () => { await nextTick(); - expect(wrapper.find('.js-remove-source-branch').attributes('disabled')).toBe('disabled'); + expect(wrapper.find('.js-remove-source-branch').props('loading')).toBe(true); }); it('should render the status text as "...to merged automatically" if MWPS is selected', () => { @@ -322,9 +302,9 @@ describe('MRWidgetAutoMergeEnabled', () => { autoMergeStrategy: MWPS_MERGE_STRATEGY, }); - const statusText = trimText(wrapper.find('.js-status-text-after-author').text()); - - expect(statusText).toBe('to be merged automatically when the pipeline succeeds'); + expect(getStatusText()).toBe( + 'Set by %{merge_author} to be merged automatically when the pipeline succeeds', + ); }); it('should render the cancel button as "Cancel" if MWPS is selected', () => { @@ -335,7 +315,7 @@ describe('MRWidgetAutoMergeEnabled', () => { const cancelButtonText = trimText(wrapper.find('.js-cancel-auto-merge').text()); - expect(cancelButtonText).toBe('Cancel'); + expect(cancelButtonText).toBe('Cancel auto-merge'); }); }); }); diff --git a/spec/frontend/vue_shared/directives/autofocusonshow_spec.js b/spec/frontend/vue_shared/directives/autofocusonshow_spec.js index 1c9e89f99e9..59ce9f086c3 100644 --- a/spec/frontend/vue_shared/directives/autofocusonshow_spec.js +++ b/spec/frontend/vue_shared/directives/autofocusonshow_spec.js @@ -1,4 +1,3 @@ -import { useMockIntersectionObserver } from 'helpers/mock_dom_observer'; import autofocusonshow from '~/vue_shared/directives/autofocusonshow'; /** @@ -7,8 +6,6 @@ import autofocusonshow from '~/vue_shared/directives/autofocusonshow'; * on underlying DOM methods. */ describe('AutofocusOnShow directive', () => { - useMockIntersectionObserver(); - describe('with input invisible on component render', () => { let el; diff --git a/spec/lib/gitlab/ci/parsers/security/common_spec.rb b/spec/lib/gitlab/ci/parsers/security/common_spec.rb index 35eba4cacf4..c6387bf615b 100644 --- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb +++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb @@ -99,42 +99,42 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do context 'when message is provided' do it 'sets message from the report as a finding name' do - vulnerability = report.findings.find { |x| x.compare_key == 'CVE-1020' } - expected_name = Gitlab::Json.parse(vulnerability.raw_metadata)['message'] + finding = report.findings.find { |x| x.compare_key == 'CVE-1020' } + expected_name = Gitlab::Json.parse(finding.raw_metadata)['message'] - expect(vulnerability.name).to eq(expected_name) + expect(finding.name).to eq(expected_name) end end context 'when message is not provided' do context 'and name is provided' do it 'sets name from the report as a name' do - vulnerability = report.findings.find { |x| x.compare_key == 'CVE-1030' } - expected_name = Gitlab::Json.parse(vulnerability.raw_metadata)['name'] + finding = report.findings.find { |x| x.compare_key == 'CVE-1030' } + expected_name = Gitlab::Json.parse(finding.raw_metadata)['name'] - expect(vulnerability.name).to eq(expected_name) + expect(finding.name).to eq(expected_name) end end context 'and name is not provided' do context 'when CVE identifier exists' do it 'combines identifier with location to create name' do - vulnerability = report.findings.find { |x| x.compare_key == 'CVE-2017-11429' } - expect(vulnerability.name).to eq("CVE-2017-11429 in yarn.lock") + finding = report.findings.find { |x| x.compare_key == 'CVE-2017-11429' } + expect(finding.name).to eq("CVE-2017-11429 in yarn.lock") end end context 'when CWE identifier exists' do it 'combines identifier with location to create name' do - vulnerability = report.findings.find { |x| x.compare_key == 'CWE-2017-11429' } - expect(vulnerability.name).to eq("CWE-2017-11429 in yarn.lock") + finding = report.findings.find { |x| x.compare_key == 'CWE-2017-11429' } + expect(finding.name).to eq("CWE-2017-11429 in yarn.lock") end end context 'when neither CVE nor CWE identifier exist' do it 'combines identifier with location to create name' do - vulnerability = report.findings.find { |x| x.compare_key == 'OTHER-2017-11429' } - expect(vulnerability.name).to eq("other-2017-11429 in yarn.lock") + finding = report.findings.find { |x| x.compare_key == 'OTHER-2017-11429' } + expect(finding.name).to eq("other-2017-11429 in yarn.lock") end end end @@ -144,17 +144,17 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do describe 'parsing finding.details' do context 'when details are provided' do it 'sets details from the report' do - vulnerability = report.findings.find { |x| x.compare_key == 'CVE-1020' } - expected_details = Gitlab::Json.parse(vulnerability.raw_metadata)['details'] + finding = report.findings.find { |x| x.compare_key == 'CVE-1020' } + expected_details = Gitlab::Json.parse(finding.raw_metadata)['details'] - expect(vulnerability.details).to eq(expected_details) + expect(finding.details).to eq(expected_details) end end context 'when details are not provided' do it 'sets empty hash' do - vulnerability = report.findings.find { |x| x.compare_key == 'CVE-1030' } - expect(vulnerability.details).to eq({}) + finding = report.findings.find { |x| x.compare_key == 'CVE-1030' } + expect(finding.details).to eq({}) end end end diff --git a/spec/lib/gitlab/email/message/in_product_marketing/trial_short_spec.rb b/spec/lib/gitlab/email/message/in_product_marketing/trial_short_spec.rb new file mode 100644 index 00000000000..ebad4672eb3 --- /dev/null +++ b/spec/lib/gitlab/email/message/in_product_marketing/trial_short_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Email::Message::InProductMarketing::TrialShort do + let_it_be(:group) { build(:group) } + let_it_be(:user) { build(:user) } + + let(:series) { 0 } + + subject(:message) { described_class.new(group: group, user: user, series: series)} + + describe 'public methods' do + it 'returns value for series', :aggregate_failures do + expect(message.subject_line).to eq 'Be a DevOps hero' + expect(message.tagline).to be_nil + expect(message.title).to eq 'Expand your DevOps journey with a free GitLab trial' + expect(message.subtitle).to eq 'Start your trial today to experience single application success and discover all the features of GitLab Ultimate for free!' + expect(message.body_line1).to be_empty + expect(message.body_line2).to be_empty + expect(message.cta_text).to eq 'Start a trial' + expect(message.logo_path).to eq 'mailers/in_product_marketing/trial-0.png' + end + + describe '#progress' do + subject { message.progress } + + before do + allow(Gitlab).to receive(:com?).and_return(is_gitlab_com) + end + + context 'on gitlab.com' do + let(:is_gitlab_com) { true } + + it { is_expected.to eq('This is email 1 of 4 in the Trial series.') } + end + + context 'not on gitlab.com' do + let(:is_gitlab_com) { false } + + it { is_expected.to include('This is email 1 of 4 in the Trial series', Gitlab::Routing.url_helpers.profile_notifications_url) } + end + end + end +end diff --git a/spec/lib/gitlab/email/message/in_product_marketing/trial_spec.rb b/spec/lib/gitlab/email/message/in_product_marketing/trial_spec.rb index 5f7639a9ed6..3e18b8e35b6 100644 --- a/spec/lib/gitlab/email/message/in_product_marketing/trial_spec.rb +++ b/spec/lib/gitlab/email/message/in_product_marketing/trial_spec.rb @@ -23,6 +23,26 @@ RSpec.describe Gitlab::Email::Message::InProductMarketing::Trial do expect(message.body_line2).to be_present expect(message.cta_text).to be_present end + + describe '#progress' do + subject { message.progress } + + before do + allow(Gitlab).to receive(:com?).and_return(is_gitlab_com) + end + + context 'on gitlab.com' do + let(:is_gitlab_com) { true } + + it { is_expected.to eq("This is email #{series + 2} of 4 in the Trial series.") } + end + + context 'not on gitlab.com' do + let(:is_gitlab_com) { false } + + it { is_expected.to include("This is email #{series + 2} of 4 in the Trial series", Gitlab::Routing.url_helpers.profile_notifications_url) } + end + end end end end diff --git a/spec/lib/gitlab/usage_data_metrics_spec.rb b/spec/lib/gitlab/usage_data_metrics_spec.rb index bcf14d8ea4d..59ceaeaf6de 100644 --- a/spec/lib/gitlab/usage_data_metrics_spec.rb +++ b/spec/lib/gitlab/usage_data_metrics_spec.rb @@ -41,6 +41,31 @@ RSpec.describe Gitlab::UsageDataMetrics do ]) end + it 'includes incident_management_alerts monthly and weekly keys' do + expect(subject[:redis_hll_counters][:incident_management_alerts].keys).to contain_exactly(*[ + :incident_management_alert_create_incident_monthly, :incident_management_alert_create_incident_weekly + ]) + end + + it 'includes incident_management monthly and weekly keys' do + expect(subject[:redis_hll_counters][:incident_management]).to include( + :incident_management_incident_created_monthly, :incident_management_incident_created_weekly, + :incident_management_incident_reopened_monthly, :incident_management_incident_reopened_weekly, + :incident_management_incident_closed_monthly, :incident_management_incident_closed_weekly, + :incident_management_incident_assigned_monthly, :incident_management_incident_assigned_weekly, + :incident_management_incident_todo_monthly, :incident_management_incident_todo_weekly, + :incident_management_incident_comment_monthly, :incident_management_incident_comment_weekly, + :incident_management_incident_zoom_meeting_monthly, :incident_management_incident_zoom_meeting_weekly, + :incident_management_incident_relate_monthly, :incident_management_incident_relate_weekly, + :incident_management_incident_unrelate_monthly, :incident_management_incident_unrelate_weekly, + :incident_management_incident_change_confidential_monthly, :incident_management_incident_change_confidential_weekly, + :incident_management_alert_status_changed_monthly, :incident_management_alert_status_changed_weekly, + :incident_management_alert_assigned_monthly, :incident_management_alert_assigned_weekly, + :incident_management_alert_todo_monthly, :incident_management_alert_todo_weekly, + :incident_management_total_unique_counts_monthly, :incident_management_total_unique_counts_weekly + ) + end + it 'includes source_code monthly and weekly keys' do expect(subject[:redis_hll_counters][:source_code].keys).to contain_exactly(*[ :wiki_action_monthly, :wiki_action_weekly, diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 556b65679bb..6fd25571c6e 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -1361,6 +1361,8 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do "in_product_marketing_email_create_2_cta_clicked" => -1, "in_product_marketing_email_team_short_0_sent" => -1, "in_product_marketing_email_team_short_0_cta_clicked" => -1, + "in_product_marketing_email_trial_short_0_sent" => -1, + "in_product_marketing_email_trial_short_0_cta_clicked" => -1, "in_product_marketing_email_verify_0_sent" => -1, "in_product_marketing_email_verify_0_cta_clicked" => -1, "in_product_marketing_email_verify_1_sent" => -1, @@ -1402,6 +1404,8 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do "in_product_marketing_email_create_2_cta_clicked" => 0, "in_product_marketing_email_team_short_0_sent" => 0, "in_product_marketing_email_team_short_0_cta_clicked" => 0, + "in_product_marketing_email_trial_short_0_sent" => 0, + "in_product_marketing_email_trial_short_0_cta_clicked" => 0, "in_product_marketing_email_verify_0_sent" => 1, "in_product_marketing_email_verify_0_cta_clicked" => 0, "in_product_marketing_email_verify_1_sent" => 0, diff --git a/spec/models/ci/pending_build_spec.rb b/spec/models/ci/pending_build_spec.rb index b64f3999232..211caebdf70 100644 --- a/spec/models/ci/pending_build_spec.rb +++ b/spec/models/ci/pending_build_spec.rb @@ -8,6 +8,12 @@ RSpec.describe Ci::PendingBuild do let(:build) { create(:ci_build, :created, pipeline: pipeline) } + describe 'associations' do + it { is_expected.to belong_to :project } + it { is_expected.to belong_to :build } + it { is_expected.to belong_to :namespace } + end + describe '.upsert_from_build!' do context 'another pending entry does not exist' do it 'creates a new pending entry' do diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 3f626fb3fce..e2700378f5f 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -23,6 +23,7 @@ RSpec.describe Namespace do it { is_expected.to have_one :package_setting_relation } it { is_expected.to have_one :onboarding_progress } it { is_expected.to have_one :admin_note } + it { is_expected.to have_many :pending_builds } end describe 'validations' do diff --git a/spec/models/postgresql/replication_slot_spec.rb b/spec/models/postgresql/replication_slot_spec.rb index 4bad8a3f0c0..c3b67a2e7b8 100644 --- a/spec/models/postgresql/replication_slot_spec.rb +++ b/spec/models/postgresql/replication_slot_spec.rb @@ -60,4 +60,71 @@ RSpec.describe Postgresql::ReplicationSlot do expect(described_class.lag_too_great?).to eq(false) end end + + describe '#max_replication_slots' do + it 'returns the maximum number of replication slots' do + expect(described_class.max_replication_slots).to be >= 0 + end + end + + context 'with enough slots available' do + skip_examples = described_class.max_replication_slots <= described_class.count + + before(:all) do + skip('max_replication_slots too small') if skip_examples + + @current_slot_count = ApplicationRecord + .connection + .execute("SELECT COUNT(*) FROM pg_replication_slots;") + .first + .fetch('count') + .to_i + + @current_unused_count = ApplicationRecord + .connection + .execute("SELECT COUNT(*) FROM pg_replication_slots WHERE active = 'f';") + .first + .fetch('count') + .to_i + + ApplicationRecord + .connection + .execute("SELECT * FROM pg_create_physical_replication_slot('test_slot');") + end + + after(:all) do + unless skip_examples + ApplicationRecord + .connection + .execute("SELECT pg_drop_replication_slot('test_slot');") + end + end + + describe '#slots_count' do + it 'returns the number of replication slots' do + expect(described_class.count).to eq(@current_slot_count + 1) + end + end + + describe '#unused_slots_count' do + it 'returns the number of unused replication slots' do + expect(described_class.unused_slots_count).to eq(@current_unused_count + 1) + end + end + + describe '#max_retained_wal' do + it 'returns the retained WAL size' do + expect(described_class.max_retained_wal).not_to be_nil + end + end + + describe '#slots_retained_bytes' do + it 'returns the number of retained bytes' do + slot = described_class.slots_retained_bytes.find {|x| x['slot_name'] == 'test_slot' } + + expect(slot).not_to be_nil + expect(slot['retained_bytes']).to be_nil + end + end + end end diff --git a/spec/services/namespaces/in_product_marketing_emails_service_spec.rb b/spec/services/namespaces/in_product_marketing_emails_service_spec.rb index 3f2611d90ee..6857b842d5a 100644 --- a/spec/services/namespaces/in_product_marketing_emails_service_spec.rb +++ b/spec/services/namespaces/in_product_marketing_emails_service_spec.rb @@ -39,20 +39,21 @@ RSpec.describe Namespaces::InProductMarketingEmailsService, '#execute' do using RSpec::Parameterized::TableSyntax where(:track, :interval, :actions_completed) do - :create | 1 | { created_at: frozen_time - 2.days } - :create | 5 | { created_at: frozen_time - 6.days } - :create | 10 | { created_at: frozen_time - 11.days } - :team_short | 1 | { created_at: frozen_time - 2.days, git_write_at: frozen_time - 2.days } - :verify | 2 | { created_at: frozen_time - 3.days, git_write_at: frozen_time - 3.days } - :verify | 6 | { created_at: frozen_time - 7.days, git_write_at: frozen_time - 7.days } - :verify | 11 | { created_at: frozen_time - 12.days, git_write_at: frozen_time - 12.days } - :trial | 1 | { created_at: frozen_time - 2.days, git_write_at: frozen_time - 2.days, pipeline_created_at: frozen_time - 2.days } - :trial | 5 | { created_at: frozen_time - 6.days, git_write_at: frozen_time - 6.days, pipeline_created_at: frozen_time - 6.days } - :trial | 10 | { created_at: frozen_time - 11.days, git_write_at: frozen_time - 11.days, pipeline_created_at: frozen_time - 11.days } - :team | 1 | { created_at: frozen_time - 2.days, git_write_at: frozen_time - 2.days, pipeline_created_at: frozen_time - 2.days, trial_started_at: frozen_time - 2.days } - :team | 5 | { created_at: frozen_time - 6.days, git_write_at: frozen_time - 6.days, pipeline_created_at: frozen_time - 6.days, trial_started_at: frozen_time - 6.days } - :team | 10 | { created_at: frozen_time - 11.days, git_write_at: frozen_time - 11.days, pipeline_created_at: frozen_time - 11.days, trial_started_at: frozen_time - 11.days } - :experience | 30 | { created_at: frozen_time - 31.days, git_write_at: frozen_time - 31.days } + :create | 1 | { created_at: frozen_time - 2.days } + :create | 5 | { created_at: frozen_time - 6.days } + :create | 10 | { created_at: frozen_time - 11.days } + :team_short | 1 | { created_at: frozen_time - 2.days, git_write_at: frozen_time - 2.days } + :trial_short | 2 | { created_at: frozen_time - 3.days, git_write_at: frozen_time - 3.days } + :verify | 3 | { created_at: frozen_time - 4.days, git_write_at: frozen_time - 4.days } + :verify | 7 | { created_at: frozen_time - 8.days, git_write_at: frozen_time - 8.days } + :verify | 12 | { created_at: frozen_time - 13.days, git_write_at: frozen_time - 13.days } + :trial | 1 | { created_at: frozen_time - 2.days, git_write_at: frozen_time - 2.days, pipeline_created_at: frozen_time - 2.days } + :trial | 5 | { created_at: frozen_time - 6.days, git_write_at: frozen_time - 6.days, pipeline_created_at: frozen_time - 6.days } + :trial | 10 | { created_at: frozen_time - 11.days, git_write_at: frozen_time - 11.days, pipeline_created_at: frozen_time - 11.days } + :team | 1 | { created_at: frozen_time - 2.days, git_write_at: frozen_time - 2.days, pipeline_created_at: frozen_time - 2.days, trial_started_at: frozen_time - 2.days } + :team | 5 | { created_at: frozen_time - 6.days, git_write_at: frozen_time - 6.days, pipeline_created_at: frozen_time - 6.days, trial_started_at: frozen_time - 6.days } + :team | 10 | { created_at: frozen_time - 11.days, git_write_at: frozen_time - 11.days, pipeline_created_at: frozen_time - 11.days, trial_started_at: frozen_time - 11.days } + :experience | 30 | { created_at: frozen_time - 31.days, git_write_at: frozen_time - 31.days } end with_them do |