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>2022-09-23 15:13:18 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-09-23 15:13:18 +0300
commit1ccebc7b3f091ef585dc87a91847aa35f7ae2130 (patch)
tree2e7970962dd429c3dd98ca09e52004df6b3aaa30 /spec
parentfd767b7d65e0e9cb6f313b1803b859b8a7c0da57 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/merge_request/user_manages_subscription_spec.rb4
-rw-r--r--spec/frontend/pages/shared/wikis/components/wiki_form_spec.js266
-rw-r--r--spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js226
-rw-r--r--spec/lib/gitlab/background_migration/backfill_internal_on_notes_spec.rb30
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb36
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb16
-rw-r--r--spec/migrations/20220920124709_backfill_internal_on_notes_spec.rb31
-rw-r--r--spec/models/ci/pipeline_spec.rb30
-rw-r--r--spec/services/ci/create_pipeline_service/rules_spec.rb41
9 files changed, 403 insertions, 277 deletions
diff --git a/spec/features/merge_request/user_manages_subscription_spec.rb b/spec/features/merge_request/user_manages_subscription_spec.rb
index 9fb85957979..ec22201b88f 100644
--- a/spec/features/merge_request/user_manages_subscription_spec.rb
+++ b/spec/features/merge_request/user_manages_subscription_spec.rb
@@ -50,15 +50,11 @@ RSpec.describe 'User manages subscription', :js do
wait_for_requests
- click_button 'Toggle dropdown'
-
expect(page).to have_selector('.gl-toggle.is-checked')
find('[data-testid="notifications-toggle"] .gl-toggle').click
wait_for_requests
- click_button 'Toggle dropdown'
-
expect(page).to have_selector('.gl-toggle:not(.is-checked)')
end
end
diff --git a/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js b/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js
index b37d2f06191..83aced63475 100644
--- a/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js
+++ b/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js
@@ -1,15 +1,12 @@
import { nextTick } from 'vue';
-import { GlAlert, GlButton, GlFormInput, GlFormGroup, GlSegmentedControl } from '@gitlab/ui';
+import { GlAlert, GlButton, GlFormInput, GlFormGroup } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { mockTracking } from 'helpers/tracking_helper';
-import { stubComponent } from 'helpers/stub_component';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import ContentEditor from '~/content_editor/components/content_editor.vue';
-import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import WikiForm from '~/pages/shared/wikis/components/wiki_form.vue';
+import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue';
import {
CONTENT_EDITOR_LOADED_ACTION,
SAVED_USING_CONTENT_EDITOR_ACTION,
@@ -18,8 +15,6 @@ import {
WIKI_FORMAT_UPDATED_ACTION,
} from '~/pages/shared/wikis/constants';
-import MarkdownField from '~/vue_shared/components/markdown/field.vue';
-
jest.mock('~/emoji');
describe('WikiForm', () => {
@@ -30,16 +25,12 @@ describe('WikiForm', () => {
const findForm = () => wrapper.find('form');
const findTitle = () => wrapper.find('#wiki_title');
const findFormat = () => wrapper.find('#wiki_format');
- const findContent = () => wrapper.find('#wiki_content');
const findMessage = () => wrapper.find('#wiki_message');
+ const findMarkdownEditor = () => wrapper.findComponent(MarkdownEditor);
const findSubmitButton = () => wrapper.findByTestId('wiki-submit-button');
const findCancelButton = () => wrapper.findByTestId('wiki-cancel-button');
- const findToggleEditingModeButton = () => wrapper.findByTestId('toggle-editing-mode-button');
const findTitleHelpLink = () => wrapper.findByText('Learn more.');
const findMarkdownHelpLink = () => wrapper.findByTestId('wiki-markdown-help-link');
- const findContentEditor = () => wrapper.findComponent(ContentEditor);
- const findClassicEditor = () => wrapper.findComponent(MarkdownField);
- const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
const setFormat = (value) => {
const format = findFormat();
@@ -103,11 +94,8 @@ describe('WikiForm', () => {
},
},
stubs: {
- MarkdownField,
GlAlert,
GlButton,
- GlSegmentedControl,
- LocalStorageSync: stubComponent(LocalStorageSync),
GlFormInput,
GlFormGroup,
},
@@ -126,6 +114,22 @@ describe('WikiForm', () => {
wrapper = null;
});
+ it('displays markdown editor', () => {
+ createWrapper({ persisted: true });
+
+ expect(findMarkdownEditor().props()).toEqual(
+ expect.objectContaining({
+ value: pageInfoPersisted.content,
+ renderMarkdownPath: pageInfoPersisted.markdownPreviewPath,
+ markdownDocsPath: pageInfoPersisted.markdownHelpPath,
+ uploadsPath: pageInfoPersisted.uploadsPath,
+ autofocus: pageInfoPersisted.persisted,
+ formFieldId: 'wiki_content',
+ formFieldName: 'wiki[content]',
+ }),
+ );
+ });
+
it.each`
title | persisted | message
${'my page'} | ${false} | ${'Create my page'}
@@ -154,7 +158,7 @@ describe('WikiForm', () => {
it('does not trim page content by default', () => {
createWrapper({ persisted: true });
- expect(findContent().element.value).toBe(' My page content ');
+ expect(findMarkdownEditor().props().value).toBe(' My page content ');
});
it.each`
@@ -168,7 +172,9 @@ describe('WikiForm', () => {
await setFormat(format);
- expect(findClassicEditor().props('enablePreview')).toBe(enabled);
+ nextTick();
+
+ expect(findMarkdownEditor().props('enablePreview')).toBe(enabled);
});
it.each`
@@ -219,9 +225,7 @@ describe('WikiForm', () => {
beforeEach(async () => {
createWrapper({ mountFn: mount, persisted: true });
- const input = findContent();
-
- await input.setValue(' Lorem ipsum dolar sit! ');
+ await findMarkdownEditor().vm.$emit('input', ' Lorem ipsum dolar sit! ');
});
it('sets before unload warning', () => {
@@ -245,7 +249,7 @@ describe('WikiForm', () => {
});
it('does not trim page content', () => {
- expect(findContent().element.value).toBe(' Lorem ipsum dolar sit! ');
+ expect(findMarkdownEditor().props().value).toBe(' Lorem ipsum dolar sit! ');
});
});
});
@@ -264,7 +268,7 @@ describe('WikiForm', () => {
createWrapper({ mountFn: mount });
await findTitle().setValue(title);
- await findContent().setValue(content);
+ await findMarkdownEditor().vm.$emit('input', content);
expect(findSubmitButton().props().disabled).toBe(disabledAttr);
},
@@ -296,208 +300,64 @@ describe('WikiForm', () => {
);
});
- describe('toggle editing mode control', () => {
- beforeEach(() => {
- createWrapper({ mountFn: mount });
- });
+ it.each`
+ format | enabled | action
+ ${'markdown'} | ${true} | ${'enables'}
+ ${'rdoc'} | ${false} | ${'disables'}
+ ${'asciidoc'} | ${false} | ${'disables'}
+ ${'org'} | ${false} | ${'disables'}
+ `('$action content editor when format is $format', async ({ format, enabled }) => {
+ createWrapper({ mountFn: mount });
- it.each`
- format | exists | action
- ${'markdown'} | ${true} | ${'displays'}
- ${'rdoc'} | ${false} | ${'hides'}
- ${'asciidoc'} | ${false} | ${'hides'}
- ${'org'} | ${false} | ${'hides'}
- `('$action toggle editing mode button when format is $format', async ({ format, exists }) => {
- await setFormat(format);
-
- expect(findToggleEditingModeButton().exists()).toBe(exists);
- });
+ setFormat(format);
- describe('when content editor is not active', () => {
- it('displays "Source" label in the toggle editing mode button', () => {
- expect(findToggleEditingModeButton().props().checked).toBe('source');
- });
+ await nextTick();
- describe('when clicking the toggle editing mode button', () => {
- beforeEach(async () => {
- await findToggleEditingModeButton().vm.$emit('input', 'richText');
- });
+ expect(findMarkdownEditor().props().enableContentEditor).toBe(enabled);
+ });
- it('hides the classic editor', () => {
- expect(findClassicEditor().exists()).toBe(false);
- });
+ describe('when markdown editor activates the content editor', () => {
+ beforeEach(async () => {
+ createWrapper({ mountFn: mount, persisted: true });
- it('shows the content editor', () => {
- expect(findContentEditor().exists()).toBe(true);
- });
- });
+ await findMarkdownEditor().vm.$emit('contentEditor');
});
- describe('markdown editor type persistance', () => {
- it('loads content editor by default if it is persisted in local storage', async () => {
- expect(findClassicEditor().exists()).toBe(true);
- expect(findContentEditor().exists()).toBe(false);
-
- // enable content editor
- await findLocalStorageSync().vm.$emit('input', 'richText');
-
- expect(findContentEditor().exists()).toBe(true);
- expect(findClassicEditor().exists()).toBe(false);
- });
+ it('disables the format dropdown', () => {
+ expect(findFormat().element.getAttribute('disabled')).toBeDefined();
});
- describe('when content editor is active', () => {
- beforeEach(() => {
- createWrapper();
- findToggleEditingModeButton().vm.$emit('input', 'richText');
- });
-
- it('displays "Edit Rich" label in the toggle editing mode button', () => {
- expect(findToggleEditingModeButton().props().checked).toBe('richText');
- });
-
- describe('when clicking the toggle editing mode button', () => {
- beforeEach(async () => {
- await findToggleEditingModeButton().vm.$emit('input', 'source');
- await nextTick();
- });
-
- it('hides the content editor', () => {
- expect(findContentEditor().exists()).toBe(false);
- });
-
- it('displays the classic editor', () => {
- expect(findClassicEditor().exists()).toBe(true);
- });
- });
-
- describe('when content editor is loading', () => {
- beforeEach(async () => {
- findContentEditor().vm.$emit('loading');
-
- await nextTick();
- });
-
- it('disables toggle editing mode button', () => {
- expect(findToggleEditingModeButton().attributes().disabled).toBe('true');
- });
-
- describe('when content editor loads successfully', () => {
- it('enables toggle editing mode button', async () => {
- findContentEditor().vm.$emit('loadingSuccess');
-
- await nextTick();
-
- expect(findToggleEditingModeButton().attributes().disabled).not.toBeDefined();
- });
- });
-
- describe('when content editor fails to load', () => {
- it('enables toggle editing mode button', async () => {
- findContentEditor().vm.$emit('loadingError');
-
- await nextTick();
-
- expect(findToggleEditingModeButton().attributes().disabled).not.toBeDefined();
- });
- });
+ it('sends tracking event when editor loads', async () => {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, CONTENT_EDITOR_LOADED_ACTION, {
+ label: WIKI_CONTENT_EDITOR_TRACKING_LABEL,
});
});
- });
- describe('wiki content editor', () => {
- describe('clicking "Edit rich text": editor fails to load', () => {
- beforeEach(async () => {
- createWrapper({ mountFn: mount });
- mock.onPost(/preview-markdown/).reply(400);
+ describe('when triggering form submit', () => {
+ const updatedMarkdown = 'hello **world**';
- await findToggleEditingModeButton().vm.$emit('input', 'richText');
-
- // try waiting for content editor to load (but it will never actually load)
- await waitForPromises();
- });
-
- it('disables the submit button', () => {
- expect(findSubmitButton().props('disabled')).toBe(true);
- });
-
- describe('toggling editing modes to the classic editor', () => {
- beforeEach(() => {
- return findToggleEditingModeButton().vm.$emit('input', 'source');
- });
-
- it('switches to classic editor', () => {
- expect(findContentEditor().exists()).toBe(false);
- expect(findClassicEditor().exists()).toBe(true);
- });
- });
- });
-
- describe('clicking "Edit rich text": editor loads successfully', () => {
beforeEach(async () => {
- createWrapper({ persisted: true, mountFn: mount });
-
- mock.onPost(/preview-markdown/).reply(200, { body: '<p>hello <strong>world</strong></p>' });
-
- await findToggleEditingModeButton().vm.$emit('input', 'richText');
- await waitForPromises();
+ findMarkdownEditor().vm.$emit('input', updatedMarkdown);
+ await triggerFormSubmit();
});
- it('shows the rich text editor when loading finishes', async () => {
- expect(findContentEditor().exists()).toBe(true);
+ it('unsets before unload warning on form submit', async () => {
+ const e = dispatchBeforeUnload();
+ expect(e.preventDefault).not.toHaveBeenCalled();
});
- it('sends tracking event when editor loads', async () => {
- expect(trackingSpy).toHaveBeenCalledWith(undefined, CONTENT_EDITOR_LOADED_ACTION, {
+ it('triggers tracking events on form submit', async () => {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, SAVED_USING_CONTENT_EDITOR_ACTION, {
label: WIKI_CONTENT_EDITOR_TRACKING_LABEL,
});
- });
-
- it('disables the format dropdown', () => {
- expect(findFormat().element.getAttribute('disabled')).toBeDefined();
- });
-
- describe('when wiki content is updated', () => {
- const updatedMarkdown = 'hello **world**';
-
- beforeEach(() => {
- findContentEditor().vm.$emit('change', {
- empty: false,
- changed: true,
- markdown: updatedMarkdown,
- });
- });
-
- it('sets before unload warning', () => {
- const e = dispatchBeforeUnload();
- expect(e.preventDefault).toHaveBeenCalledTimes(1);
- });
-
- it('unsets before unload warning on form submit', async () => {
- await triggerFormSubmit();
-
- const e = dispatchBeforeUnload();
- expect(e.preventDefault).not.toHaveBeenCalled();
- });
- it('triggers tracking events on form submit', async () => {
- await triggerFormSubmit();
- expect(trackingSpy).toHaveBeenCalledWith(undefined, SAVED_USING_CONTENT_EDITOR_ACTION, {
- label: WIKI_CONTENT_EDITOR_TRACKING_LABEL,
- });
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, WIKI_FORMAT_UPDATED_ACTION, {
- label: WIKI_FORMAT_LABEL,
- extra: {
- value: findFormat().element.value,
- old_format: pageInfoPersisted.format,
- project_path: pageInfoPersisted.path,
- },
- });
- });
-
- it('sets content field to the content editor updated markdown', async () => {
- expect(findContent().element.value).toBe(updatedMarkdown);
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, WIKI_FORMAT_UPDATED_ACTION, {
+ label: WIKI_FORMAT_LABEL,
+ extra: {
+ value: findFormat().element.value,
+ old_format: pageInfoPersisted.format,
+ project_path: pageInfoPersisted.path,
+ },
});
});
});
diff --git a/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js b/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js
new file mode 100644
index 00000000000..da3777de1d0
--- /dev/null
+++ b/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js
@@ -0,0 +1,226 @@
+import { GlSegmentedControl } from '@gitlab/ui';
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { EDITING_MODE_MARKDOWN_FIELD, EDITING_MODE_CONTENT_EDITOR } from '~/vue_shared/constants';
+import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue';
+import ContentEditor from '~/content_editor/components/content_editor.vue';
+import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
+import MarkdownField from '~/vue_shared/components/markdown/field.vue';
+
+jest.mock('~/emoji');
+
+describe('vue_shared/component/markdown/markdown_editor', () => {
+ let wrapper;
+ const value = 'test markdown';
+ const renderMarkdownPath = '/api/markdown';
+ const markdownDocsPath = '/help/markdown';
+ const uploadsPath = '/uploads';
+ const enableAutocomplete = true;
+ const enablePreview = false;
+ const formFieldId = 'markdown_field';
+ const formFieldName = 'form[markdown_field]';
+ const formFieldPlaceholder = 'Write some markdown';
+ const formFieldAriaLabel = 'Edit your content';
+ let mock;
+
+ const buildWrapper = (propsData = {}) => {
+ wrapper = mountExtended(MarkdownEditor, {
+ propsData: {
+ value,
+ renderMarkdownPath,
+ markdownDocsPath,
+ uploadsPath,
+ enableAutocomplete,
+ enablePreview,
+ formFieldId,
+ formFieldName,
+ formFieldPlaceholder,
+ formFieldAriaLabel,
+ ...propsData,
+ },
+ });
+ };
+ const findSegmentedControl = () => wrapper.findComponent(GlSegmentedControl);
+ const findMarkdownField = () => wrapper.findComponent(MarkdownField);
+ const findTextarea = () => wrapper.find('textarea');
+ const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
+ const findContentEditor = () => wrapper.findComponent(ContentEditor);
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ mock.restore();
+ });
+
+ it('displays markdown field by default', () => {
+ buildWrapper();
+
+ expect(findMarkdownField().props()).toEqual(
+ expect.objectContaining({
+ markdownPreviewPath: renderMarkdownPath,
+ canAttachFile: true,
+ enableAutocomplete,
+ textareaValue: value,
+ markdownDocsPath,
+ uploadsPath,
+ enablePreview,
+ }),
+ );
+ });
+
+ it('renders markdown field textarea', () => {
+ buildWrapper();
+
+ expect(findTextarea().attributes()).toEqual(
+ expect.objectContaining({
+ id: formFieldId,
+ name: formFieldName,
+ placeholder: formFieldPlaceholder,
+ 'aria-label': formFieldAriaLabel,
+ }),
+ );
+
+ expect(findTextarea().element.value).toBe(value);
+ });
+
+ it('renders switch segmented control', () => {
+ buildWrapper();
+
+ expect(findSegmentedControl().props()).toEqual({
+ checked: EDITING_MODE_MARKDOWN_FIELD,
+ options: [
+ {
+ text: expect.any(String),
+ value: EDITING_MODE_MARKDOWN_FIELD,
+ },
+ {
+ text: expect.any(String),
+ value: EDITING_MODE_CONTENT_EDITOR,
+ },
+ ],
+ });
+ });
+
+ describe.each`
+ editingMode
+ ${EDITING_MODE_CONTENT_EDITOR}
+ ${EDITING_MODE_MARKDOWN_FIELD}
+ `('when segmented control emits change event with $editingMode value', ({ editingMode }) => {
+ it(`emits ${editingMode} event`, () => {
+ buildWrapper();
+
+ findSegmentedControl().vm.$emit('change', editingMode);
+
+ expect(wrapper.emitted(editingMode)).toHaveLength(1);
+ });
+ });
+
+ describe(`when editingMode is ${EDITING_MODE_MARKDOWN_FIELD}`, () => {
+ it('emits input event when markdown field textarea changes', async () => {
+ buildWrapper();
+ const newValue = 'new value';
+
+ await findTextarea().setValue(newValue);
+
+ expect(wrapper.emitted('input')).toEqual([[newValue]]);
+ });
+
+ describe(`when segmented control triggers input event with ${EDITING_MODE_CONTENT_EDITOR} value`, () => {
+ beforeEach(() => {
+ buildWrapper();
+ findSegmentedControl().vm.$emit('input', EDITING_MODE_CONTENT_EDITOR);
+ });
+
+ it('displays the content editor', () => {
+ expect(findContentEditor().props()).toEqual(
+ expect.objectContaining({
+ renderMarkdown: expect.any(Function),
+ uploadsPath,
+ markdown: value,
+ }),
+ );
+ });
+
+ it('adds hidden field with current markdown', () => {
+ const hiddenField = wrapper.find(`#${formFieldId}`);
+
+ expect(hiddenField.attributes()).toEqual(
+ expect.objectContaining({
+ id: formFieldId,
+ name: formFieldName,
+ }),
+ );
+ expect(hiddenField.element.value).toBe(value);
+ });
+
+ it('hides the markdown field', () => {
+ expect(findMarkdownField().exists()).toBe(false);
+ });
+
+ it('updates localStorage value', () => {
+ expect(findLocalStorageSync().props().value).toBe(EDITING_MODE_CONTENT_EDITOR);
+ });
+ });
+ });
+
+ describe(`when editingMode is ${EDITING_MODE_CONTENT_EDITOR}`, () => {
+ beforeEach(() => {
+ buildWrapper();
+ findSegmentedControl().vm.$emit('input', EDITING_MODE_CONTENT_EDITOR);
+ });
+
+ it('emits input event when content editor emits change event', async () => {
+ const newValue = 'new value';
+
+ await findContentEditor().vm.$emit('change', { markdown: newValue });
+
+ expect(wrapper.emitted('input')).toEqual([[newValue]]);
+ });
+
+ describe(`when segmented control triggers input event with ${EDITING_MODE_MARKDOWN_FIELD} value`, () => {
+ beforeEach(() => {
+ findSegmentedControl().vm.$emit('input', EDITING_MODE_MARKDOWN_FIELD);
+ });
+
+ it('hides the content editor', () => {
+ expect(findContentEditor().exists()).toBe(false);
+ });
+
+ it('shows the markdown field', () => {
+ expect(findMarkdownField().exists()).toBe(true);
+ });
+
+ it('updates localStorage value', () => {
+ expect(findLocalStorageSync().props().value).toBe(EDITING_MODE_MARKDOWN_FIELD);
+ });
+ });
+
+ describe('when content editor emits loading event', () => {
+ beforeEach(() => {
+ findContentEditor().vm.$emit('loading');
+ });
+
+ it('disables switch editing mode control', () => {
+ // This is the only way that I found to check the segmented control is disabled
+ expect(findSegmentedControl().find('input[disabled]').exists()).toBe(true);
+ });
+
+ describe.each`
+ event
+ ${'loadingSuccess'}
+ ${'loadingError'}
+ `('when content editor emits $event event', ({ event }) => {
+ beforeEach(() => {
+ findContentEditor().vm.$emit(event);
+ });
+ it('enables the switch editing mode control', () => {
+ expect(findSegmentedControl().find('input[disabled]').exists()).toBe(false);
+ });
+ });
+ });
+ });
+});
diff --git a/spec/lib/gitlab/background_migration/backfill_internal_on_notes_spec.rb b/spec/lib/gitlab/background_migration/backfill_internal_on_notes_spec.rb
new file mode 100644
index 00000000000..40a4758ba5f
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_internal_on_notes_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BackfillInternalOnNotes, :migration, schema: 20220920124709 do
+ let(:notes_table) { table(:notes) }
+
+ let!(:confidential_note) { notes_table.create!(id: 1, confidential: true, internal: false) }
+ let!(:non_confidential_note) { notes_table.create!(id: 2, confidential: false, internal: false) }
+
+ describe '#perform' do
+ subject(:perform) do
+ described_class.new(
+ start_id: 1,
+ end_id: 2,
+ batch_table: :notes,
+ batch_column: :id,
+ sub_batch_size: 1,
+ pause_ms: 0,
+ connection: ApplicationRecord.connection
+ ).perform
+ end
+
+ it 'backfills internal column on notes when confidential' do
+ expect { perform }
+ .to change { confidential_note.reload.internal }.from(false).to(true)
+ .and not_change { non_confidential_note.reload.internal }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
index 234ba68d627..a22aa30304b 100644
--- a/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
@@ -122,19 +122,17 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
context 'when compare_to is branch or tag' do
using RSpec::Parameterized::TableSyntax
- where(:pipeline_ref, :compare_to, :paths, :ff, :result) do
- 'feature_1' | 'master' | ['file1.txt'] | true | true
- 'feature_1' | 'master' | ['README.md'] | true | false
- 'feature_1' | 'master' | ['xyz.md'] | true | false
- 'feature_2' | 'master' | ['file1.txt'] | true | true
- 'feature_2' | 'master' | ['file2.txt'] | true | true
- 'feature_2' | 'feature_1' | ['file1.txt'] | true | false
- 'feature_2' | 'feature_1' | ['file1.txt'] | false | true
- 'feature_2' | 'feature_1' | ['file2.txt'] | true | true
- 'feature_1' | 'tag_1' | ['file1.txt'] | true | false
- 'feature_1' | 'tag_1' | ['file1.txt'] | false | true
- 'feature_1' | 'tag_1' | ['file2.txt'] | true | true
- 'feature_2' | 'tag_1' | ['file2.txt'] | true | true
+ where(:pipeline_ref, :compare_to, :paths, :result) do
+ 'feature_1' | 'master' | ['file1.txt'] | true
+ 'feature_1' | 'master' | ['README.md'] | false
+ 'feature_1' | 'master' | ['xyz.md'] | false
+ 'feature_2' | 'master' | ['file1.txt'] | true
+ 'feature_2' | 'master' | ['file2.txt'] | true
+ 'feature_2' | 'feature_1' | ['file1.txt'] | false
+ 'feature_2' | 'feature_1' | ['file2.txt'] | true
+ 'feature_1' | 'tag_1' | ['file1.txt'] | false
+ 'feature_1' | 'tag_1' | ['file2.txt'] | true
+ 'feature_2' | 'tag_1' | ['file2.txt'] | true
end
with_them do
@@ -144,10 +142,6 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
build(:ci_pipeline, project: project, ref: pipeline_ref, sha: project.commit(pipeline_ref).sha)
end
- before do
- stub_feature_flags(ci_rules_changes_compare: ff)
- end
-
it { is_expected.to eq(result) }
end
end
@@ -174,14 +168,6 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
::Gitlab::Ci::Build::Rules::Rule::Clause::ParseError, 'rules:changes:compare_to is not a valid ref'
)
end
-
- context 'when the FF ci_rules_changes_compare is disabled' do
- before do
- stub_feature_flags(ci_rules_changes_compare: false)
- end
-
- it { is_expected.to be_truthy }
- end
end
end
end
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
index 8f9c3573897..9ff3ad1dfc0 100644
--- a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
@@ -11,14 +11,21 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::RedisMetric, :clean_git
let(:expected_value) { 4 }
- it_behaves_like 'a correct instrumented metric value', { options: { event: 'pushes', prefix: 'source_code' } }
+ it_behaves_like 'a correct instrumented metric value', {
+ options: { event: 'pushes', prefix: 'source_code' },
+ time_frame: 'all'
+ }
it 'raises an exception if event option is not present' do
- expect { described_class.new(prefix: 'source_code') }.to raise_error(ArgumentError)
+ expect do
+ described_class.new(options: { prefix: 'source_code' }, time_frame: 'all')
+ end.to raise_error(ArgumentError, /'event' option is required/)
end
it 'raises an exception if prefix option is not present' do
- expect { described_class.new(event: 'pushes') }.to raise_error(ArgumentError)
+ expect do
+ described_class.new(options: { event: 'pushes' }, time_frame: 'all')
+ end.to raise_error(ArgumentError, /'prefix' option is required/)
end
describe 'children classes' do
@@ -55,7 +62,8 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::RedisMetric, :clean_git
end
it_behaves_like 'a correct instrumented metric value', {
- options: { event: 'merge_requests_count', prefix: 'web_ide', include_usage_prefix: false }
+ options: { event: 'merge_requests_count', prefix: 'web_ide', include_usage_prefix: false },
+ time_frame: 'all'
}
end
diff --git a/spec/migrations/20220920124709_backfill_internal_on_notes_spec.rb b/spec/migrations/20220920124709_backfill_internal_on_notes_spec.rb
new file mode 100644
index 00000000000..f4ac6e6fc8e
--- /dev/null
+++ b/spec/migrations/20220920124709_backfill_internal_on_notes_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe BackfillInternalOnNotes, :migration do
+ let(:migration) { described_class::MIGRATION }
+
+ describe '#up' do
+ it 'schedules background jobs for each batch of issues' do
+ migrate!
+
+ expect(migration).to have_scheduled_batched_migration(
+ table_name: :notes,
+ column_name: :id,
+ interval: described_class::DELAY_INTERVAL,
+ batch_size: described_class::BATCH_SIZE,
+ sub_batch_size: described_class::SUB_BATCH_SIZE
+ )
+ end
+ end
+
+ describe '#down' do
+ it 'deletes all batched migration records' do
+ migrate!
+ schema_migrate_down!
+
+ expect(migration).not_to have_scheduled_batched_migration
+ end
+ end
+end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 2e3d5302a76..79f2565e173 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -5516,4 +5516,34 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
end
+
+ describe '#notes=' do
+ context 'when notes already exist' do
+ it 'does not create duplicate notes', :aggregate_failures do
+ time = Time.zone.now
+ pipeline = create(:ci_pipeline, user: user, project: project)
+ note = Note.new(
+ note: 'note',
+ noteable_type: 'Commit',
+ noteable_id: pipeline.id,
+ commit_id: pipeline.id,
+ author_id: user.id,
+ project_id: pipeline.project_id,
+ created_at: time
+ )
+ another_note = note.dup.tap { |note| note.note = 'another note' }
+
+ expect(project.notes.for_commit_id(pipeline.sha).count).to eq(0)
+
+ pipeline.notes = [note]
+
+ expect(project.notes.for_commit_id(pipeline.sha).count).to eq(1)
+
+ pipeline.notes = [note, note, another_note]
+
+ expect(project.notes.for_commit_id(pipeline.sha).count).to eq(2)
+ expect(project.notes.for_commit_id(pipeline.sha).pluck(:note)).to contain_exactly(note.note, another_note.note)
+ end
+ end
+ end
end
diff --git a/spec/services/ci/create_pipeline_service/rules_spec.rb b/spec/services/ci/create_pipeline_service/rules_spec.rb
index fc57ca66d3a..87e9b4400e7 100644
--- a/spec/services/ci/create_pipeline_service/rules_spec.rb
+++ b/spec/services/ci/create_pipeline_service/rules_spec.rb
@@ -544,16 +544,6 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
'Failed to parse rule for job1: rules:changes:compare_to is not a valid ref'
])
end
-
- context 'when the FF ci_rules_changes_compare is not enabled' do
- before do
- stub_feature_flags(ci_rules_changes_compare: false)
- end
-
- it 'ignores compare_to and changes is always true' do
- expect(build_names).to contain_exactly('job1', 'job2')
- end
- end
end
context 'when the compare_to ref exists' do
@@ -563,16 +553,6 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
it 'creates job1 and job2' do
expect(build_names).to contain_exactly('job1', 'job2')
end
-
- context 'when the FF ci_rules_changes_compare is not enabled' do
- before do
- stub_feature_flags(ci_rules_changes_compare: false)
- end
-
- it 'ignores compare_to and changes is always true' do
- expect(build_names).to contain_exactly('job1', 'job2')
- end
- end
end
context 'when the rule does not match' do
@@ -581,16 +561,6 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
it 'does not create job1' do
expect(build_names).to contain_exactly('job2')
end
-
- context 'when the FF ci_rules_changes_compare is not enabled' do
- before do
- stub_feature_flags(ci_rules_changes_compare: false)
- end
-
- it 'ignores compare_to and changes is always true' do
- expect(build_names).to contain_exactly('job1', 'job2')
- end
- end
end
end
end
@@ -616,17 +586,6 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
expect(pipeline).to be_created_successfully
expect(build_names).to contain_exactly('job1')
end
-
- context 'when the FF ci_rules_changes_compare is not enabled' do
- before do
- stub_feature_flags(ci_rules_changes_compare: false)
- end
-
- it 'ignores compare_to and changes is always true' do
- expect(pipeline).to be_created_successfully
- expect(build_names).to contain_exactly('job1')
- end
- end
end
context 'when the rule does not match' do