diff options
Diffstat (limited to 'spec')
33 files changed, 371 insertions, 68 deletions
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb index aa61aff3b05..80bf964e2ee 100644 --- a/spec/features/issues/bulk_assignment_labels_spec.rb +++ b/spec/features/issues/bulk_assignment_labels_spec.rb @@ -295,8 +295,8 @@ RSpec.describe 'Issues > Labels bulk assignment' do before do issue1.milestone = milestone issue2.milestone = milestone - issue1.save - issue2.save + issue1.save! + issue2.save! issue1.labels << bug issue2.labels << feature diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb index ca44978d223..bbf2a889880 100644 --- a/spec/features/issues/issue_sidebar_spec.rb +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -171,7 +171,7 @@ RSpec.describe 'Issue Sidebar' do context 'editing issue labels', :js do before do - issue.update(labels: [label]) + issue.update!(labels: [label]) page.within('.block.labels') do click_on 'Edit' end diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb index bc4c67fdd79..5e02d5ad038 100644 --- a/spec/features/issues/note_polling_spec.rb +++ b/spec/features/issues/note_polling_spec.rb @@ -103,7 +103,7 @@ RSpec.describe 'Issue notes polling', :js do end def update_note(note, new_text) - note.update(note: new_text) + note.update!(note: new_text) wait_for_requests end diff --git a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb index e225a45481d..6e8b3e4fb7c 100644 --- a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb +++ b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb @@ -150,7 +150,7 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do context 'when merge requests are disabled' do before do - project.project_feature.update(merge_requests_access_level: 0) + project.project_feature.update!(merge_requests_access_level: 0) visit project_issue_path(project, issue) end diff --git a/spec/features/issues/user_creates_confidential_merge_request_spec.rb b/spec/features/issues/user_creates_confidential_merge_request_spec.rb index ea96165d7b7..6b4526cd624 100644 --- a/spec/features/issues/user_creates_confidential_merge_request_spec.rb +++ b/spec/features/issues/user_creates_confidential_merge_request_spec.rb @@ -38,7 +38,7 @@ RSpec.describe 'User creates confidential merge request on issue page', :js do let(:forked_project) { fork_project(project, user, repository: true) } before do - forked_project.update(visibility: Gitlab::VisibilityLevel::PRIVATE) + forked_project.update!(visibility: Gitlab::VisibilityLevel::PRIVATE) visit_confidential_issue end diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb index b8e35c9afa3..4b0886ee85e 100644 --- a/spec/features/issues/user_edits_issue_spec.rb +++ b/spec/features/issues/user_edits_issue_spec.rb @@ -78,7 +78,7 @@ RSpec.describe "Issues > User edits issue", :js do end it 'warns about version conflict' do - issue.update(title: "New title") + issue.update!(title: "New title") fill_in 'issue_title', with: 'bug 345' fill_in 'issue_description', with: 'bug description' @@ -307,7 +307,7 @@ RSpec.describe "Issues > User edits issue", :js do before do project.add_guest(guest) issue.milestone = milestone - issue.save + issue.save! end it 'shows milestone text' do diff --git a/spec/features/issues/user_filters_issues_spec.rb b/spec/features/issues/user_filters_issues_spec.rb index 1b246181523..5d05df6aaf0 100644 --- a/spec/features/issues/user_filters_issues_spec.rb +++ b/spec/features/issues/user_filters_issues_spec.rb @@ -18,7 +18,7 @@ RSpec.describe 'User filters issues', :js do @issue = Issue.find_by(title: 'foobar') @issue.milestone = create(:milestone, project: project) @issue.assignees = [] - @issue.save + @issue.save! end let(:issue) { @issue } diff --git a/spec/features/issues/user_sees_live_update_spec.rb b/spec/features/issues/user_sees_live_update_spec.rb index 79c6978cbc0..7e4880f209e 100644 --- a/spec/features/issues/user_sees_live_update_spec.rb +++ b/spec/features/issues/user_sees_live_update_spec.rb @@ -18,7 +18,7 @@ RSpec.describe 'Issues > User sees live update', :js do expect(page).to have_text("new title") - issue.update(title: "updated title") + issue.update!(title: "updated title") wait_for_requests expect(page).to have_text("updated title") diff --git a/spec/features/issues/user_sorts_issues_spec.rb b/spec/features/issues/user_sorts_issues_spec.rb index f0bb055c6f2..c161e1deb83 100644 --- a/spec/features/issues/user_sorts_issues_spec.rb +++ b/spec/features/issues/user_sorts_issues_spec.rb @@ -77,7 +77,7 @@ RSpec.describe "User sorts issues" do it 'sorts by most recently updated', :js do issue3.updated_at = Time.now + 100 - issue3.save + issue3.save! visit project_issues_path(project, sort: sort_value_recently_updated) expect(first_issue).to include('baz') @@ -85,8 +85,8 @@ RSpec.describe "User sorts issues" do describe 'sorting by due date', :js do before do - issue1.update(due_date: 1.day.from_now) - issue2.update(due_date: 6.days.from_now) + issue1.update!(due_date: 1.day.from_now) + issue2.update!(due_date: 6.days.from_now) end it 'sorts by due date' do @@ -96,7 +96,7 @@ RSpec.describe "User sorts issues" do end it 'sorts by due date by excluding nil due dates' do - issue2.update(due_date: nil) + issue2.update!(due_date: nil) visit project_issues_path(project, sort: sort_value_due_date) @@ -111,7 +111,7 @@ RSpec.describe "User sorts issues" do end it 'sorts by least recently due date by excluding nil due dates' do - issue2.update(due_date: nil) + issue2.update!(due_date: nil) visit project_issues_path(project, label_names: [label.name], sort: sort_value_due_date_later) @@ -122,8 +122,8 @@ RSpec.describe "User sorts issues" do describe 'filtering by due date', :js do before do - issue1.update(due_date: 1.day.from_now) - issue2.update(due_date: 6.days.from_now) + issue1.update!(due_date: 1.day.from_now) + issue2.update!(due_date: 6.days.from_now) end it 'filters by none' do @@ -147,9 +147,9 @@ RSpec.describe "User sorts issues" do end it 'filters by due this week' do - issue1.update(due_date: Date.today.beginning_of_week + 2.days) - issue2.update(due_date: Date.today.end_of_week) - issue3.update(due_date: Date.today - 8.days) + issue1.update!(due_date: Date.today.beginning_of_week + 2.days) + issue2.update!(due_date: Date.today.end_of_week) + issue3.update!(due_date: Date.today - 8.days) visit project_issues_path(project, due_date: Issue::DueThisWeek.name) @@ -161,9 +161,9 @@ RSpec.describe "User sorts issues" do end it 'filters by due this month' do - issue1.update(due_date: Date.today.beginning_of_month + 2.days) - issue2.update(due_date: Date.today.end_of_month) - issue3.update(due_date: Date.today - 50.days) + issue1.update!(due_date: Date.today.beginning_of_month + 2.days) + issue2.update!(due_date: Date.today.end_of_month) + issue3.update!(due_date: Date.today - 50.days) visit project_issues_path(project, due_date: Issue::DueThisMonth.name) @@ -175,9 +175,9 @@ RSpec.describe "User sorts issues" do end it 'filters by overdue' do - issue1.update(due_date: Date.today + 2.days) - issue2.update(due_date: Date.today + 20.days) - issue3.update(due_date: Date.yesterday) + issue1.update!(due_date: Date.today + 2.days) + issue2.update!(due_date: Date.today + 20.days) + issue3.update!(due_date: Date.yesterday) visit project_issues_path(project, due_date: Issue::Overdue.name) @@ -189,9 +189,9 @@ RSpec.describe "User sorts issues" do end it 'filters by due next month and previous two weeks' do - issue1.update(due_date: Date.today - 4.weeks) - issue2.update(due_date: (Date.today + 2.months).beginning_of_month) - issue3.update(due_date: Date.yesterday) + issue1.update!(due_date: Date.today - 4.weeks) + issue2.update!(due_date: (Date.today + 2.months).beginning_of_month) + issue3.update!(due_date: Date.yesterday) visit project_issues_path(project, due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name) @@ -206,9 +206,9 @@ RSpec.describe "User sorts issues" do describe 'sorting by milestone', :js do before do issue1.milestone = newer_due_milestone - issue1.save + issue1.save! issue2.milestone = later_due_milestone - issue2.save + issue2.save! end it 'sorts by milestone' do @@ -224,9 +224,9 @@ RSpec.describe "User sorts issues" do before do issue1.assignees << user2 - issue1.save + issue1.save! issue2.assignees << user2 - issue2.save + issue2.save! end it 'sorts with a filter applied' do diff --git a/spec/frontend/__helpers__/vue_test_utils_helper.js b/spec/frontend/__helpers__/vue_test_utils_helper.js index d6132ef84ac..a94cee84f74 100644 --- a/spec/frontend/__helpers__/vue_test_utils_helper.js +++ b/spec/frontend/__helpers__/vue_test_utils_helper.js @@ -1,4 +1,6 @@ -import { isArray } from 'lodash'; +import * as testingLibrary from '@testing-library/dom'; +import { createWrapper, WrapperArray, mount, shallowMount } from '@vue/test-utils'; +import { isArray, upperFirst } from 'lodash'; const vNodeContainsText = (vnode, text) => (vnode.text && vnode.text.includes(text)) || @@ -37,6 +39,17 @@ export const waitForMutation = (store, expectedMutationType) => }); export const extendedWrapper = (wrapper) => { + // https://testing-library.com/docs/queries/about + const AVAILABLE_QUERIES = [ + 'byRole', + 'byLabelText', + 'byPlaceholderText', + 'byText', + 'byDisplayValue', + 'byAltText', + 'byTitle', + ]; + if (isArray(wrapper) || !wrapper?.find) { // eslint-disable-next-line no-console console.warn( @@ -56,5 +69,63 @@ export const extendedWrapper = (wrapper) => { return this.findAll(`[data-testid="${id}"]`); }, }, + // `findBy` + ...AVAILABLE_QUERIES.reduce((accumulator, query) => { + return { + ...accumulator, + [`find${upperFirst(query)}`]: { + value(text, options = {}) { + const elements = testingLibrary[`queryAll${upperFirst(query)}`]( + wrapper.element, + text, + options, + ); + + // Return VTU `ErrorWrapper` if element is not found + // https://github.com/vuejs/vue-test-utils/blob/dev/packages/test-utils/src/error-wrapper.js + // VTU does not expose `ErrorWrapper` so, as of now, this is the best way to + // create an `ErrorWrapper` + if (!elements.length) { + const emptyElement = document.createElement('div'); + + return createWrapper(emptyElement).find('testing-library-element-not-found'); + } + + return createWrapper(elements[0], this.options || {}); + }, + }, + }; + }, {}), + // `findAllBy` + ...AVAILABLE_QUERIES.reduce((accumulator, query) => { + return { + ...accumulator, + [`findAll${upperFirst(query)}`]: { + value(text, options = {}) { + const elements = testingLibrary[`queryAll${upperFirst(query)}`]( + wrapper.element, + text, + options, + ); + + const wrappers = elements.map((element) => { + const elementWrapper = createWrapper(element, this.options || {}); + elementWrapper.selector = text; + + return elementWrapper; + }); + + const wrapperArray = new WrapperArray(wrappers); + wrapperArray.selector = text; + + return wrapperArray; + }, + }, + }; + }, {}), }); }; + +export const shallowMountExtended = (...args) => extendedWrapper(shallowMount(...args)); + +export const mountExtended = (...args) => extendedWrapper(mount(...args)); diff --git a/spec/frontend/__helpers__/vue_test_utils_helper_spec.js b/spec/frontend/__helpers__/vue_test_utils_helper_spec.js index d4f8e36c169..dfe5a483223 100644 --- a/spec/frontend/__helpers__/vue_test_utils_helper_spec.js +++ b/spec/frontend/__helpers__/vue_test_utils_helper_spec.js @@ -1,7 +1,27 @@ -import { shallowMount } from '@vue/test-utils'; -import { extendedWrapper, shallowWrapperContainsSlotText } from './vue_test_utils_helper'; +import * as testingLibrary from '@testing-library/dom'; +import * as vtu from '@vue/test-utils'; +import { + shallowMount, + Wrapper as VTUWrapper, + WrapperArray as VTUWrapperArray, +} from '@vue/test-utils'; +import { + extendedWrapper, + shallowMountExtended, + mountExtended, + shallowWrapperContainsSlotText, +} from './vue_test_utils_helper'; + +jest.mock('@testing-library/dom', () => ({ + __esModule: true, + ...jest.requireActual('@testing-library/dom'), +})); describe('Vue test utils helpers', () => { + afterAll(() => { + jest.unmock('@testing-library/dom'); + }); + describe('shallowWrapperContainsSlotText', () => { const mockText = 'text'; const mockSlot = `<div>${mockText}</div>`; @@ -84,7 +104,7 @@ describe('Vue test utils helpers', () => { ); }); - it('should find the component by test id', () => { + it('should find the element by test id', () => { expect(mockComponent.findByTestId(testId).exists()).toBe(true); }); }); @@ -105,5 +125,187 @@ describe('Vue test utils helpers', () => { expect(mockComponent.findAllByTestId(testId)).toHaveLength(2); }); }); + + describe.each` + findMethod | expectedQuery + ${'findByRole'} | ${'queryAllByRole'} + ${'findByLabelText'} | ${'queryAllByLabelText'} + ${'findByPlaceholderText'} | ${'queryAllByPlaceholderText'} + ${'findByText'} | ${'queryAllByText'} + ${'findByDisplayValue'} | ${'queryAllByDisplayValue'} + ${'findByAltText'} | ${'queryAllByAltText'} + `('$findMethod', ({ findMethod, expectedQuery }) => { + const text = 'foo bar'; + const options = { selector: 'div' }; + const mockDiv = document.createElement('div'); + + let wrapper; + beforeEach(() => { + wrapper = extendedWrapper( + shallowMount({ + template: `<div>foo bar</div>`, + }), + ); + }); + + it(`calls Testing Library \`${expectedQuery}\` function with correct parameters`, () => { + jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => [mockDiv]); + + wrapper[findMethod](text, options); + + expect(testingLibrary[expectedQuery]).toHaveBeenLastCalledWith( + wrapper.element, + text, + options, + ); + }); + + describe('when element is found', () => { + beforeEach(() => { + jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => [mockDiv]); + jest.spyOn(vtu, 'createWrapper'); + }); + + it('returns a VTU wrapper', () => { + const result = wrapper[findMethod](text, options); + + expect(vtu.createWrapper).toHaveBeenCalledWith(mockDiv, wrapper.options); + expect(result).toBeInstanceOf(VTUWrapper); + }); + }); + + describe('when multiple elements are found', () => { + beforeEach(() => { + const mockSpan = document.createElement('span'); + jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => [mockDiv, mockSpan]); + jest.spyOn(vtu, 'createWrapper'); + }); + + it('returns the first element as a VTU wrapper', () => { + const result = wrapper[findMethod](text, options); + + expect(vtu.createWrapper).toHaveBeenCalledWith(mockDiv, wrapper.options); + expect(result).toBeInstanceOf(VTUWrapper); + }); + }); + + describe('when element is not found', () => { + beforeEach(() => { + jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => []); + }); + + it('returns a VTU error wrapper', () => { + expect(wrapper[findMethod](text, options).exists()).toBe(false); + }); + }); + }); + + describe.each` + findMethod | expectedQuery + ${'findAllByRole'} | ${'queryAllByRole'} + ${'findAllByLabelText'} | ${'queryAllByLabelText'} + ${'findAllByPlaceholderText'} | ${'queryAllByPlaceholderText'} + ${'findAllByText'} | ${'queryAllByText'} + ${'findAllByDisplayValue'} | ${'queryAllByDisplayValue'} + ${'findAllByAltText'} | ${'queryAllByAltText'} + `('$findMethod', ({ findMethod, expectedQuery }) => { + const text = 'foo bar'; + const options = { selector: 'div' }; + const mockElements = [ + document.createElement('li'), + document.createElement('li'), + document.createElement('li'), + ]; + + let wrapper; + beforeEach(() => { + wrapper = extendedWrapper( + shallowMount({ + template: ` + <ul> + <li>foo</li> + <li>bar</li> + <li>baz</li> + </ul> + `, + }), + ); + }); + + it(`calls Testing Library \`${expectedQuery}\` function with correct parameters`, () => { + jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => mockElements); + + wrapper[findMethod](text, options); + + expect(testingLibrary[expectedQuery]).toHaveBeenLastCalledWith( + wrapper.element, + text, + options, + ); + }); + + describe('when elements are found', () => { + beforeEach(() => { + jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => mockElements); + }); + + it('returns a VTU wrapper array', () => { + const result = wrapper[findMethod](text, options); + + expect(result).toBeInstanceOf(VTUWrapperArray); + expect( + result.wrappers.every( + (resultWrapper) => + resultWrapper instanceof VTUWrapper && resultWrapper.options === wrapper.options, + ), + ).toBe(true); + expect(result.length).toBe(3); + }); + }); + + describe('when elements are not found', () => { + beforeEach(() => { + jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => []); + }); + + it('returns an empty VTU wrapper array', () => { + const result = wrapper[findMethod](text, options); + + expect(result).toBeInstanceOf(VTUWrapperArray); + expect(result.length).toBe(0); + }); + }); + }); + }); + + describe.each` + mountExtendedFunction | expectedMountFunction + ${shallowMountExtended} | ${'shallowMount'} + ${mountExtended} | ${'mount'} + `('$mountExtendedFunction', ({ mountExtendedFunction, expectedMountFunction }) => { + const FakeComponent = jest.fn(); + const options = { + propsData: { + foo: 'bar', + }, + }; + + beforeEach(() => { + const mockWrapper = { find: jest.fn() }; + jest.spyOn(vtu, expectedMountFunction).mockImplementation(() => mockWrapper); + }); + + it(`calls \`${expectedMountFunction}\` with passed arguments`, () => { + mountExtendedFunction(FakeComponent, options); + + expect(vtu[expectedMountFunction]).toHaveBeenCalledWith(FakeComponent, options); + }); + + it('returns extended wrapper', () => { + const result = mountExtendedFunction(FakeComponent, options); + + expect(result).toHaveProperty('find'); + expect(result).toHaveProperty('findByTestId'); + }); }); }); diff --git a/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js b/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js index f86237dc160..f1471f625f8 100644 --- a/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js +++ b/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js @@ -42,6 +42,7 @@ describe('AccessRequestActionButtons', () => { memberId: member.id, title: 'Deny access', isAccessRequest: true, + isInvite: false, icon: 'close', }); }); diff --git a/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js b/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js index 55866f90baa..e7a99a96da6 100644 --- a/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js +++ b/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js @@ -43,6 +43,7 @@ describe('InviteActionButtons', () => { message: `Are you sure you want to revoke the invitation for ${member.invite.email} to join "${member.source.fullName}"`, title: 'Revoke invite', isAccessRequest: false, + isInvite: true, icon: 'remove', }); }); diff --git a/spec/frontend/members/components/action_buttons/remove_member_button_spec.js b/spec/frontend/members/components/action_buttons/remove_member_button_spec.js index 0d66f343fda..952ce679a2f 100644 --- a/spec/frontend/members/components/action_buttons/remove_member_button_spec.js +++ b/spec/frontend/members/components/action_buttons/remove_member_button_spec.js @@ -28,6 +28,7 @@ describe('RemoveMemberButton', () => { message: 'Are you sure you want to remove John Smith?', title: 'Remove member', isAccessRequest: true, + isInvite: true, ...propsData, }, directives: { @@ -48,6 +49,7 @@ describe('RemoveMemberButton', () => { 'data-member-type': 'GroupMember', 'data-message': 'Are you sure you want to remove John Smith?', 'data-is-access-request': 'true', + 'data-is-invite': 'true', 'aria-label': 'Remove member', title: 'Remove member', icon: 'remove', diff --git a/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js b/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js index f43779b8970..2f13c436fd9 100644 --- a/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js +++ b/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js @@ -43,6 +43,7 @@ describe('UserActionButtons', () => { message: `Are you sure you want to remove ${member.user.name} from "${member.source.fullName}"`, title: 'Remove member', isAccessRequest: false, + isInvite: false, icon: 'remove', }); }); diff --git a/spec/frontend/vue_shared/components/remove_member_modal_spec.js b/spec/frontend/vue_shared/components/remove_member_modal_spec.js index 1ef3cc348bd..7a04de13615 100644 --- a/spec/frontend/vue_shared/components/remove_member_modal_spec.js +++ b/spec/frontend/vue_shared/components/remove_member_modal_spec.js @@ -15,16 +15,18 @@ describe('RemoveMemberModal', () => { }); describe.each` - state | memberType | isAccessRequest | actionText | removeSubMembershipsCheckboxExpected | unassignIssuablesCheckboxExpected | message - ${'removing a group member'} | ${'GroupMember'} | ${'false'} | ${'Remove member'} | ${true} | ${true} | ${'Are you sure you want to remove Jane Doe from the Gitlab Org / Gitlab Test project?'} - ${'removing a project member'} | ${'ProjectMember'} | ${'false'} | ${'Remove member'} | ${false} | ${true} | ${'Are you sure you want to remove Jane Doe from the Gitlab Org / Gitlab Test project?'} - ${'denying an access request'} | ${'ProjectMember'} | ${'true'} | ${'Deny access request'} | ${false} | ${false} | ${"Are you sure you want to deny Jane Doe's request to join the Gitlab Org / Gitlab Test project?"} + state | memberType | isAccessRequest | isInvite | actionText | removeSubMembershipsCheckboxExpected | unassignIssuablesCheckboxExpected | message + ${'removing a group member'} | ${'GroupMember'} | ${'false'} | ${'false'} | ${'Remove member'} | ${true} | ${true} | ${'Are you sure you want to remove Jane Doe from the Gitlab Org / Gitlab Test project?'} + ${'removing a project member'} | ${'ProjectMember'} | ${'false'} | ${'false'} | ${'Remove member'} | ${false} | ${true} | ${'Are you sure you want to remove Jane Doe from the Gitlab Org / Gitlab Test project?'} + ${'denying an access request'} | ${'ProjectMember'} | ${'true'} | ${'false'} | ${'Deny access request'} | ${false} | ${false} | ${"Are you sure you want to deny Jane Doe's request to join the Gitlab Org / Gitlab Test project?"} + ${'revoking invite'} | ${'ProjectMember'} | ${'false'} | ${'true'} | ${'Revoke invite'} | ${false} | ${false} | ${'Are you sure you want to revoke the invitation for foo@bar.com to join the Gitlab Org / Gitlab Test project?'} `( 'when $state', ({ actionText, memberType, isAccessRequest, + isInvite, message, removeSubMembershipsCheckboxExpected, unassignIssuablesCheckboxExpected, @@ -35,6 +37,7 @@ describe('RemoveMemberModal', () => { return { modalData: { isAccessRequest, + isInvite, message, memberPath, memberType, diff --git a/spec/graphql/resolvers/ci/jobs_resolver_spec.rb b/spec/graphql/resolvers/ci/jobs_resolver_spec.rb index c44f6b623d7..1b69bf7f63a 100644 --- a/spec/graphql/resolvers/ci/jobs_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/jobs_resolver_spec.rb @@ -13,6 +13,7 @@ RSpec.describe Resolvers::Ci::JobsResolver do create(:ci_build, :sast, name: 'DAST job', pipeline: pipeline) create(:ci_build, :dast, name: 'SAST job', pipeline: pipeline) create(:ci_build, :container_scanning, name: 'Container scanning job', pipeline: pipeline) + create(:ci_build, name: 'Job with tags', pipeline: pipeline, tag_list: ['review']) end describe '#resolve' do @@ -24,7 +25,8 @@ RSpec.describe Resolvers::Ci::JobsResolver do have_attributes(name: 'Normal job'), have_attributes(name: 'DAST job'), have_attributes(name: 'SAST job'), - have_attributes(name: 'Container scanning job') + have_attributes(name: 'Container scanning job'), + have_attributes(name: 'Job with tags') ) end end @@ -43,5 +45,18 @@ RSpec.describe Resolvers::Ci::JobsResolver do ) end end + + context 'when a job has tags' do + it "returns jobs with tags when applicable" do + jobs = resolve(described_class, obj: pipeline) + expect(jobs).to contain_exactly( + have_attributes(tag_list: []), + have_attributes(tag_list: []), + have_attributes(tag_list: []), + have_attributes(tag_list: []), + have_attributes(tag_list: ['review']) + ) + end + end end end diff --git a/spec/graphql/types/ci/job_type_spec.rb b/spec/graphql/types/ci/job_type_spec.rb index 75a178bc8f6..787e2174070 100644 --- a/spec/graphql/types/ci/job_type_spec.rb +++ b/spec/graphql/types/ci/job_type_spec.rb @@ -33,6 +33,7 @@ RSpec.describe Types::Ci::JobType do stage started_at status + tags ] expect(described_class).to have_graphql_fields(*expected_fields) diff --git a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb index a8fcb714c64..993afd47a57 100644 --- a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb +++ b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb @@ -26,6 +26,7 @@ RSpec.describe BatchDestroyDependentAssociations do let_it_be(:project) { create(:project) } let_it_be(:build) { create(:ci_build, project: project) } let_it_be(:notification_setting) { create(:notification_setting, project: project) } + let!(:todos) { create(:todo, project: project) } it 'destroys multiple builds' do diff --git a/spec/models/concerns/featurable_spec.rb b/spec/models/concerns/featurable_spec.rb index b550d22f686..bcff5ce383e 100644 --- a/spec/models/concerns/featurable_spec.rb +++ b/spec/models/concerns/featurable_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe Featurable do let_it_be(:user) { create(:user) } + let(:project) { create(:project) } let(:feature_class) { subject.class } let(:features) { feature_class::FEATURES } diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb index 10fa15d468f..ac125e81acd 100644 --- a/spec/requests/api/commit_statuses_spec.rb +++ b/spec/requests/api/commit_statuses_spec.rb @@ -14,8 +14,8 @@ RSpec.describe API::CommitStatuses do let(:get_url) { "/projects/#{project.id}/repository/commits/#{sha}/statuses" } context 'ci commit exists' do - let!(:master) { project.ci_pipelines.create(source: :push, sha: commit.id, ref: 'master', protected: false) } - let!(:develop) { project.ci_pipelines.create(source: :push, sha: commit.id, ref: 'develop', protected: false) } + let!(:master) { project.ci_pipelines.create!(source: :push, sha: commit.id, ref: 'master', protected: false) } + let!(:develop) { project.ci_pipelines.create!(source: :push, sha: commit.id, ref: 'develop', protected: false) } context "reporter user" do let(:statuses_id) { json_response.map { |status| status['id'] } } @@ -270,8 +270,8 @@ RSpec.describe API::CommitStatuses do end context 'when a pipeline id is specified' do - let!(:first_pipeline) { project.ci_pipelines.create(source: :push, sha: commit.id, ref: 'master', status: 'created') } - let!(:other_pipeline) { project.ci_pipelines.create(source: :push, sha: commit.id, ref: 'master', status: 'created') } + let!(:first_pipeline) { project.ci_pipelines.create!(source: :push, sha: commit.id, ref: 'master', status: 'created') } + let!(:other_pipeline) { project.ci_pipelines.create!(source: :push, sha: commit.id, ref: 'master', status: 'created') } subject do post api(post_url, developer), params: { diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb index d6a0423e83c..c89c59a2151 100644 --- a/spec/requests/api/deployments_spec.rb +++ b/spec/requests/api/deployments_spec.rb @@ -345,7 +345,7 @@ RSpec.describe API::Deployments do context 'as a maintainer' do it 'returns a 403 when updating a deployment with a build' do - deploy.update(deployable: build) + deploy.update!(deployable: build) put( api("/projects/#{project.id}/deployments/#{deploy.id}", user), @@ -394,7 +394,7 @@ RSpec.describe API::Deployments do end it 'returns a 403 when updating a deployment with a build' do - deploy.update(deployable: build) + deploy.update!(deployable: build) put( api("/projects/#{project.id}/deployments/#{deploy.id}", developer), diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb index 303e510883d..aa1a4643593 100644 --- a/spec/requests/api/environments_spec.rb +++ b/spec/requests/api/environments_spec.rb @@ -214,7 +214,7 @@ RSpec.describe API::Environments do context 'as a maintainer' do context 'with a stoppable environment' do before do - environment.update(state: :available) + environment.update!(state: :available) post api("/projects/#{project.id}/environments/#{environment.id}/stop", user) end diff --git a/spec/requests/api/go_proxy_spec.rb b/spec/requests/api/go_proxy_spec.rb index d45e24241b2..e678b6cf1c8 100644 --- a/spec/requests/api/go_proxy_spec.rb +++ b/spec/requests/api/go_proxy_spec.rb @@ -363,7 +363,7 @@ RSpec.describe API::GoProxy do let(:module_name) { base } before do - project.update(visibility_level: Gitlab::VisibilityLevel::PRIVATE) + project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) end describe 'GET /projects/:id/packages/go/*module_name/@v/list' do @@ -412,7 +412,7 @@ RSpec.describe API::GoProxy do let(:module_name) { base } before do - project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) end describe 'GET /projects/:id/packages/go/*module_name/@v/list' do diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb index 34d347c76fd..0d0cc66c52a 100644 --- a/spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb +++ b/spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb @@ -52,7 +52,7 @@ RSpec.describe 'Setting labels of a merge request' do end it 'sets the merge request labels, removing existing ones' do - merge_request.update(labels: [label2]) + merge_request.update!(labels: [label2]) post_graphql_mutation(mutation, current_user: current_user) diff --git a/spec/requests/api/group_import_spec.rb b/spec/requests/api/group_import_spec.rb index bb7436502ed..f632e49bf3a 100644 --- a/spec/requests/api/group_import_spec.rb +++ b/spec/requests/api/group_import_spec.rb @@ -218,12 +218,14 @@ RSpec.describe API::GroupImport do stub_uploads_object_storage(ImportExportUploader, direct_upload: true) end + # rubocop:disable Rails/SaveBang let(:tmp_object) do fog_connection.directories.new(key: 'uploads').files.create( key: "tmp/uploads/#{file_name}", body: file_upload ) end + # rubocop:enable Rails/SaveBang let(:fog_file) { fog_to_uploaded_file(tmp_object) } let(:params) do diff --git a/spec/requests/api/group_milestones_spec.rb b/spec/requests/api/group_milestones_spec.rb index 7d387079b3a..e3e0164e5a7 100644 --- a/spec/requests/api/group_milestones_spec.rb +++ b/spec/requests/api/group_milestones_spec.rb @@ -20,7 +20,7 @@ RSpec.describe API::GroupMilestones do let_it_be(:params) { { include_parent_milestones: true } } before_all do - group.update(parent: ancestor_group) + group.update!(parent: ancestor_group) end shared_examples 'listing all milestones' do @@ -83,9 +83,9 @@ RSpec.describe API::GroupMilestones do end def setup_for_group - context_group.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + context_group.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) context_group.add_developer(user) - public_project.update(namespace: context_group) + public_project.update!(namespace: context_group) context_group.reload end end diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb index 8b060fa2217..6bedd43e5c4 100644 --- a/spec/requests/api/internal/base_spec.rb +++ b/spec/requests/api/internal/base_spec.rb @@ -887,7 +887,7 @@ RSpec.describe API::Internal::Base do context 'project does not exist' do context 'git pull' do it 'returns a 200 response with status: false' do - project.destroy + project.destroy! pull(key, project) diff --git a/spec/requests/api/issues/get_group_issues_spec.rb b/spec/requests/api/issues/get_group_issues_spec.rb index 3870c78deee..cebde747210 100644 --- a/spec/requests/api/issues/get_group_issues_spec.rb +++ b/spec/requests/api/issues/get_group_issues_spec.rb @@ -754,7 +754,7 @@ RSpec.describe API::Issues do let(:parent_group) { create(:group) } before do - group.update(parent_id: parent_group.id) + group.update!(parent_id: parent_group.id) group_closed_issue.reload end diff --git a/spec/requests/api/issues/post_projects_issues_spec.rb b/spec/requests/api/issues/post_projects_issues_spec.rb index 5b3e2363669..7f1db620d4f 100644 --- a/spec/requests/api/issues/post_projects_issues_spec.rb +++ b/spec/requests/api/issues/post_projects_issues_spec.rb @@ -111,7 +111,7 @@ RSpec.describe API::Issues do let(:not_member) { create(:user) } before do - project.project_feature.update(issues_access_level: ProjectFeature::PRIVATE) + project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE) end it 'renders 403' do diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb index fe00b654d3b..4f73917763f 100644 --- a/spec/requests/api/jobs_spec.rb +++ b/spec/requests/api/jobs_spec.rb @@ -215,7 +215,7 @@ RSpec.describe API::Jobs do first_build = create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: pipeline) first_build.runner = create(:ci_runner) first_build.user = create(:user) - first_build.save + first_build.save! control_count = ActiveRecord::QueryRecorder.new { go }.count @@ -223,7 +223,7 @@ RSpec.describe API::Jobs do second_build = create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: second_pipeline) second_build.runner = create(:ci_runner) second_build.user = create(:user) - second_build.save + second_build.save! expect { go }.not_to exceed_query_limit(control_count) end @@ -684,7 +684,7 @@ RSpec.describe API::Jobs do context 'with regular branch' do before do pipeline.reload - pipeline.update(ref: 'master', + pipeline.update!(ref: 'master', sha: project.commit('master').sha) get_for_ref('master') @@ -696,7 +696,7 @@ RSpec.describe API::Jobs do context 'with branch name containing slash' do before do pipeline.reload - pipeline.update(ref: 'improve/awesome', + pipeline.update!(ref: 'improve/awesome', sha: project.commit('improve/awesome').sha) end @@ -732,7 +732,7 @@ RSpec.describe API::Jobs do stub_artifacts_object_storage job.success - project.update(visibility_level: visibility_level, + project.update!(visibility_level: visibility_level, public_builds: public_builds) get_artifact_file(artifact) @@ -826,7 +826,7 @@ RSpec.describe API::Jobs do context 'with branch name containing slash' do before do pipeline.reload - pipeline.update(ref: 'improve/awesome', + pipeline.update!(ref: 'improve/awesome', sha: project.commit('improve/awesome').sha) end diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index e3fffd3e3fd..26377c40b73 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -119,7 +119,7 @@ RSpec.describe API::Labels do expect(label).not_to be_nil - label.priorities.create(project: label.project, priority: 1) + label.priorities.create!(project: label.project, priority: 1) label.save! request_params = { @@ -139,7 +139,7 @@ RSpec.describe API::Labels do expect(label).not_to be_nil label_id = spec_params[:name] || spec_params[:label_id] - label.priorities.create(project: label.project, priority: 1) + label.priorities.create!(project: label.project, priority: 1) label.save! request_params = { @@ -383,7 +383,7 @@ RSpec.describe API::Labels do it 'returns 409 if label already exists in group' do group = create(:group) group_label = create(:group_label, group: group) - project.update(group: group) + project.update!(group: group) post api("/projects/#{project.id}/labels", user), params: { diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index a049d7d7515..f6cdf370e5c 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -235,12 +235,14 @@ RSpec.describe API::ProjectImport do stub_uploads_object_storage(ImportExportUploader, direct_upload: true) end + # rubocop:disable Rails/SaveBang let(:tmp_object) do fog_connection.directories.new(key: 'uploads').files.create( key: "tmp/uploads/#{file_name}", body: fixture_file_upload(file) ) end + # rubocop:enable Rails/SaveBang let(:file_upload) { fog_to_uploaded_file(tmp_object) } @@ -285,7 +287,7 @@ RSpec.describe API::ProjectImport do it 'returns the import status and the error if failed' do project = create(:project, :import_failed) project.add_maintainer(user) - project.import_state.update(last_error: 'error') + project.import_state.update!(last_error: 'error') get api("/projects/#{project.id}/import", user) |