From 26dbe855d9883881c7b004bb74fd6d81670fedda Mon Sep 17 00:00:00 2001 From: Oiza Date: Wed, 4 Jun 2025 18:38:32 -0400 Subject: [PATCH 01/11] Remove default target namespace for external imports We no longer want to suggest to users which namespace (personal or group) to import into when importing a project from GitHub, Gitea, Bitbucket. Changelog: changed --- .../components/provider_repo_table_row.vue | 37 +++++- .../import_projects/store/getters.js | 2 +- app/views/import/_githubish_status.html.haml | 2 +- .../provider_repo_table_row_spec.js | 120 +++++++++++++----- .../import_projects/store/actions_spec.js | 2 +- .../import_projects/store/getters_spec.js | 3 +- 6 files changed, 123 insertions(+), 43 deletions(-) diff --git a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue index ab166b8fba197d..447bed7f9d0300 100644 --- a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue +++ b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue @@ -60,6 +60,8 @@ export default { return { isSelectedForReimport: false, showMembershipsModal: false, + userHasSelectedNamespace: false, + showNamespaceRequiredError: false, }; }, @@ -72,8 +74,13 @@ export default { }, showMembershipsWarning() { - const userNamespaceSelected = this.importTarget.targetNamespace === this.userNamespace; - return (this.isImportNotStarted || this.isSelectedForReimport) && userNamespaceSelected; + const usersNamespaceIsSelected = this.importTarget.targetNamespace === this.userNamespace; + + return this.isNotImporting && usersNamespaceIsSelected; + }, + + isNotImporting() { + return this.isImportNotStarted || this.isSelectedForReimport; }, isFinished() { @@ -128,6 +135,15 @@ export default { this.updateImportTarget({ newName: value }); }, }, + shouldBlockImportForNamespace() { + if (this.importTarget.targetNamespace) { + return false; + } + + return ( + !this.repo.importSource.target && this.isNotImporting && !this.userHasSelectedNamespace + ); + }, }, methods: { @@ -156,7 +172,9 @@ export default { }, onImportClick() { - if (this.showMembershipsWarning) { + if (this.shouldBlockImportForNamespace) { + this.showNamespaceRequiredError = true; + } else if (this.showMembershipsWarning) { this.showMembershipsModal = true; } else { this.handleImportRepo(); @@ -164,6 +182,8 @@ export default { }, onSelect(value) { + this.userHasSelectedNamespace = true; + this.showNamespaceRequiredError = false; this.updateImportTarget({ targetNamespace: value }); }, }, @@ -209,6 +229,7 @@ export default {
@@ -224,6 +245,14 @@ export default { data-testid="project-path-field" />
+ @@ -264,9 +293,11 @@ export default { (repoId) => { return { newName: repo.importSource.sanitizedName, - targetNamespace: state.defaultTargetNamespace, + targetNamespace: null, }; }; diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml index ad68a0587f2fb2..b6d4dc6b9c915b 100644 --- a/app/views/import/_githubish_status.html.haml +++ b/app/views/import/_githubish_status.html.haml @@ -3,7 +3,7 @@ - extra_data = local_assigns.fetch(:extra_data, {}) - filterable = local_assigns.fetch(:filterable, true) - paginatable = local_assigns.fetch(:paginatable, false) -- default_namespace_path = (local_assigns[:default_namespace] || current_user.namespace).full_path +- default_namespace_path = local_assigns[:default_namespace]&.full_path - cancel_path = local_assigns.fetch(:cancel_path, nil) - details_path = local_assigns.fetch(:details_path, nil) - provider_title = Gitlab::ImportSources.title(local_assigns.fetch(:provider)) diff --git a/spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js b/spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js index a135570656693c..9f5c1bca94f13d 100644 --- a/spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js +++ b/spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js @@ -1,4 +1,4 @@ -import { GlBadge, GlButton, GlModal } from '@gitlab/ui'; +import { GlBadge, GlButton } from '@gitlab/ui'; import Vue, { nextTick } from 'vue'; // eslint-disable-next-line no-restricted-imports import Vuex from 'vuex'; @@ -14,14 +14,14 @@ describe('ProviderRepoTableRow', () => { const fetchImport = jest.fn(); const cancelImport = jest.fn(); const setImportTarget = jest.fn(); - const groupImportTarget = { - targetNamespace: 'target', + const defaultImportTarget = { + targetNamespace: null, newName: 'newName', }; const userNamespace = 'root'; - function initStore({ importTarget = groupImportTarget } = {}) { + function initStore({ importTarget = defaultImportTarget } = {}) { const store = new Vuex.Store({ state: {}, getters: { @@ -45,7 +45,8 @@ describe('ProviderRepoTableRow', () => { const findImportStatus = () => wrapper.findComponent(ImportStatus); const findProviderLink = () => wrapper.findByTestId('provider-link'); const findMembershipsWarning = () => wrapper.findByTestId('memberships-warning'); - const findGlModal = () => wrapper.findComponent(GlModal); + const findMembershipsWarningModal = () => wrapper.findByTestId('memberships-warning-modal'); + const findNamespaceRequiredWarning = () => wrapper.findByTestId('namespace-required-warning'); const findCancelButton = () => { const buttons = wrapper @@ -97,12 +98,37 @@ describe('ProviderRepoTableRow', () => { expect(findImportTargetDropdown().exists()).toBe(true); }); + it('renders the dropdown without a default selected', () => { + expect(findImportTargetDropdown().props('selected')).toBe(null); + expect(findImportTargetDropdown().props().toggleText).toBe('Select namespace'); + }); + + describe('when no namespace is selected', () => { + it('shows namespace required warning when import button is clicked', async () => { + findImportButton().vm.$emit('click'); + await nextTick(); + + expect(findNamespaceRequiredWarning().text()).toBe('Select a destination namespace.'); + }); + + it('does not trigger import when clicking import button', async () => { + findImportButton().vm.$emit('click'); + await nextTick(); + + expect(fetchImport).not.toHaveBeenCalled(); + }); + }); + describe('when user namespace is selected as import target', () => { - beforeEach(() => { + beforeEach(async () => { mountComponent( { repo }, { storeOptions: { importTarget: { targetNamespace: userNamespace } } }, ); + + const dropdown = findImportTargetDropdown(); + dropdown.vm.$emit('select', userNamespace); + await nextTick(); }); it('shows memberships warning', () => { @@ -113,7 +139,7 @@ describe('ProviderRepoTableRow', () => { findImportButton().vm.$emit('click'); await nextTick(); - const modal = findGlModal(); + const modal = findMembershipsWarningModal(); expect(modal.props('title')).toBe( 'Are you sure you want to import the project to a personal namespace?', ); @@ -122,11 +148,15 @@ describe('ProviderRepoTableRow', () => { ); }); + it('does not show `missing namespace` warning', () => { + expect(findNamespaceRequiredWarning().exists()).toBe(false); + }); + it('triggers import when clicking modal primary button', async () => { findImportButton().vm.$emit('click'); await nextTick(); - findGlModal().vm.$emit('primary'); + findMembershipsWarningModal().vm.$emit('primary'); expect(fetchImport).toHaveBeenCalledWith(expect.anything(), { repoId: repo.importSource.id, @@ -136,53 +166,68 @@ describe('ProviderRepoTableRow', () => { }); describe('when group namespace is selected as import target', () => { + beforeEach(async () => { + const dropdown = findImportTargetDropdown(); + dropdown.vm.$emit('select', 'target'); + await nextTick(); + }); + it('does not show memberships warning', () => { expect(findMembershipsWarning().isVisible()).toBe(false); }); - it('does not show modal when import button is clicked', async () => { + it('does not show memberships modal when import button is clicked', async () => { findImportButton().vm.$emit('click'); await nextTick(); - expect(findGlModal().exists()).toBe(false); + expect(findMembershipsWarningModal().exists()).toBe(false); }); - }); - it('renders import button', () => { - expect(findImportButton().exists()).toBe(true); - }); + it('does not show `missing namespace` warning when import button is clicked', async () => { + findImportButton().vm.$emit('click'); + await nextTick(); + expect(findNamespaceRequiredWarning().exists()).toBe(false); + }); + + it('renders import button', () => { + expect(findImportButton().exists()).toBe(true); + }); - it('imports repo when clicking import button', async () => { - findImportButton().vm.$emit('click'); + it('imports repo when clicking import button', async () => { + findImportButton().vm.$emit('click'); - await nextTick(); + await nextTick(); - expect(fetchImport).toHaveBeenCalledWith(expect.anything(), { - repoId: repo.importSource.id, - optionalStages: {}, + expect(fetchImport).toHaveBeenCalledWith(expect.anything(), { + repoId: repo.importSource.id, + optionalStages: {}, + }); }); - }); - it('includes optionalStages to import', async () => { - const OPTIONAL_STAGES = { stage1: true, stage2: false }; + it('includes optionalStages to import', async () => { + const OPTIONAL_STAGES = { stage1: true, stage2: false }; - mountComponent({ - repo, - optionalStages: OPTIONAL_STAGES, - }); + mountComponent({ + repo, + optionalStages: OPTIONAL_STAGES, + }); - findImportButton().vm.$emit('click'); + const dropdown = findImportTargetDropdown(); + dropdown.vm.$emit('select', 'target'); + await nextTick(); - await nextTick(); + findImportButton().vm.$emit('click'); + await nextTick(); - expect(fetchImport).toHaveBeenCalledWith(expect.anything(), { - repoId: repo.importSource.id, - optionalStages: OPTIONAL_STAGES, + expect(fetchImport).toHaveBeenCalledWith(expect.anything(), { + repoId: repo.importSource.id, + optionalStages: OPTIONAL_STAGES, + }); }); - }); - it('does not render re-import button', () => { - expect(findReimportButton().exists()).toBe(false); + it('does not render re-import button', () => { + expect(findReimportButton().exists()).toBe(false); + }); }); }); @@ -290,7 +335,12 @@ describe('ProviderRepoTableRow', () => { await nextTick(); + findImportTargetDropdown().vm.$emit('select', 'some-namespace'); + await nextTick(); + findReimportButton().vm.$emit('click'); + await nextTick(); + expect(findNamespaceRequiredWarning().exists()).toBe(false); expect(fetchImport).toHaveBeenCalledWith(expect.anything(), { repoId: repo.importSource.id, diff --git a/spec/frontend/import_entities/import_projects/store/actions_spec.js b/spec/frontend/import_entities/import_projects/store/actions_spec.js index 918821dfa59816..aa57ae9f041371 100644 --- a/spec/frontend/import_entities/import_projects/store/actions_spec.js +++ b/spec/frontend/import_entities/import_projects/store/actions_spec.js @@ -55,7 +55,7 @@ describe('import_projects store actions', () => { let localState; const importRepoId = 1; const otherImportRepoId = 2; - const defaultTargetNamespace = 'default'; + const defaultTargetNamespace = null; const sanitizedName = 'sanitizedName'; const defaultImportTarget = { newName: sanitizedName, targetNamespace: defaultTargetNamespace }; diff --git a/spec/frontend/import_entities/import_projects/store/getters_spec.js b/spec/frontend/import_entities/import_projects/store/getters_spec.js index fced5670f25b3e..55bac7e3f5f966 100644 --- a/spec/frontend/import_entities/import_projects/store/getters_spec.js +++ b/spec/frontend/import_entities/import_projects/store/getters_spec.js @@ -94,12 +94,11 @@ describe('import_projects store getters', () => { describe('getImportTarget', () => { it('returns default value if no custom target available', () => { - localState.defaultTargetNamespace = 'default'; localState.repositories = [IMPORTABLE_REPO]; expect(getImportTarget(localState)(IMPORTABLE_REPO.importSource.id)).toStrictEqual({ newName: IMPORTABLE_REPO.importSource.sanitizedName, - targetNamespace: localState.defaultTargetNamespace, + targetNamespace: null, }); }); -- GitLab From 3b86c5701390e566c0e67ed390f3d51e21867891 Mon Sep 17 00:00:00 2001 From: Oiza Date: Wed, 4 Jun 2025 18:42:14 -0400 Subject: [PATCH 02/11] Translate error message and dropdown default --- locale/gitlab.pot | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index eb2ad77a627bc6..3200ab7a21de95 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -32068,6 +32068,12 @@ msgstr "" msgid "ImportProjects|Requesting your %{provider} repositories failed" msgstr "" +msgid "ImportProjects|Select a destination namespace." +msgstr "" + +msgid "ImportProjects|Select namespace" +msgstr "" + msgid "ImportProjects|Select the repositories you want to import" msgstr "" -- GitLab From d2459d652e8fc140cbbd3ddc3d33abda4703c192 Mon Sep 17 00:00:00 2001 From: Oiza Date: Wed, 4 Jun 2025 18:47:03 -0400 Subject: [PATCH 03/11] Delete expectation in manifest import flow --- spec/features/import/manifest_import_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb index 175ae2fee8c00b..35c25966e878b3 100644 --- a/spec/features/import/manifest_import_spec.rb +++ b/spec/features/import/manifest_import_spec.rb @@ -34,7 +34,6 @@ page.within(second_row) do click_on 'Import' end - click_on 'Continue import' wait_for_requests -- GitLab From 00fd98ab85ca8bf764ae99bbda2f2074e82587c1 Mon Sep 17 00:00:00 2001 From: Oiza Date: Wed, 4 Jun 2025 18:56:45 -0400 Subject: [PATCH 04/11] Add an extra test for import all feature --- spec/features/import/manifest_import_spec.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb index 35c25966e878b3..ed2e8cfbbb2155 100644 --- a/spec/features/import/manifest_import_spec.rb +++ b/spec/features/import/manifest_import_spec.rb @@ -43,6 +43,21 @@ end end + it 'confirms user wishes to import all projects', :sidekiq_inline, :js do + visit new_import_manifest_path + + attach_file('manifest', Rails.root.join('spec/fixtures/aosp_manifest.xml')) + click_on 'List available repositories' + + wait_for_requests + + click_on 'Import 660 repositories' + + wait_for_requests + + expect(page).to have_content 'Are you sure you want to import 660 repositories?' + end + it 'renders an error if the remote url scheme starts with javascript' do visit new_import_manifest_path -- GitLab From a439641af70e99d00e54a2e7a627d46f73de51f9 Mon Sep 17 00:00:00 2001 From: Oiza Date: Fri, 6 Jun 2025 18:39:40 -0400 Subject: [PATCH 05/11] Fix import All flow The target namespace or group is no longer pre-set when the ImportProjectsTable loads. As a result, we need to ensure that users select namespaces before clicking the importAll button. --- .../components/import_projects_table.vue | 53 +++++++++++++++++-- .../components/provider_repo_table_row.vue | 1 + .../components/import_projects_table_spec.js | 42 ++++++++++----- 3 files changed, 80 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue index 9a0c94a7d00087..5c3a7338c568cd 100644 --- a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue +++ b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue @@ -61,6 +61,8 @@ export default { optionalStagesSelection: Object.fromEntries( this.optionalStages.map(({ name, selected }) => [name, selected]), ), + showImportAllModal: false, + showNamespaceRequiredModal: false, }; }, @@ -72,6 +74,7 @@ export default { 'hasImportableRepos', 'hasIncompatibleRepos', 'importAllCount', + 'getImportTarget', ]), pagePaginationStateKey() { @@ -101,6 +104,14 @@ export default { fromHeaderText() { return sprintf(__('From %{providerTitle}'), { providerTitle: this.providerTitle }); }, + + missingNamespaceCount() { + return this.reposMissingTargetNamespace().length; + }, + + isManifestImport() { + return this.providerTitle === `s__('ImportProjects|Manifest file')`; + }, }, mounted() { @@ -123,10 +134,28 @@ export default { 'importAll', ]), - showImportAllModal() { - this.$refs.importAllModal.show(); + showModalHandler() { + if (this.missingNamespaceCount > 0) { + this.showNamespaceRequiredModal = true; + } else { + this.showImportAllModal = true; + } + }, + + reposMissingTargetNamespace() { + if (this.isManifestImport) { + return []; + } + + return this.repositories.filter((repo) => { + if (repo.importSource.target) return false; + + const target = this.getImportTarget(repo.importSource.id); + return !target?.targetNamespace; + }); }, }, + actionPrimary: { text: __('Okay') }, }; @@ -138,14 +167,14 @@ export default { - +
{{ importAllButtonText }} @@ -170,6 +199,7 @@ export default { /> + + {{ + n__( + 'Select a namespace for %d repository before importing all.', + 'Select namespaces for %d repositories before importing all.', + missingNamespaceCount, + ) + }} +
diff --git a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue index 447bed7f9d0300..6de371735351f7 100644 --- a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue +++ b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue @@ -136,6 +136,7 @@ export default { }, }, shouldBlockImportForNamespace() { + // destination repo pre-selected eg. manifest imports if (this.importTarget.targetNamespace) { return false; } diff --git a/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js b/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js index f8a9bb8af9b675..d4024b11d7b4d3 100644 --- a/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js +++ b/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js @@ -1,8 +1,8 @@ import { GlLoadingIcon, GlButton, GlIntersectionObserver, GlSearchBoxByClick } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; import Vue, { nextTick } from 'vue'; // eslint-disable-next-line no-restricted-imports import Vuex from 'vuex'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { STATUSES } from '~/import_entities/constants'; import ImportProjectsTable from '~/import_entities/import_projects/components/import_projects_table.vue'; import ProviderRepoTableRow from '~/import_entities/import_projects/components/provider_repo_table_row.vue'; @@ -13,8 +13,6 @@ import state from '~/import_entities/import_projects/store/state'; describe('ImportProjectsTable', () => { let wrapper; - const USER_NAMESPACE = 'root'; - const findFilterField = () => wrapper .findAllComponents(GlSearchBoxByClick) @@ -33,15 +31,15 @@ describe('ImportProjectsTable', () => { const findImportAllButton = () => wrapper.findAllComponents(GlButton).wrappers.find((w) => w.props('variant') === 'confirm'); const findImportAllModal = () => wrapper.findComponent({ ref: 'importAllModal' }); + const findNamespaceRequiredModal = () => wrapper.findComponent({ ref: 'namespaceRequiredModal' }); const findIntersectionObserver = () => wrapper.findComponent(GlIntersectionObserver); const importAllFn = jest.fn(); - const importAllModalShowFn = jest.fn(); const fetchReposFn = jest.fn(); function createComponent({ state: initialState, - getters: customGetters, + getters: customGetters = {}, slots, filterable, paginatable, @@ -50,7 +48,7 @@ describe('ImportProjectsTable', () => { Vue.use(Vuex); const store = new Vuex.Store({ - state: { ...state(), defaultTargetNamespace: USER_NAMESPACE, ...initialState }, + state: { ...state(), customImportTargets: {}, ...initialState }, getters: { ...getters, ...customGetters, @@ -65,7 +63,7 @@ describe('ImportProjectsTable', () => { }, }); - wrapper = shallowMount(ImportProjectsTable, { + wrapper = shallowMountExtended(ImportProjectsTable, { store, propsData: { providerTitle, @@ -74,9 +72,6 @@ describe('ImportProjectsTable', () => { optionalStages, }, slots, - stubs: { - GlModal: { template: '
Modal!
', methods: { show: importAllModalShowFn } }, - }, }); } @@ -162,13 +157,36 @@ describe('ImportProjectsTable', () => { expect(wrapper.text()).toContain(`No ${providerTitle} repositories found`); }); - it('opens confirmation modal when import all button is clicked', async () => { + it('requires namespace selection when `import all` button is clicked before selection', async () => { createComponent({ state: { repositories: [providerRepo] } }); findImportAllButton().vm.$emit('click'); await nextTick(); - expect(importAllModalShowFn).toHaveBeenCalled(); + const namespaceRequired = findNamespaceRequiredModal(); + expect(namespaceRequired.props('title')).toBe('Namespace required'); + expect(namespaceRequired.props('visible')).toBe(true); + + expect(namespaceRequired.text()).toBe( + 'Select a namespace for 1 repository before importing all.', + ); + }); + + it('opens confirmation modal when `import all` button is clicked after namespace selection', async () => { + const mockGetImportTarget = jest.fn(() => () => ({ + targetNamespace: 'some-namespace', + })); + + createComponent({ + state: { repositories: [providerRepo] }, + getters: { getImportTarget: mockGetImportTarget }, + }); + + findImportAllButton().vm.$emit('click'); + await nextTick(); + + const verifyImport = findImportAllModal(); + expect(verifyImport.props('visible')).toBe(true); }); it('triggers importAll action when modal is confirmed', async () => { -- GitLab From a605d57c07fcea065012ddd0532b4ea39a9a4f21 Mon Sep 17 00:00:00 2001 From: Oiza Date: Fri, 6 Jun 2025 18:43:15 -0400 Subject: [PATCH 06/11] Translate namespace-required copy --- locale/gitlab.pot | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 3200ab7a21de95..ab4588a06e92f7 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -32047,6 +32047,9 @@ msgstr "" msgid "ImportProjects|Importing the project failed: %{reason}" msgstr "" +msgid "ImportProjects|Namespace required" +msgstr "" + msgid "ImportProjects|Organization" msgstr "" @@ -57311,6 +57314,11 @@ msgstr "" msgid "Select a milestone" msgstr "" +msgid "Select a namespace for %d repository before importing all." +msgid_plural "Select namespaces for %d repositories before importing all." +msgstr[0] "" +msgstr[1] "" + msgid "Select a new namespace" msgstr "" -- GitLab From cac00464d6da041883ee9f61f92cbb26b38b6d37 Mon Sep 17 00:00:00 2001 From: Oiza Date: Fri, 6 Jun 2025 19:53:34 -0400 Subject: [PATCH 07/11] Simplify namespace warning copy --- .../import_projects/components/import_projects_table.vue | 6 ++---- locale/gitlab.pot | 8 +++----- .../components/import_projects_table_spec.js | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue index 5c3a7338c568cd..fec33ecbb5e70f 100644 --- a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue +++ b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue @@ -221,10 +221,8 @@ export default { :action-primary="$options.actionPrimary" > {{ - n__( - 'Select a namespace for %d repository before importing all.', - 'Select namespaces for %d repositories before importing all.', - missingNamespaceCount, + s__( + 'ImportProjects|Select a destination namespace for each repository before importing all.', ) }} diff --git a/locale/gitlab.pot b/locale/gitlab.pot index ab4588a06e92f7..cf0d040235b3b0 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -32071,6 +32071,9 @@ msgstr "" msgid "ImportProjects|Requesting your %{provider} repositories failed" msgstr "" +msgid "ImportProjects|Select a destination namespace for each repository before importing all." +msgstr "" + msgid "ImportProjects|Select a destination namespace." msgstr "" @@ -57314,11 +57317,6 @@ msgstr "" msgid "Select a milestone" msgstr "" -msgid "Select a namespace for %d repository before importing all." -msgid_plural "Select namespaces for %d repositories before importing all." -msgstr[0] "" -msgstr[1] "" - msgid "Select a new namespace" msgstr "" diff --git a/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js b/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js index d4024b11d7b4d3..e429e1f7e53f2c 100644 --- a/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js +++ b/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js @@ -168,7 +168,7 @@ describe('ImportProjectsTable', () => { expect(namespaceRequired.props('visible')).toBe(true); expect(namespaceRequired.text()).toBe( - 'Select a namespace for 1 repository before importing all.', + 'Select a destination namespace for each repository before importing all.', ); }); -- GitLab From f6dd2e6af2af184cf3ee4dd17f9060cb170f3e1f Mon Sep 17 00:00:00 2001 From: Oiza Date: Fri, 6 Jun 2025 20:39:38 -0400 Subject: [PATCH 08/11] Update handler ftn in github table --- .../import_projects/components/github_status_table.vue | 4 ++-- .../components/github_status_table_spec.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/import_entities/import_projects/components/github_status_table.vue b/app/assets/javascripts/import_entities/import_projects/components/github_status_table.vue index 7e3b60bcad22c0..6ac74d3c8d25ba 100644 --- a/app/assets/javascripts/import_entities/import_projects/components/github_status_table.vue +++ b/app/assets/javascripts/import_entities/import_projects/components/github_status_table.vue @@ -64,7 +64,7 @@ export default {