diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-13 18:12:17 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-13 18:12:17 +0300 |
commit | 4597f7fe473d9fa622510f8967620006d4bda64e (patch) | |
tree | dafe547a51e57112ad92258f4bf992c014591a88 /spec | |
parent | 37a739daec0d7021b2af6ad03c60d37ac3461d88 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
19 files changed, 295 insertions, 116 deletions
diff --git a/spec/frontend/content_editor/extensions/paste_markdown_spec.js b/spec/frontend/content_editor/extensions/paste_markdown_spec.js index 53efda6aee2..30e798e8817 100644 --- a/spec/frontend/content_editor/extensions/paste_markdown_spec.js +++ b/spec/frontend/content_editor/extensions/paste_markdown_spec.js @@ -5,12 +5,7 @@ import Frontmatter from '~/content_editor/extensions/frontmatter'; import Bold from '~/content_editor/extensions/bold'; import { VARIANT_DANGER } from '~/flash'; import eventHubFactory from '~/helpers/event_hub_factory'; -import { - ALERT_EVENT, - LOADING_CONTENT_EVENT, - LOADING_SUCCESS_EVENT, - LOADING_ERROR_EVENT, -} from '~/content_editor/constants'; +import { ALERT_EVENT } from '~/content_editor/constants'; import waitForPromises from 'helpers/wait_for_promises'; import { createTestEditor, createDocBuilder, waitUntilNextDocTransaction } from '../test_utils'; @@ -115,13 +110,6 @@ describe('content_editor/extensions/paste_markdown', () => { expect(tiptapEditor.state.doc.toJSON()).toEqual(expectedDoc.toJSON()); }); - - it(`triggers ${LOADING_SUCCESS_EVENT}`, async () => { - await triggerPasteEventHandlerAndWaitForTransaction(buildClipboardEvent()); - - expect(eventHub.$emit).toHaveBeenCalledWith(LOADING_CONTENT_EVENT); - expect(eventHub.$emit).toHaveBeenCalledWith(LOADING_SUCCESS_EVENT); - }); }); describe('when rendering markdown fails', () => { @@ -129,13 +117,6 @@ describe('content_editor/extensions/paste_markdown', () => { renderMarkdown.mockRejectedValueOnce(); }); - it(`triggers ${LOADING_ERROR_EVENT} event`, async () => { - await triggerPasteEventHandler(buildClipboardEvent()); - await waitForPromises(); - - expect(eventHub.$emit).toHaveBeenCalledWith(LOADING_ERROR_EVENT); - }); - it(`triggers ${ALERT_EVENT} event`, async () => { await triggerPasteEventHandler(buildClipboardEvent()); await waitForPromises(); diff --git a/spec/frontend/cycle_analytics/__snapshots__/total_time_spec.js.snap b/spec/frontend/cycle_analytics/__snapshots__/total_time_spec.js.snap index 7f211c1028e..92927ef16ec 100644 --- a/spec/frontend/cycle_analytics/__snapshots__/total_time_spec.js.snap +++ b/spec/frontend/cycle_analytics/__snapshots__/total_time_spec.js.snap @@ -1,28 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`TotalTime with a blank object should render -- 1`] = `"<span class=\\"total-time\\"> -- </span>"`; +exports[`TotalTime with a blank object should render -- 1`] = `"<span> -- </span>"`; exports[`TotalTime with a valid time object with {"days": 3, "mins": 47, "seconds": 3} 1`] = ` -"<span class=\\"total-time\\"> +"<span> 3 <span>days</span></span>" `; exports[`TotalTime with a valid time object with {"hours": 7, "mins": 20, "seconds": 10} 1`] = ` -"<span class=\\"total-time\\"> +"<span> 7 <span>hrs</span></span>" `; exports[`TotalTime with a valid time object with {"hours": 23, "mins": 10} 1`] = ` -"<span class=\\"total-time\\"> +"<span> 23 <span>hrs</span></span>" `; exports[`TotalTime with a valid time object with {"mins": 47, "seconds": 3} 1`] = ` -"<span class=\\"total-time\\"> +"<span> 47 <span>mins</span></span>" `; exports[`TotalTime with a valid time object with {"seconds": 35} 1`] = ` -"<span class=\\"total-time\\"> +"<span> 35 <span>s</span></span>" `; diff --git a/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap b/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap index 8fe3e92360a..096d776a7d2 100644 --- a/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap +++ b/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap @@ -11,7 +11,7 @@ exports[`Design management list item component when item appears in view after i exports[`Design management list item component with notes renders item with multiple comments 1`] = ` <router-link-stub ariacurrentvalue="page" - class="card gl-cursor-pointer text-plain js-design-list-item design-list-item design-list-item-new" + class="card gl-cursor-pointer text-plain js-design-list-item design-list-item design-list-item-new gl-mb-0" event="click" tag="a" to="[object Object]" @@ -88,7 +88,7 @@ exports[`Design management list item component with notes renders item with mult exports[`Design management list item component with notes renders item with single comment 1`] = ` <router-link-stub ariacurrentvalue="page" - class="card gl-cursor-pointer text-plain js-design-list-item design-list-item design-list-item-new" + class="card gl-cursor-pointer text-plain js-design-list-item design-list-item design-list-item-new gl-mb-0" event="click" tag="a" to="[object Object]" diff --git a/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap index 9997f02cd01..8cfe11c9040 100644 --- a/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap +++ b/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap @@ -9,9 +9,7 @@ exports[`Design management index page designs renders error 1`] = ` <!----> - <div - class="gl-mt-6" - > + <div> <gl-alert-stub dismisslabel="Dismiss" primarybuttonlink="" @@ -43,9 +41,7 @@ exports[`Design management index page designs renders loading icon 1`] = ` <!----> - <div - class="gl-mt-6" - > + <div> <gl-loading-icon-stub color="dark" label="Loading" diff --git a/spec/frontend/design_management/pages/index_spec.js b/spec/frontend/design_management/pages/index_spec.js index f90feaadfb0..1033b509419 100644 --- a/spec/frontend/design_management/pages/index_spec.js +++ b/spec/frontend/design_management/pages/index_spec.js @@ -254,7 +254,7 @@ describe('Design management index page', () => { 'gl-flex-direction-column', 'col-md-6', 'col-lg-3', - 'gl-mb-3', + 'gl-mt-5', ]); }); }); diff --git a/spec/frontend/locale/sprintf_spec.js b/spec/frontend/locale/sprintf_spec.js index 52e903b819f..e0d0e117ea4 100644 --- a/spec/frontend/locale/sprintf_spec.js +++ b/spec/frontend/locale/sprintf_spec.js @@ -63,12 +63,26 @@ describe('locale', () => { it('does not escape parameters for escapeParameters = false', () => { const input = 'contains %{safeContent}'; const parameters = { - safeContent: '<strong>bold attempt</strong>', + safeContent: '15', }; const output = sprintf(input, parameters, false); - expect(output).toBe('contains <strong>bold attempt</strong>'); + expect(output).toBe('contains 15'); + }); + + describe('replaces duplicated % in input', () => { + it('removes duplicated percentage signs', () => { + const input = 'contains duplicated %{safeContent}%%'; + + const parameters = { + safeContent: '15', + }; + + const output = sprintf(input, parameters, false); + + expect(output).toBe('contains duplicated 15%'); + }); }); }); }); diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js index 0f7cf4e61b2..6ece72c41bb 100644 --- a/spec/frontend/repository/components/blob_content_viewer_spec.js +++ b/spec/frontend/repository/components/blob_content_viewer_spec.js @@ -17,7 +17,8 @@ import { loadViewer } from '~/repository/components/blob_viewers'; import DownloadViewer from '~/repository/components/blob_viewers/download_viewer.vue'; import EmptyViewer from '~/repository/components/blob_viewers/empty_viewer.vue'; import SourceViewer from '~/vue_shared/components/source_viewer/source_viewer.vue'; -import blobInfoQuery from '~/repository/queries/blob_info.query.graphql'; +import blobInfoQuery from 'shared_queries/repository/blob_info.query.graphql'; +import projectInfoQuery from '~/repository/queries/project_info.query.graphql'; import userInfoQuery from '~/repository/queries/user_info.query.graphql'; import applicationInfoQuery from '~/repository/queries/application_info.query.graphql'; import CodeIntelligence from '~/code_navigation/components/app.vue'; @@ -45,8 +46,9 @@ jest.mock('~/lib/utils/common_utils'); jest.mock('~/blob/line_highlighter'); let wrapper; -let mockResolver; +let blobInfoMockResolver; let userInfoMockResolver; +let projectInfoMockResolver; let applicationInfoMockResolver; const mockAxios = new MockAdapter(axios); @@ -74,22 +76,40 @@ const createComponent = async (mockData = {}, mountFn = shallowMount, mockRoute highlightJs = true, } = mockData; - const project = { + const blobInfo = { ...projectMock, + repository: { + empty, + blobs: { nodes: [blob] }, + }, + }; + + const projectInfo = { + __typename: 'Project', + id: '123', userPermissions: { pushCode, forkProject, downloadCode, createMergeRequestIn, }, - repository: { - empty, - blobs: { nodes: [blob] }, + pathLocks: { + nodes: [ + { + id: 'test', + path: 'locked_file.js', + user: { id: '123', username: 'root' }, + }, + ], }, }; - mockResolver = jest.fn().mockResolvedValue({ - data: { isBinary, project }, + projectInfoMockResolver = jest.fn().mockResolvedValue({ + data: { project: projectInfo }, + }); + + blobInfoMockResolver = jest.fn().mockResolvedValue({ + data: { isBinary, project: blobInfo }, }); userInfoMockResolver = jest.fn().mockResolvedValue({ @@ -101,8 +121,9 @@ const createComponent = async (mockData = {}, mountFn = shallowMount, mockRoute }); const fakeApollo = createMockApollo([ - [blobInfoQuery, mockResolver], + [blobInfoQuery, blobInfoMockResolver], [userInfoQuery, userInfoMockResolver], + [projectInfoQuery, projectInfoMockResolver], [applicationInfoQuery, applicationInfoMockResolver], ]); @@ -129,7 +150,7 @@ const createComponent = async (mockData = {}, mountFn = shallowMount, mockRoute // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ project, isBinary }); + wrapper.setData({ project: blobInfo, isBinary }); await waitForPromises(); }; @@ -504,14 +525,16 @@ describe('Blob content viewer component', () => { async ({ highlightJs, shouldFetchRawText }) => { await createComponent({ highlightJs }); - expect(mockResolver).toHaveBeenCalledWith(expect.objectContaining({ shouldFetchRawText })); + expect(blobInfoMockResolver).toHaveBeenCalledWith( + expect.objectContaining({ shouldFetchRawText }), + ); }, ); it('is called with originalBranch value if the prop has a value', async () => { await createComponent({ inject: { originalBranch: 'some-branch' } }); - expect(mockResolver).toHaveBeenCalledWith( + expect(blobInfoMockResolver).toHaveBeenCalledWith( expect.objectContaining({ ref: 'some-branch', }), @@ -521,7 +544,7 @@ describe('Blob content viewer component', () => { it('is called with ref value if the originalBranch prop has no value', async () => { await createComponent(); - expect(mockResolver).toHaveBeenCalledWith( + expect(blobInfoMockResolver).toHaveBeenCalledWith( expect.objectContaining({ ref: 'default-ref', }), diff --git a/spec/frontend/repository/mock_data.js b/spec/frontend/repository/mock_data.js index 4db295fe0b7..cda47a5b0a5 100644 --- a/spec/frontend/repository/mock_data.js +++ b/spec/frontend/repository/mock_data.js @@ -1,4 +1,5 @@ export const simpleViewerMock = { + __typename: 'RepositoryBlob', id: '1', name: 'some_file.js', size: 123, diff --git a/spec/frontend/vue_shared/components/upload_dropzone/__snapshots__/upload_dropzone_spec.js.snap b/spec/frontend/vue_shared/components/upload_dropzone/__snapshots__/upload_dropzone_spec.js.snap index 1798ca5ccde..f9d615d4f68 100644 --- a/spec/frontend/vue_shared/components/upload_dropzone/__snapshots__/upload_dropzone_spec.js.snap +++ b/spec/frontend/vue_shared/components/upload_dropzone/__snapshots__/upload_dropzone_spec.js.snap @@ -5,7 +5,7 @@ exports[`Upload dropzone component correctly overrides description and drop mess class="gl-w-full gl-relative" > <button - class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4" + class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0" type="button" > <div @@ -86,7 +86,7 @@ exports[`Upload dropzone component when dragging renders correct template when d class="gl-w-full gl-relative" > <button - class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4" + class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0" type="button" > <div @@ -171,7 +171,7 @@ exports[`Upload dropzone component when dragging renders correct template when d class="gl-w-full gl-relative" > <button - class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4" + class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0" type="button" > <div @@ -256,7 +256,7 @@ exports[`Upload dropzone component when dragging renders correct template when d class="gl-w-full gl-relative" > <button - class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4" + class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0" type="button" > <div @@ -342,7 +342,7 @@ exports[`Upload dropzone component when dragging renders correct template when d class="gl-w-full gl-relative" > <button - class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4" + class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0" type="button" > <div @@ -428,7 +428,7 @@ exports[`Upload dropzone component when dragging renders correct template when d class="gl-w-full gl-relative" > <button - class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4" + class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0" type="button" > <div @@ -514,7 +514,7 @@ exports[`Upload dropzone component when no slot provided renders default dropzon class="gl-w-full gl-relative" > <button - class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4" + class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0" type="button" > <div diff --git a/spec/frontend/work_items/components/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js index 49f7957440d..351a363590c 100644 --- a/spec/frontend/work_items/components/work_item_detail_spec.js +++ b/spec/frontend/work_items/components/work_item_detail_spec.js @@ -142,6 +142,10 @@ describe('WorkItemDetail component', () => { expect(findWorkItemState().exists()).toBe(true); expect(findWorkItemTitle().exists()).toBe(true); }); + + it('updates the document title', () => { + expect(document.title).toEqual('Updated title · Task · test-project-path'); + }); }); describe('close button', () => { diff --git a/spec/frontend_integration/content_editor/content_editor_integration_spec.js b/spec/frontend_integration/content_editor/content_editor_integration_spec.js index 7781a463fd6..c0c6b5e5dc8 100644 --- a/spec/frontend_integration/content_editor/content_editor_integration_spec.js +++ b/spec/frontend_integration/content_editor/content_editor_integration_spec.js @@ -27,6 +27,10 @@ describe('content_editor', () => { await nextTick(); }; + const mockRenderMarkdownResponse = (response) => { + renderMarkdown.mockImplementation((markdown) => (markdown ? response : null)); + }; + beforeEach(() => { renderMarkdown = jest.fn(); }); @@ -34,7 +38,7 @@ describe('content_editor', () => { describe('when loading initial content', () => { describe('when the initial content is empty', () => { it('still hides the loading indicator', async () => { - renderMarkdown.mockResolvedValue(''); + mockRenderMarkdownResponse(''); buildWrapper(); @@ -47,9 +51,11 @@ describe('content_editor', () => { describe('when the initial content is not empty', () => { const initialContent = '<p><strong>bold text</strong></p>'; beforeEach(async () => { - renderMarkdown.mockResolvedValue(initialContent); + mockRenderMarkdownResponse(initialContent); - buildWrapper(); + buildWrapper({ + markdown: '**bold text**', + }); await waitUntilContentIsLoaded(); }); @@ -129,4 +135,38 @@ This reference tag is a mix of letters and numbers [^footnote]. expect(wrapper.findByTestId('table-of-contents').text()).toContain('Heading 1'); expect(wrapper.findByTestId('table-of-contents').text()).toContain('Heading 2'); }); + + describe('when pasting content', () => { + const buildClipboardData = (data = {}) => ({ + clipboardData: { + getData(mimeType) { + return data[mimeType]; + }, + types: Object.keys(data), + }, + }); + + describe('when the clipboard does not contain text/html data', () => { + it('processes the clipboard content as markdown', async () => { + const processedMarkdown = '<strong>bold text</strong>'; + + buildWrapper(); + + await waitUntilContentIsLoaded(); + + mockRenderMarkdownResponse(processedMarkdown); + + wrapper.find('[contenteditable]').trigger( + 'paste', + buildClipboardData({ + 'text/plain': '**bold text**', + }), + ); + + await waitUntilContentIsLoaded(); + + expect(wrapper.find('[contenteditable]').html()).toContain(processedMarkdown); + }); + }); + }); }); diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb index 19a4c82dcc1..3d19987a0be 100644 --- a/spec/lib/gitlab/ci/config/entry/root_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb @@ -155,7 +155,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }], cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success' }], only: { refs: %w(branches tags) }, - job_variables: { 'VAR' => 'job' }, + job_variables: { 'VAR' => { value: 'job' } }, root_variables_inheritance: true, after_script: [], ignore: false, @@ -215,7 +215,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }], stage: 'test', cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }], - job_variables: { 'VAR' => 'job' }, + job_variables: { 'VAR' => { value: 'job' } }, root_variables_inheritance: true, ignore: false, after_script: ['make clean'], diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb index b6cb07bf119..75f6a773c2d 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb @@ -104,8 +104,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do it do is_expected.to include(yaml_variables: [{ key: 'VAR1', value: 'new var 1' }, - { key: 'VAR2', value: 'var 2' }, - { key: 'VAR3', value: 'var 3' }]) + { key: 'VAR3', value: 'var 3' }, + { key: 'VAR2', value: 'var 2' }]) end end diff --git a/spec/lib/gitlab/ci/trace/archive_spec.rb b/spec/lib/gitlab/ci/trace/archive_spec.rb index 3ae0e5d1f0e..f91cb03883a 100644 --- a/spec/lib/gitlab/ci/trace/archive_spec.rb +++ b/spec/lib/gitlab/ci/trace/archive_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Trace::Archive do context 'with transactional fixtures' do - let_it_be(:job) { create(:ci_build, :success, :trace_live) } + let_it_be_with_reload(:job) { create(:ci_build, :success, :trace_live) } let_it_be_with_reload(:trace_metadata) { create(:ci_build_trace_metadata, build: job) } let_it_be(:src_checksum) do job.trace.read { |stream| Digest::MD5.hexdigest(stream.raw) } diff --git a/spec/lib/gitlab/ci/variables/helpers_spec.rb b/spec/lib/gitlab/ci/variables/helpers_spec.rb index ece456ee2d3..fb1e66bd605 100644 --- a/spec/lib/gitlab/ci/variables/helpers_spec.rb +++ b/spec/lib/gitlab/ci/variables/helpers_spec.rb @@ -22,14 +22,14 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do subject { described_class.merge_variables(current_variables, new_variables) } - it { is_expected.to eq(result) } + it { is_expected.to match_array(result) } context 'when new variables is a hash' do let(:new_variables) do { 'key2' => 'value22', 'key3' => 'value3' } end - it { is_expected.to eq(result) } + it { is_expected.to match_array(result) } end context 'when new variables is a hash with symbol keys' do @@ -37,7 +37,7 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do { key2: 'value22', key3: 'value3' } end - it { is_expected.to eq(result) } + it { is_expected.to match_array(result) } end context 'when new variables is nil' do @@ -47,69 +47,58 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do { key: 'key2', value: 'value2' }] end - it { is_expected.to eq(result) } + it { is_expected.to match_array(result) } end end - describe '.transform_to_yaml_variables' do - let(:variables) do - { 'key1' => 'value1', 'key2' => 'value2' } - end + describe '.transform_to_array' do + subject { described_class.transform_to_array(variables) } - let(:result) do - [{ key: 'key1', value: 'value1' }, - { key: 'key2', value: 'value2' }] - end - - subject { described_class.transform_to_yaml_variables(variables) } - - it { is_expected.to eq(result) } - - context 'when variables is nil' do - let(:variables) {} - - it { is_expected.to eq([]) } - end - end + context 'when values are strings' do + let(:variables) do + { 'key1' => 'value1', 'key2' => 'value2' } + end - describe '.transform_from_yaml_variables' do - let(:variables) do - [{ key: 'key1', value: 'value1' }, - { key: 'key2', value: 'value2' }] - end + let(:result) do + [{ key: 'key1', value: 'value1' }, + { key: 'key2', value: 'value2' }] + end - let(:result) do - { 'key1' => 'value1', 'key2' => 'value2' } + it { is_expected.to match_array(result) } end - subject { described_class.transform_from_yaml_variables(variables) } - - it { is_expected.to eq(result) } - context 'when variables is nil' do let(:variables) {} - it { is_expected.to eq({}) } + it { is_expected.to match_array([]) } end - context 'when variables is a hash' do + context 'when values are hashes' do let(:variables) do - { key1: 'value1', 'key2' => 'value2' } + { 'key1' => { value: 'value1', description: 'var 1' }, 'key2' => { value: 'value2' } } end - it { is_expected.to eq(result) } - end - - context 'when variables contain integers and symbols' do - let(:variables) do - { key1: 1, key2: :value2 } + let(:result) do + [{ key: 'key1', value: 'value1', description: 'var 1' }, + { key: 'key2', value: 'value2' }] end - let(:result1) do - { 'key1' => '1', 'key2' => 'value2' } - end + it { is_expected.to match_array(result) } + + context 'when a value data has `key` as a key' do + let(:variables) do + { 'key1' => { value: 'value1', key: 'new_key1' }, 'key2' => { value: 'value2' } } + end - it { is_expected.to eq(result1) } + let(:result) do + [{ key: 'key1', value: 'value1' }, + { key: 'key2', value: 'value2' }] + end + + it 'ignores the key set with "key"' do + is_expected.to match_array(result) + end + end end end @@ -134,7 +123,7 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do subject { described_class.inherit_yaml_variables(from: from, to: to, inheritance: inheritance) } - it { is_expected.to eq(result) } + it { is_expected.to match_array(result) } context 'when inheritance is false' do let(:inheritance) { false } @@ -144,7 +133,7 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do { key: 'key3', value: 'value3' }] end - it { is_expected.to eq(result) } + it { is_expected.to match_array(result) } end context 'when inheritance is array' do @@ -155,7 +144,7 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do { key: 'key3', value: 'value3' }] end - it { is_expected.to eq(result) } + it { is_expected.to match_array(result) } end end end diff --git a/spec/lib/gitlab_edition_spec.rb b/spec/lib/gitlab_edition_spec.rb index 0ee63279984..46be1471896 100644 --- a/spec/lib/gitlab_edition_spec.rb +++ b/spec/lib/gitlab_edition_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'fast_spec_helper' +require 'rspec-parameterized' RSpec.describe GitlabEdition do def remove_instance_variable(ivar) @@ -27,7 +28,57 @@ RSpec.describe GitlabEdition do end end - describe 'extensions' do + describe '.path_glob' do + using RSpec::Parameterized::TableSyntax + + let(:root) { described_class.root.to_s } + + subject { described_class.path_glob(path) } + + before do + allow(described_class).to receive(:jh?).and_return(jh) + allow(described_class).to receive(:ee?).and_return(ee) + end + + where(:ee, :jh, :path, :expected) do + false | false | nil | '' + true | false | nil | '{,ee/}' + true | true | nil | '{,ee/,jh/}' + false | true | nil | '{,ee/,jh/}' + false | false | 'app/models' | 'app/models' + true | false | 'app/models' | '{,ee/}app/models' + true | true | 'app/models' | '{,ee/,jh/}app/models' + false | true | 'app/models' | '{,ee/,jh/}app/models' + end + + with_them do + it { is_expected.to eq("#{root}/#{expected}") } + end + end + + describe '.extension_path_prefixes' do + using RSpec::Parameterized::TableSyntax + + subject { described_class.extension_path_prefixes } + + before do + allow(described_class).to receive(:jh?).and_return(jh) + allow(described_class).to receive(:ee?).and_return(ee) + end + + where(:ee, :jh, :expected) do + false | false | '' + true | false | '{,ee/}' + true | true | '{,ee/,jh/}' + false | true | '{,ee/,jh/}' + end + + with_them do + it { is_expected.to eq(expected) } + end + end + + describe '.extensions' do context 'when .jh? is true' do before do allow(described_class).to receive(:jh?).and_return(true) diff --git a/spec/mailers/previews_spec.rb b/spec/mailers/previews_spec.rb new file mode 100644 index 00000000000..14bd56e5d40 --- /dev/null +++ b/spec/mailers/previews_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Mailer previews' do + # Setup needed for email previews + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, :repository, :import_failed, group: group, import_last_error: 'some error') } + let_it_be(:user) { create(:user) } + let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + let_it_be(:merge_request) { create(:merge_request, source_project: project) } + let_it_be(:milestone) { create(:milestone, project: project) } + let_it_be(:issue) { create(:issue, project: project, milestone: milestone) } + let_it_be(:remote_mirror) { create(:remote_mirror, project: project) } + let_it_be(:member) { create(:project_member, :maintainer, project: project, created_by: user) } + + Gitlab.ee do + let_it_be(:epic) { create(:epic, group: group) } + end + + let(:expected_kind) { [Mail::Message, ActionMailer::MessageDelivery] } + + let(:pending_failures) do + { + 'NotifyPreview#note_merge_request_email_for_diff_discussion' => + 'https://gitlab.com/gitlab-org/gitlab/-/issues/372885' + } + end + + subject { preview.call(email) } + + where(:preview, :email) do + ActionMailer::Preview.all.flat_map { |preview| preview.emails.map { |email| [preview, email] } } + end + + with_them do + it do + issue_link = pending_failures["#{preview.name}##{email}"] + pending "See #{issue_link}" if issue_link + + is_expected.to be_kind_of(Mail::Message).or(be_kind_of(ActionMailer::MessageDelivery)) + end + end +end diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb index f31edb747bd..1962d232f3d 100644 --- a/spec/models/ci/job_artifact_spec.rb +++ b/spec/models/ci/job_artifact_spec.rb @@ -8,6 +8,8 @@ RSpec.describe Ci::JobArtifact do describe "Associations" do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:job) } + it { is_expected.to validate_presence_of(:job) } + it { is_expected.to validate_presence_of(:partition_id) } end it { is_expected.to respond_to(:file) } @@ -758,4 +760,26 @@ RSpec.describe Ci::JobArtifact do let!(:model) { create(:ci_job_artifact, project: parent) } end end + + context 'with partition_id' do + let(:job) { build(:ci_build, partition_id: 123) } + let(:artifact) { build(:ci_job_artifact, job: job, partition_id: nil) } + + it 'copies the partition_id from job' do + expect { artifact.valid? }.to change(artifact, :partition_id).from(nil).to(123) + end + + context 'when the job is missing' do + let(:artifact) do + build(:ci_job_artifact, + project: build_stubbed(:project), + job: nil, + partition_id: nil) + end + + it 'does not change the partition_id value' do + expect { artifact.valid? }.not_to change(artifact, :partition_id) + end + end + end end diff --git a/spec/services/ci/job_artifacts/create_service_spec.rb b/spec/services/ci/job_artifacts/create_service_spec.rb index 6809b694b22..a2259f9813b 100644 --- a/spec/services/ci/job_artifacts/create_service_spec.rb +++ b/spec/services/ci/job_artifacts/create_service_spec.rb @@ -181,6 +181,18 @@ RSpec.describe Ci::JobArtifacts::CreateService do end end + context 'with job partitioning' do + let(:job) { create(:ci_build, project: project, partition_id: 123) } + + it 'sets partition_id on artifacts' do + expect { subject }.to change { Ci::JobArtifact.count } + + artifacts_partitions = job.job_artifacts.map(&:partition_id).uniq + + expect(artifacts_partitions).to eq([123]) + end + end + shared_examples 'rescues object storage error' do |klass, message, expected_message| it "handles #{klass}" do allow_next_instance_of(JobArtifactUploader) do |uploader| |