diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-03 09:09:11 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-03 09:09:11 +0300 |
commit | e0a415ccb7a7e59c7a6c16841bdd1668d2ef0be5 (patch) | |
tree | 7a2019bcbc2bc1cee665f3ebbcd824b16d2680ce /spec | |
parent | f7dd4c23f3a8c61d54f4589c7835e882c889f462 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r-- | spec/factories/projects.rb | 4 | ||||
-rw-r--r-- | spec/frontend/custom_emoji/components/form_spec.js | 116 | ||||
-rw-r--r-- | spec/frontend/custom_emoji/mock_data.js | 16 | ||||
-rw-r--r-- | spec/models/namespaces/project_namespace_spec.rb | 77 | ||||
-rw-r--r-- | spec/models/project_spec.rb | 6 | ||||
-rw-r--r-- | spec/services/projects/create_service_spec.rb | 4 |
6 files changed, 218 insertions, 5 deletions
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index bd207bc9a64..fdf60ca71f8 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -100,6 +100,10 @@ FactoryBot.define do project.set_runners_token(evaluator.runners_token) if evaluator.runners_token.present? end + to_create do |project| + project.project_namespace.save! if project.valid? + end + after(:create) do |project, evaluator| # Normally the class Projects::CreateService is used for creating # projects, and this class takes care of making sure the owner and current diff --git a/spec/frontend/custom_emoji/components/form_spec.js b/spec/frontend/custom_emoji/components/form_spec.js new file mode 100644 index 00000000000..c5010d93da4 --- /dev/null +++ b/spec/frontend/custom_emoji/components/form_spec.js @@ -0,0 +1,116 @@ +import Vue, { nextTick } from 'vue'; +import { GlAlert } from '@gitlab/ui'; +import VueApollo from 'vue-apollo'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import Form from '~/custom_emoji/components/form.vue'; +import createCustomEmojiMutation from '~/custom_emoji/queries/create_custom_emoji.mutation.graphql'; +import { CREATED_CUSTOM_EMOJI, CREATED_CUSTOM_EMOJI_WITH_ERROR } from '../mock_data'; + +let wrapper; +let createCustomEmojiResponseSpy; + +function createMockApolloProvider(response) { + Vue.use(VueApollo); + + createCustomEmojiResponseSpy = jest.fn().mockResolvedValue(response); + + const requestHandlers = [[createCustomEmojiMutation, createCustomEmojiResponseSpy]]; + + return createMockApollo(requestHandlers); +} + +function createComponent(response = CREATED_CUSTOM_EMOJI) { + const mockApollo = createMockApolloProvider(response); + + return mountExtended(Form, { + provide: { + groupPath: 'gitlab-org', + }, + apolloProvider: mockApollo, + }); +} + +const findCustomEmojiNameInput = () => wrapper.findByTestId('custom-emoji-name-input'); +const findCustomEmojiNameFormGroup = () => wrapper.findByTestId('custom-emoji-name-form-group'); +const findCustomEmojiUrlInput = () => wrapper.findByTestId('custom-emoji-url-input'); +const findCustomEmojiUrlFormGroup = () => wrapper.findByTestId('custom-emoji-url-form-group'); +const findCustomEmojiFrom = () => wrapper.findByTestId('custom-emoji-form'); +const findAlerts = () => wrapper.findAllComponents(GlAlert); +const findSubmitBtn = () => wrapper.findByTestId('custom-emoji-form-submit-btn'); + +function completeForm() { + findCustomEmojiNameInput().setValue('Test'); + findCustomEmojiUrlInput().setValue('https://example.com'); + findCustomEmojiFrom().trigger('submit'); +} + +describe('Custom emoji form component', () => { + describe('creates custom emoji', () => { + it('calls apollo mutation', async () => { + wrapper = createComponent(); + + completeForm(); + + await waitForPromises(); + + expect(createCustomEmojiResponseSpy).toHaveBeenCalledWith({ + groupPath: 'gitlab-org', + url: 'https://example.com', + name: 'Test', + }); + }); + + it('does not submit when form validation fails', async () => { + wrapper = createComponent(); + + findCustomEmojiFrom().trigger('submit'); + + await waitForPromises(); + + expect(createCustomEmojiResponseSpy).not.toHaveBeenCalled(); + }); + + it.each` + findFormGroup | findInput | fieldName + ${findCustomEmojiNameFormGroup} | ${findCustomEmojiUrlInput} | ${'name'} + ${findCustomEmojiUrlFormGroup} | ${findCustomEmojiNameInput} | ${'URL'} + `('shows errors for empty $fieldName input', async ({ findFormGroup, findInput }) => { + wrapper = createComponent(CREATED_CUSTOM_EMOJI_WITH_ERROR); + + findInput().setValue('Test'); + findCustomEmojiFrom().trigger('submit'); + + await waitForPromises(); + + expect(findFormGroup().classes('is-invalid')).toBe(true); + }); + + it('displays errors when mutation fails', async () => { + wrapper = createComponent(CREATED_CUSTOM_EMOJI_WITH_ERROR); + + completeForm(); + + await waitForPromises(); + + const alertMessages = findAlerts().wrappers.map((x) => x.text()); + + expect(alertMessages).toEqual(CREATED_CUSTOM_EMOJI_WITH_ERROR.data.createCustomEmoji.errors); + }); + + it('shows loading state when saving', async () => { + wrapper = createComponent(); + + completeForm(); + + await nextTick(); + + expect(findSubmitBtn().props('loading')).toBe(true); + + await waitForPromises(); + + expect(findSubmitBtn().props('loading')).toBe(false); + }); + }); +}); diff --git a/spec/frontend/custom_emoji/mock_data.js b/spec/frontend/custom_emoji/mock_data.js index 19ba7022bf1..9936274d71c 100644 --- a/spec/frontend/custom_emoji/mock_data.js +++ b/spec/frontend/custom_emoji/mock_data.js @@ -6,3 +6,19 @@ export const CUSTOM_EMOJI = [ createdAt: 'created-at', }, ]; + +export const CREATED_CUSTOM_EMOJI = { + data: { + createCustomEmoji: { + errors: [], + }, + }, +}; + +export const CREATED_CUSTOM_EMOJI_WITH_ERROR = { + data: { + createCustomEmoji: { + errors: ['Test error'], + }, + }, +}; diff --git a/spec/models/namespaces/project_namespace_spec.rb b/spec/models/namespaces/project_namespace_spec.rb index 78403db7fa8..c635d6e54e7 100644 --- a/spec/models/namespaces/project_namespace_spec.rb +++ b/spec/models/namespaces/project_namespace_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Namespaces::ProjectNamespace, type: :model do describe 'relationships' do - it { is_expected.to have_one(:project).with_foreign_key(:project_namespace_id).inverse_of(:project_namespace) } + it { is_expected.to have_one(:project).inverse_of(:project_namespace) } specify do project = create(:project) @@ -32,4 +32,79 @@ RSpec.describe Namespaces::ProjectNamespace, type: :model do expect { project.reload }.to raise_error(ActiveRecord::RecordNotFound) end end + + describe '.create_from_project!' do + context 'when namespace does not exist' do + it 'new project_namespace is not saved' do + expect_any_instance_of(described_class) do |instance| + expect(instance).not_to receive(:save!) + end + + project = Project.new(namespace: nil) + described_class.create_from_project!(project) + end + end + + context 'for new record when namespace exists' do + let(:project) { build(:project) } + let(:project_namespace) { project.project_namespace } + + it 'syncs the project attributes to project namespace' do + project_name = 'project 1 name' + project.name = project_name + + described_class.create_from_project!(project) + expect(project.project_namespace.name).to eq(project_name) + end + + context 'when project has an unsaved project namespace' do + it 'saves the same project namespace' do + described_class.create_from_project!(project) + + expect(project_namespace).to be_persisted + end + end + end + end + + describe '#sync_attributes_from_project' do + context 'with existing project' do + let(:project) { create(:project) } + let(:project_namespace) { project.project_namespace } + let(:project_new_namespace) { create(:namespace) } + let(:project_new_path) { 'project-new-path' } + let(:project_new_name) { project_new_path.titleize } + let(:project_new_visibility_level) { Gitlab::VisibilityLevel::INTERNAL } + let(:project_shared_runners_enabled) { !project.shared_runners_enabled } + + before do + project.name = project_new_name + project.path = project_new_path + project.visibility_level = project_new_visibility_level + project.namespace = project_new_namespace + project.shared_runners_enabled = project_shared_runners_enabled + end + + it 'syncs the relevant keys from the project' do + project_namespace.sync_attributes_from_project(project) + + expect(project_namespace.name).to eq(project_new_name) + expect(project_namespace.path).to eq(project_new_path) + expect(project_namespace.visibility_level).to eq(project_new_visibility_level) + expect(project_namespace.namespace).to eq(project_new_namespace) + expect(project_namespace.namespace_id).to eq(project_new_namespace.id) + expect(project_namespace.shared_runners_enabled).to eq(project_shared_runners_enabled) + end + end + + it 'syncs visibility_level if project is new' do + project = build(:project) + project_namespace = project.project_namespace + project_namespace.visibility_level = Gitlab::VisibilityLevel::PUBLIC + + project_namespace.sync_attributes_from_project(project) + + expect(project_namespace.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) + end + end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 82cefc1a95c..d71ae75aefb 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -19,7 +19,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr describe 'associations' do it { is_expected.to belong_to(:group) } it { is_expected.to belong_to(:namespace) } - it { is_expected.to belong_to(:project_namespace).class_name('Namespaces::ProjectNamespace').with_foreign_key('project_namespace_id') } + it { is_expected.to belong_to(:project_namespace).class_name('Namespaces::ProjectNamespace').with_foreign_key('project_namespace_id').inverse_of(:project) } it { is_expected.to belong_to(:creator).class_name('User') } it { is_expected.to belong_to(:pool_repository) } it { is_expected.to have_many(:users) } @@ -634,8 +634,8 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr end it 'validates the visibility' do - expect_any_instance_of(described_class).to receive(:visibility_level_allowed_as_fork).and_call_original - expect_any_instance_of(described_class).to receive(:visibility_level_allowed_by_group).and_call_original + expect_any_instance_of(described_class).to receive(:visibility_level_allowed_as_fork).twice.and_call_original + expect_any_instance_of(described_class).to receive(:visibility_level_allowed_by_group).twice.and_call_original create(:project) end diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 8a737e4df56..683e438eb08 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -628,11 +628,13 @@ RSpec.describe Projects::CreateService, '#execute', feature_category: :groups_an context 'repository creation' do it 'synchronously creates the repository' do expect_next_instance_of(Project) do |instance| - expect(instance).to receive(:create_repository) + expect(instance).to receive(:create_repository).and_return(true) end project = create_project(user, opts) + expect(project).to be_valid + expect(project).to be_persisted expect(project.owner).to eq(user) expect(project.namespace).to eq(user.namespace) expect(project.project_namespace).to be_in_sync_with_project(project) |