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>2024-01-16 15:08:54 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2024-01-16 15:08:54 +0300
commit218585fc850159e0cf7fa4b609f0837cb5f29599 (patch)
treeb4210dff575984a1a5f7aa6328355dc499c62b93 /spec
parent65d9a877b3487bdcb75985dbcbe8bcf76280591f (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/fixtures/config/redis_config_with_extra_config_command.yml4
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_actions_cell_spec.js199
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_table_spec.js45
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/fixtures.js7
-rw-r--r--spec/frontend/import_entities/import_groups/utils_spec.js21
-rw-r--r--spec/frontend/import_entities/mock_data.js1
-rw-r--r--spec/frontend/read_more_spec.js47
-rw-r--r--spec/models/ci/catalog/resource_spec.rb5
-rw-r--r--spec/support/helpers/user_with_namespace_shim.yml4
-rw-r--r--spec/support/shared_examples/redis/redis_shared_examples.rb36
10 files changed, 267 insertions, 102 deletions
diff --git a/spec/fixtures/config/redis_config_with_extra_config_command.yml b/spec/fixtures/config/redis_config_with_extra_config_command.yml
new file mode 100644
index 00000000000..186ce9e4936
--- /dev/null
+++ b/spec/fixtures/config/redis_config_with_extra_config_command.yml
@@ -0,0 +1,4 @@
+development:
+ config_command: '/opt/redis-config.sh'
+ url: 'redis://redis.example.com'
+ password: 'dummy-password'
diff --git a/spec/frontend/import_entities/import_groups/components/import_actions_cell_spec.js b/spec/frontend/import_entities/import_groups/components/import_actions_cell_spec.js
index 87bee6afd62..9cdcc1cf67b 100644
--- a/spec/frontend/import_entities/import_groups/components/import_actions_cell_spec.js
+++ b/spec/frontend/import_entities/import_groups/components/import_actions_cell_spec.js
@@ -1,124 +1,147 @@
-import {
- GlDisclosureDropdown,
- GlDisclosureDropdownItem,
- GlButtonGroup,
- GlButton,
- GlIcon,
-} from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { GlDisclosureDropdown, GlDisclosureDropdownItem, GlButton } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+
import ImportActionsCell from '~/import_entities/import_groups/components/import_actions_cell.vue';
describe('import actions cell', () => {
let wrapper;
+ const defaultProps = {
+ isFinished: false,
+ isAvailableForImport: false,
+ isInvalid: false,
+ isProjectCreationAllowed: true,
+ };
+
const createComponent = (props) => {
- wrapper = shallowMount(ImportActionsCell, {
+ wrapper = shallowMountExtended(ImportActionsCell, {
propsData: {
- isFinished: false,
- isAvailableForImport: false,
- isInvalid: false,
+ ...defaultProps,
...props,
},
stubs: {
- GlButtonGroup,
GlDisclosureDropdown,
GlDisclosureDropdownItem,
},
});
};
- describe('when group is available for import', () => {
- beforeEach(() => {
- createComponent({ isAvailableForImport: true });
- });
-
- it('renders import dropdown', () => {
- const button = wrapper.findComponent(GlButton);
- expect(button.exists()).toBe(true);
- expect(button.text()).toBe('Import with projects');
- });
+ const findButton = () => wrapper.findComponent(GlButton);
+ const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
+ const findDropdownItem = () => findDropdown().findComponent(GlDisclosureDropdownItem);
+ const findReimportInfoIcon = () => wrapper.findByTestId('reimport-info-icon');
+ const findProjectCreationWarningIcon = () =>
+ wrapper.findByTestId('project-creation-warning-icon');
- it('does not render icon with a hint', () => {
- expect(wrapper.findComponent(GlIcon).exists()).toBe(false);
- });
- });
-
- describe('when group is finished', () => {
- beforeEach(() => {
- createComponent({ isAvailableForImport: false, isFinished: true });
- });
-
- it('renders re-import dropdown', () => {
- const button = wrapper.findComponent(GlButton);
- expect(button.exists()).toBe(true);
- expect(button.text()).toBe('Re-import with projects');
- });
-
- it('renders icon with a hint', () => {
- const icon = wrapper.findComponent(GlIcon);
- expect(icon.exists()).toBe(true);
- expect(icon.attributes().title).toBe(
- 'Re-import creates a new group. It does not sync with the existing group.',
- );
- });
- });
-
- it('does not render import dropdown when group is not available for import', () => {
- createComponent({ isAvailableForImport: false });
+ describe.each`
+ isProjectCreationAllowed | isAvailableForImport | isFinished | expectedButton | expectedDropdown | expectedWarningIcon
+ ${true} | ${false} | ${false} | ${false} | ${false} | ${false}
+ ${true} | ${false} | ${true} | ${'Re-import with projects'} | ${'Re-import without projects'} | ${false}
+ ${true} | ${true} | ${false} | ${'Import with projects'} | ${'Import without projects'} | ${false}
+ ${true} | ${true} | ${true} | ${'Re-import with projects'} | ${'Re-import without projects'} | ${false}
+ ${false} | ${false} | ${false} | ${false} | ${false} | ${false}
+ ${false} | ${false} | ${true} | ${'Re-import without projects'} | ${false} | ${true}
+ ${false} | ${true} | ${false} | ${'Import without projects'} | ${false} | ${true}
+ ${false} | ${true} | ${true} | ${'Re-import without projects'} | ${false} | ${true}
+ `(
+ 'isProjectCreationAllowed = $isProjectCreationAllowed, isAvailableForImport = $isAvailableForImport, isFinished = $isFinished',
+ ({
+ isAvailableForImport,
+ isFinished,
+ isProjectCreationAllowed,
+ expectedButton,
+ expectedDropdown,
+ expectedWarningIcon,
+ }) => {
+ beforeEach(() => {
+ createComponent({ isAvailableForImport, isFinished, isProjectCreationAllowed });
+ });
- const dropdown = wrapper.findComponent(GlDisclosureDropdown);
- expect(dropdown.exists()).toBe(false);
- });
+ if (expectedButton) {
+ it(`renders button with "${expectedButton}" text`, () => {
+ const button = findButton();
+ expect(button.exists()).toBe(true);
+ expect(button.text()).toBe(expectedButton);
+ });
+ } else {
+ it('does not render button', () => {
+ expect(findButton().exists()).toBe(false);
+ });
+ }
+
+ if (expectedDropdown) {
+ it(`renders dropdown with "${expectedDropdown}" text`, () => {
+ expect(findDropdown().exists()).toBe(true);
+ expect(findDropdownItem().text()).toBe(expectedDropdown);
+ });
+ } else {
+ it('does not render dropdown', () => {
+ expect(findDropdown().exists()).toBe(false);
+ });
+ }
+
+ if (isFinished) {
+ it('renders re-import info icon with a hint', () => {
+ const icon = findReimportInfoIcon();
+ expect(icon.exists()).toBe(true);
+ expect(icon.attributes()).toMatchObject({
+ name: 'information-o',
+ title: 'Re-import creates a new group. It does not sync with the existing group.',
+ });
+ });
+ } else {
+ it('does not render re-import info icon', () => {
+ expect(findReimportInfoIcon().exists()).toBe(false);
+ });
+ }
+
+ if (expectedWarningIcon) {
+ it('renders project creation warning icon', () => {
+ const icon = findProjectCreationWarningIcon();
+ expect(icon.exists()).toBe(true);
+ expect(icon.attributes('name')).toBe('warning');
+ });
+ } else {
+ it('does not render project creation warning icon', () => {
+ expect(findProjectCreationWarningIcon().exists()).toBe(false);
+ });
+ }
+ },
+ );
it('renders import dropdown as disabled when group is invalid', () => {
createComponent({ isInvalid: true, isAvailableForImport: true });
- const dropdown = wrapper.findComponent(GlDisclosureDropdown);
- expect(dropdown.props().disabled).toBe(true);
+ expect(findDropdown().props().disabled).toBe(true);
});
- it('emits import-group event when import button is clicked', () => {
+ it('emits import-group event (with projects) when import button is clicked', () => {
createComponent({ isAvailableForImport: true });
- const button = wrapper.findComponent(GlButton);
- button.vm.$emit('click');
+ findButton().vm.$emit('click');
expect(wrapper.emitted('import-group')).toHaveLength(1);
+ expect(wrapper.emitted('import-group')[0]).toStrictEqual([{ migrateProjects: true }]);
});
- describe.each`
- isFinished | expectedAction
- ${false} | ${'Import'}
- ${true} | ${'Re-import'}
- `(
- 'group is available for import and finish status is $isFinished',
- ({ isFinished, expectedAction }) => {
- beforeEach(() => {
- createComponent({ isAvailableForImport: true, isFinished });
- });
+ it('emits import-group event (without projects) when dropdown option is clicked', () => {
+ createComponent({ isAvailableForImport: true });
- it('render import dropdown', () => {
- const button = wrapper.findComponent(GlButton);
- const dropdown = wrapper.findComponent(GlDisclosureDropdown);
- expect(button.element).toHaveText(`${expectedAction} with projects`);
- expect(dropdown.findComponent(GlDisclosureDropdownItem).text()).toBe(
- `${expectedAction} without projects`,
- );
- });
+ findDropdownItem().vm.$emit('action');
- it('request migrate projects by default', () => {
- const button = wrapper.findComponent(GlButton);
- button.vm.$emit('click');
+ expect(wrapper.emitted('import-group')).toHaveLength(1);
+ expect(wrapper.emitted('import-group')[0]).toStrictEqual([{ migrateProjects: false }]);
+ });
- expect(wrapper.emitted('import-group')[0]).toStrictEqual([{ migrateProjects: true }]);
- });
+ it('emits import-group event (without projects) when isProjectCreationAllowed is false and import button is clicked', () => {
+ createComponent({
+ isProjectCreationAllowed: false,
+ isAvailableForImport: true,
+ });
- it('request not to migrate projects via dropdown option', () => {
- const dropdown = wrapper.findComponent(GlDisclosureDropdown);
- dropdown.findComponent(GlDisclosureDropdownItem).vm.$emit('action');
+ findButton().vm.$emit('click');
- expect(wrapper.emitted('import-group')[0]).toStrictEqual([{ migrateProjects: false }]);
- });
- },
- );
+ expect(wrapper.emitted('import-group')).toHaveLength(1);
+ expect(wrapper.emitted('import-group')[0]).toStrictEqual([{ migrateProjects: false }]);
+ });
});
diff --git a/spec/frontend/import_entities/import_groups/components/import_table_spec.js b/spec/frontend/import_entities/import_groups/components/import_table_spec.js
index 84f149b4dd5..08f38f448b9 100644
--- a/spec/frontend/import_entities/import_groups/components/import_table_spec.js
+++ b/spec/frontend/import_entities/import_groups/components/import_table_spec.js
@@ -68,6 +68,7 @@ describe('import table', () => {
const findSelectionCount = () => wrapper.find('[data-test-id="selection-count"]');
const findNewPathCol = () => wrapper.find('[data-test-id="new-path-col"]');
const findUnavailableFeaturesWarning = () => wrapper.findByTestId('unavailable-features-alert');
+ const findImportProjectsWarning = () => wrapper.findByTestId('import-projects-warning');
const findAllImportStatuses = () => wrapper.findAllComponents(ImportStatus);
const triggerSelectAllCheckbox = (checked = true) =>
@@ -628,6 +629,50 @@ describe('import table', () => {
expect(findImportSelectedDropdown().props().disabled).toBe(false);
});
+ it('does not render import projects warning when target with isProjectCreationAllowed = true is selected', async () => {
+ createComponent({
+ bulkImportSourceGroups: () => ({
+ nodes: FAKE_GROUPS,
+ pageInfo: FAKE_PAGE_INFO,
+ versionValidation: FAKE_VERSION_VALIDATION,
+ }),
+ });
+ await waitForPromises();
+
+ await selectRow(0);
+ await nextTick();
+
+ expect(findImportProjectsWarning().exists()).toBe(false);
+ });
+
+ it('renders import projects warning when target with isProjectCreationAllowed = false is selected', async () => {
+ createComponent({
+ bulkImportSourceGroups: () => ({
+ nodes: [
+ {
+ ...generateFakeEntry({ id: 1, status: STATUSES.NONE }),
+ lastImportTarget: {
+ id: 1,
+ targetNamespace: AVAILABLE_NAMESPACES[2].fullPath,
+ newName: 'does-not-matter',
+ },
+ },
+ ],
+ pageInfo: FAKE_PAGE_INFO,
+ versionValidation: FAKE_VERSION_VALIDATION,
+ }),
+ });
+ await waitForPromises();
+
+ await selectRow(0);
+ await nextTick();
+
+ expect(findImportProjectsWarning().props('name')).toBe('warning');
+ expect(findImportProjectsWarning().attributes('title')).toBe(
+ 'Some groups will be imported without projects.',
+ );
+ });
+
it('does not allow selecting already started groups', async () => {
const NEW_GROUPS = [generateFakeEntry({ id: 1, status: STATUSES.STARTED })];
diff --git a/spec/frontend/import_entities/import_groups/graphql/fixtures.js b/spec/frontend/import_entities/import_groups/graphql/fixtures.js
index edc2d1a2381..14889519993 100644
--- a/spec/frontend/import_entities/import_groups/graphql/fixtures.js
+++ b/spec/frontend/import_entities/import_groups/graphql/fixtures.js
@@ -60,10 +60,11 @@ export const statusEndpointFixture = {
},
};
-const makeGroupMock = ({ id, fullPath }) => ({
+const makeGroupMock = ({ id, fullPath, projectCreationLevel = null }) => ({
id,
fullPath,
name: fullPath,
+ projectCreationLevel: projectCreationLevel || 'maintainer',
visibility: 'public',
webUrl: `http://gdk.test:3000/groups/${fullPath}`,
__typename: 'Group',
@@ -72,8 +73,8 @@ const makeGroupMock = ({ id, fullPath }) => ({
export const AVAILABLE_NAMESPACES = [
makeGroupMock({ id: 24, fullPath: 'Commit451' }),
makeGroupMock({ id: 22, fullPath: 'gitlab-org' }),
- makeGroupMock({ id: 23, fullPath: 'gnuwget' }),
- makeGroupMock({ id: 25, fullPath: 'jashkenas' }),
+ makeGroupMock({ id: 23, fullPath: 'gnuwget', projectCreationLevel: 'noone' }),
+ makeGroupMock({ id: 25, fullPath: 'jashkenas', projectCreationLevel: 'developer' }),
];
export const availableNamespacesFixture = {
diff --git a/spec/frontend/import_entities/import_groups/utils_spec.js b/spec/frontend/import_entities/import_groups/utils_spec.js
index 3db57170ed3..d663ed9594f 100644
--- a/spec/frontend/import_entities/import_groups/utils_spec.js
+++ b/spec/frontend/import_entities/import_groups/utils_spec.js
@@ -1,5 +1,9 @@
import { STATUSES } from '~/import_entities/constants';
-import { isFinished, isAvailableForImport } from '~/import_entities/import_groups/utils';
+import {
+ isFinished,
+ isAvailableForImport,
+ isProjectCreationAllowed,
+} from '~/import_entities/import_groups/utils';
const FINISHED_STATUSES = [STATUSES.FINISHED, STATUSES.FAILED, STATUSES.TIMEOUT];
const OTHER_STATUSES = Object.values(STATUSES).filter(
@@ -53,4 +57,19 @@ describe('Direct transfer status utils', () => {
expect(isFinished({ progress: { status: 'weird' } })).toBe(false);
});
});
+
+ describe('isProjectCreationAllowed', () => {
+ it.each`
+ projectCreationLevel | expected
+ ${null} | ${false}
+ ${'noone'} | ${false}
+ ${'developer'} | ${true}
+ ${'maintainer'} | ${true}
+ `(
+ 'when projectCreationLevel is $projectCreationLevel, returns $expected',
+ ({ projectCreationLevel, expected }) => {
+ expect(isProjectCreationAllowed({ projectCreationLevel })).toBe(expected);
+ },
+ );
+ });
});
diff --git a/spec/frontend/import_entities/mock_data.js b/spec/frontend/import_entities/mock_data.js
index 9208f99651f..de44824002b 100644
--- a/spec/frontend/import_entities/mock_data.js
+++ b/spec/frontend/import_entities/mock_data.js
@@ -2,6 +2,7 @@ const mockGroupFactory = (fullPath) => ({
id: `gid://gitlab/Group/${fullPath}`,
fullPath,
name: fullPath,
+ projectCreationLevel: 'maintainer',
visibility: 'public',
webUrl: `http://gdk.test:3000/groups/${fullPath}`,
__typename: 'Group',
diff --git a/spec/frontend/read_more_spec.js b/spec/frontend/read_more_spec.js
index 9b25c56f193..6b1acfef8f5 100644
--- a/spec/frontend/read_more_spec.js
+++ b/spec/frontend/read_more_spec.js
@@ -2,6 +2,7 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import initReadMore from '~/read_more';
describe('Read more click-to-expand functionality', () => {
+ const findTarget = () => document.querySelector('.read-more-container');
const findTrigger = () => document.querySelector('.js-read-more-trigger');
afterEach(() => {
@@ -19,13 +20,11 @@ describe('Read more click-to-expand functionality', () => {
});
it('adds "is-expanded" class to target element', () => {
- const target = document.querySelector('.read-more-container');
- const trigger = findTrigger();
initReadMore();
- trigger.click();
+ findTrigger().click();
- expect(target.classList.contains('is-expanded')).toEqual(true);
+ expect(findTarget().classList.contains('is-expanded')).toEqual(true);
});
});
@@ -38,8 +37,7 @@ describe('Read more click-to-expand functionality', () => {
</button>
`);
- const trigger = findTrigger();
- const nestedElement = trigger.firstElementChild;
+ const nestedElement = findTrigger().firstElementChild;
initReadMore();
nestedElement.click();
@@ -49,4 +47,41 @@ describe('Read more click-to-expand functionality', () => {
expect(findTrigger()).toBe(null);
});
});
+
+ describe('data-read-more-height defines when to show the read-more button', () => {
+ afterEach(() => {
+ resetHTMLFixture();
+ });
+
+ it('if not set shows button all the time', () => {
+ setHTMLFixture(`
+ <div class="read-more-container">
+ <p class="read-more-content">Occaecat voluptate exercitation aliqua et duis eiusmod mollit esse ea laborum amet consectetur officia culpa anim. Fugiat laboris eu irure deserunt excepteur laboris irure quis. Occaecat nostrud irure do officia ea laborum velit sunt. Aliqua incididunt non deserunt proident magna aliqua sunt laborum laborum eiusmod ullamco. Et elit commodo irure. Labore eu nisi proident.</p>
+ <button type="button" class="js-read-more-trigger">
+ Button text
+ </button>
+ </div>
+ `);
+
+ initReadMore();
+
+ expect(findTrigger()).not.toBe(null);
+ });
+
+ it('if set hides button as threshold is met', () => {
+ setHTMLFixture(`
+ <div class="read-more-container" data-read-more-height="120">
+ <p class="read-more-content read-more-content--has-scrim">Occaecat voluptate exercitation aliqua et duis eiusmod mollit esse ea laborum amet consectetur officia culpa anim. Fugiat laboris eu irure deserunt excepteur laboris irure quis. Occaecat nostrud irure do officia ea laborum velit sunt. Aliqua incididunt non deserunt proident magna aliqua sunt laborum laborum eiusmod ullamco. Et elit commodo irure. Labore eu nisi proident.</p>
+ <button type="button" class="js-read-more-trigger">
+ Button text
+ </button>
+ </div>
+ `);
+
+ initReadMore();
+
+ expect(findTarget().classList.contains('read-more-content--has-scrim')).toBe(false);
+ expect(findTrigger()).toBe(null);
+ });
+ });
});
diff --git a/spec/models/ci/catalog/resource_spec.rb b/spec/models/ci/catalog/resource_spec.rb
index 15d8b4f440b..0faf43e9ee3 100644
--- a/spec/models/ci/catalog/resource_spec.rb
+++ b/spec/models/ci/catalog/resource_spec.rb
@@ -41,6 +41,11 @@ RSpec.describe Ci::Catalog::Resource, feature_category: :pipeline_composition do
it { is_expected.to define_enum_for(:state).with_values({ draft: 0, published: 1 }) }
+ it do
+ is_expected.to define_enum_for(:verification_level)
+ .with_values({ unverified: 0, gitlab: 1 })
+ end
+
describe '.for_projects' do
it 'returns catalog resources for the given project IDs' do
resources_for_projects = described_class.for_projects(project_a.id)
diff --git a/spec/support/helpers/user_with_namespace_shim.yml b/spec/support/helpers/user_with_namespace_shim.yml
index 7b3dc099cf9..7e7a25aa023 100644
--- a/spec/support/helpers/user_with_namespace_shim.yml
+++ b/spec/support/helpers/user_with_namespace_shim.yml
@@ -32,8 +32,6 @@
- ee/spec/features/groups/analytics/productivity_analytics_spec.rb
- ee/spec/features/groups/member_roles_spec.rb
- ee/spec/features/groups/members/list_members_spec.rb
-- ee/spec/features/groups/security/policies_list_spec.rb
-- ee/spec/features/groups/security/policy_editor_spec.rb
- ee/spec/features/groups/usage_quotas/code_suggestions_usage_tab_spec.rb
- ee/spec/features/groups/wikis_spec.rb
- ee/spec/features/incidents/incident_details_spec.rb
@@ -89,8 +87,6 @@
- ee/spec/features/projects/new_project_spec.rb
- ee/spec/features/projects/path_locks_spec.rb
- ee/spec/features/projects/push_rules_spec.rb
-- ee/spec/features/projects/security/policies_list_spec.rb
-- ee/spec/features/projects/security/policy_editor_spec.rb
- ee/spec/features/projects/settings/ee/repository_mirrors_settings_spec.rb
- ee/spec/features/projects/settings/merge_requests/user_manages_merge_requests_template_spec.rb
- ee/spec/features/projects/settings/packages_spec.rb
diff --git a/spec/support/shared_examples/redis/redis_shared_examples.rb b/spec/support/shared_examples/redis/redis_shared_examples.rb
index 1f7834a4d7c..b9179261f87 100644
--- a/spec/support/shared_examples/redis/redis_shared_examples.rb
+++ b/spec/support/shared_examples/redis/redis_shared_examples.rb
@@ -161,6 +161,42 @@ RSpec.shared_examples "redis_shared_examples" do
expect(params2).not_to have_key(:foo)
end
+ context 'with command to generate extra config specified' do
+ let(:config_file_name) { 'spec/fixtures/config/redis_config_with_extra_config_command.yml' }
+
+ context 'when the command returns valid yaml' do
+ before do
+ allow(Gitlab::Popen).to receive(:popen).and_return(["password: 'actual-password'\n", 0])
+ end
+
+ it 'merges config from command on top of config from file' do
+ is_expected.to include(password: 'actual-password')
+ end
+ end
+
+ context 'when the command returns invalid yaml' do
+ before do
+ allow(Gitlab::Popen).to receive(:popen).and_return(["password: 'actual-password\n", 0])
+ end
+
+ it 'raises error' do
+ expect { subject }.to raise_error(Gitlab::Redis::Wrapper::CommandExecutionError,
+ %r{Redis: Execution of `/opt/redis-config.sh` generated invalid yaml})
+ end
+ end
+
+ context 'when the command fails' do
+ before do
+ allow(Gitlab::Popen).to receive(:popen).and_return(["", 125])
+ end
+
+ it 'raises error' do
+ expect { subject }.to raise_error(Gitlab::Redis::Wrapper::CommandExecutionError,
+ %r{Redis: Execution of `/opt/redis-config.sh` failed})
+ end
+ end
+ end
+
context 'when url contains unix socket reference' do
context 'with old format' do
let(:config_file_name) { config_old_format_socket }