Commit 89313c7d authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch 'xanf-refactor-bulk-import-to-gl-table' into 'master'

Refactor bulk imports table to use GlTable

See merge request gitlab-org/gitlab!67932
parents 8495a557 3ed3f1dd
...@@ -28,7 +28,7 @@ export default { ...@@ -28,7 +28,7 @@ export default {
<template> <template>
<gl-dropdown <gl-dropdown
toggle-class="gl-rounded-top-right-none! gl-rounded-bottom-right-none!" toggle-class="gl-rounded-top-right-none! gl-rounded-bottom-right-none!"
class="import-entities-namespace-dropdown gl-h-7 gl-flex-fill-1" class="gl-h-7 gl-flex-fill-1"
data-qa-selector="target_namespace_selector_dropdown" data-qa-selector="target_namespace_selector_dropdown"
v-bind="$attrs" v-bind="$attrs"
> >
......
...@@ -10,19 +10,25 @@ import { ...@@ -10,19 +10,25 @@ import {
GlSearchBoxByClick, GlSearchBoxByClick,
GlSprintf, GlSprintf,
GlSafeHtmlDirective as SafeHtml, GlSafeHtmlDirective as SafeHtml,
GlTable,
GlTooltip, GlTooltip,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { s__, __, n__ } from '~/locale'; import { s__, __, n__ } from '~/locale';
import PaginationLinks from '~/vue_shared/components/pagination_links.vue'; import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
import ImportStatus from '../../components/import_status.vue';
import { STATUSES } from '../../constants'; import { STATUSES } from '../../constants';
import importGroupsMutation from '../graphql/mutations/import_groups.mutation.graphql'; import importGroupsMutation from '../graphql/mutations/import_groups.mutation.graphql';
import setImportTargetMutation from '../graphql/mutations/set_import_target.mutation.graphql'; import setImportTargetMutation from '../graphql/mutations/set_import_target.mutation.graphql';
import availableNamespacesQuery from '../graphql/queries/available_namespaces.query.graphql'; import availableNamespacesQuery from '../graphql/queries/available_namespaces.query.graphql';
import bulkImportSourceGroupsQuery from '../graphql/queries/bulk_import_source_groups.query.graphql'; import bulkImportSourceGroupsQuery from '../graphql/queries/bulk_import_source_groups.query.graphql';
import ImportTableRow from './import_table_row.vue'; import { isInvalid } from '../utils';
import ImportTargetCell from './import_target_cell.vue';
const PAGE_SIZES = [20, 50, 100]; const PAGE_SIZES = [20, 50, 100];
const DEFAULT_PAGE_SIZE = PAGE_SIZES[0]; const DEFAULT_PAGE_SIZE = PAGE_SIZES[0];
const DEFAULT_TH_CLASSES =
'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-200! gl-border-b-1! gl-p-5!';
const DEFAULT_TD_CLASSES = 'gl-vertical-align-top!';
export default { export default {
components: { components: {
...@@ -36,7 +42,9 @@ export default { ...@@ -36,7 +42,9 @@ export default {
GlSearchBoxByClick, GlSearchBoxByClick,
GlSprintf, GlSprintf,
GlTooltip, GlTooltip,
ImportTableRow, GlTable,
ImportStatus,
ImportTargetCell,
PaginationLinks, PaginationLinks,
}, },
directives: { directives: {
...@@ -76,6 +84,34 @@ export default { ...@@ -76,6 +84,34 @@ export default {
availableNamespaces: availableNamespacesQuery, availableNamespaces: availableNamespacesQuery,
}, },
fields: [
{
key: 'web_url',
label: s__('BulkImport|From source group'),
thClass: `${DEFAULT_TH_CLASSES} import-jobs-from-col`,
tdClass: DEFAULT_TD_CLASSES,
},
{
key: 'import_target',
label: s__('BulkImport|To new group'),
thClass: `${DEFAULT_TH_CLASSES} import-jobs-to-col`,
tdClass: DEFAULT_TD_CLASSES,
},
{
key: 'progress',
label: __('Status'),
thClass: `${DEFAULT_TH_CLASSES} import-jobs-status-col`,
tdClass: DEFAULT_TD_CLASSES,
tdAttr: { 'data-qa-selector': 'import_status_indicator' },
},
{
key: 'actions',
label: '',
thClass: `${DEFAULT_TH_CLASSES} import-jobs-cta-col`,
tdClass: DEFAULT_TD_CLASSES,
},
],
computed: { computed: {
groups() { groups() {
return this.bulkImportSourceGroups?.nodes ?? []; return this.bulkImportSourceGroups?.nodes ?? [];
...@@ -133,6 +169,25 @@ export default { ...@@ -133,6 +169,25 @@ export default {
}, },
methods: { methods: {
qaRowAttributes(group, type) {
if (type === 'row') {
return {
'data-qa-selector': 'import_item',
'data-qa-source-group': group.full_path,
};
}
return {};
},
isAlreadyImported(group) {
return group.progress.status !== STATUSES.NONE;
},
isInvalid(group) {
return isInvalid(group, this.groupPathRegex);
},
groupsCount(count) { groupsCount(count) {
return n__('%d group', '%d groups', count); return n__('%d group', '%d groups', count);
}, },
...@@ -243,32 +298,53 @@ export default { ...@@ -243,32 +298,53 @@ export default {
:description="s__('Check your source instance permissions.')" :description="s__('Check your source instance permissions.')"
/> />
<template v-else> <template v-else>
<table class="gl-w-full" data-qa-selector="import_table"> <gl-table
<thead class="gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1"> class="gl-w-full"
<th class="gl-py-4 import-jobs-from-col">{{ s__('BulkImport|From source group') }}</th> data-qa-selector="import_table"
<th class="gl-py-4 import-jobs-to-col">{{ s__('BulkImport|To new group') }}</th> tbody-tr-class="gl-border-gray-200 gl-border-0 gl-border-b-1 gl-border-solid"
<th class="gl-py-4 import-jobs-status-col">{{ __('Status') }}</th> :tbody-tr-attr="qaRowAttributes"
<th class="gl-py-4 import-jobs-cta-col"></th> :items="bulkImportSourceGroups.nodes"
</thead> :fields="$options.fields"
<tbody class="gl-vertical-align-top"> >
<template v-for="group in bulkImportSourceGroups.nodes"> <template #cell(web_url)="{ value: web_url, item: { full_path } }">
<import-table-row <gl-link
:key="group.id" :href="web_url"
:group="group" target="_blank"
:available-namespaces="availableNamespaces" class="gl-display-flex gl-align-items-center gl-h-7"
:group-path-regex="groupPathRegex" >
:group-url-error-message="groupUrlErrorMessage" {{ full_path }} <gl-icon name="external-link" />
@update-target-namespace=" </gl-link>
updateImportTarget(group.id, $event, group.import_target.new_name) </template>
" <template #cell(import_target)="{ item: group }">
@update-new-name=" <import-target-cell
updateImportTarget(group.id, group.import_target.target_namespace, $event) :group="group"
" :available-namespaces="availableNamespaces"
@import-group="importGroups([group.id])" :group-path-regex="groupPathRegex"
/> :group-url-error-message="groupUrlErrorMessage"
</template> @update-target-namespace="
</tbody> updateImportTarget(group.id, $event, group.import_target.new_name)
</table> "
@update-new-name="
updateImportTarget(group.id, group.import_target.target_namespace, $event)
"
/>
</template>
<template #cell(progress)="{ value: { status } }">
<import-status :status="status" class="gl-mt-2" />
</template>
<template #cell(actions)="{ item: group }">
<gl-button
v-if="!isAlreadyImported(group)"
:disabled="isInvalid(group)"
variant="confirm"
category="secondary"
data-qa-selector="import_group_button"
@click="importGroups([group.id])"
>
{{ __('Import') }}
</gl-button>
</template>
</gl-table>
<div v-if="hasGroups" class="gl-display-flex gl-mt-3 gl-align-items-center"> <div v-if="hasGroups" class="gl-display-flex gl-mt-3 gl-align-items-center">
<pagination-links <pagination-links
:change="setPage" :change="setPage"
......
<script>
import {
GlButton,
GlDropdownDivider,
GlDropdownItem,
GlDropdownSectionHeader,
GlIcon,
GlLink,
GlFormInput,
} from '@gitlab/ui';
import { joinPaths } from '~/lib/utils/url_utility';
import ImportGroupDropdown from '../../components/group_dropdown.vue';
import ImportStatus from '../../components/import_status.vue';
import { STATUSES } from '../../constants';
export default {
components: {
ImportStatus,
ImportGroupDropdown,
GlButton,
GlDropdownDivider,
GlDropdownItem,
GlDropdownSectionHeader,
GlLink,
GlIcon,
GlFormInput,
},
props: {
group: {
type: Object,
required: true,
},
availableNamespaces: {
type: Array,
required: true,
},
groupPathRegex: {
type: RegExp,
required: true,
},
groupUrlErrorMessage: {
type: String,
required: true,
},
},
computed: {
availableNamespaceNames() {
return this.availableNamespaces.map((ns) => ns.full_path);
},
importTarget() {
return this.group.import_target;
},
invalidNameValidationMessage() {
return this.group.validation_errors.find(({ field }) => field === 'new_name')?.message;
},
isInvalid() {
return Boolean(!this.isNameValid || this.invalidNameValidationMessage);
},
isNameValid() {
return this.groupPathRegex.test(this.importTarget.new_name);
},
isAlreadyImported() {
return this.group.progress.status !== STATUSES.NONE;
},
isFinished() {
return this.group.progress.status === STATUSES.FINISHED;
},
fullPath() {
return `${this.importTarget.target_namespace}/${this.importTarget.new_name}`;
},
absolutePath() {
return joinPaths(gon.relative_url_root || '/', this.fullPath);
},
},
};
</script>
<template>
<tr
class="gl-border-gray-200 gl-border-0 gl-border-b-1 gl-border-solid"
data-qa-selector="import_item"
:data-qa-source-group="group.full_path"
>
<td class="gl-p-4">
<gl-link
:href="group.web_url"
target="_blank"
class="gl-display-flex gl-align-items-center gl-h-7"
>
{{ group.full_path }} <gl-icon name="external-link" />
</gl-link>
</td>
<td class="gl-p-4">
<gl-link
v-if="isFinished"
class="gl-display-flex gl-align-items-center gl-h-7"
:href="absolutePath"
>
{{ fullPath }}
</gl-link>
<div
v-else
class="import-entities-target-select gl-display-flex gl-align-items-stretch"
:class="{
disabled: isAlreadyImported,
}"
>
<import-group-dropdown
#default="{ namespaces }"
:text="importTarget.target_namespace"
:disabled="isAlreadyImported"
:namespaces="availableNamespaceNames"
toggle-class="gl-rounded-top-right-none! gl-rounded-bottom-right-none!"
class="import-entities-namespace-dropdown gl-h-7 gl-flex-grow-1"
data-qa-selector="target_namespace_selector_dropdown"
>
<gl-dropdown-item @click="$emit('update-target-namespace', '')">{{
s__('BulkImport|No parent')
}}</gl-dropdown-item>
<template v-if="namespaces.length">
<gl-dropdown-divider />
<gl-dropdown-section-header>
{{ s__('BulkImport|Existing groups') }}
</gl-dropdown-section-header>
<gl-dropdown-item
v-for="ns in namespaces"
:key="ns"
data-qa-selector="target_group_dropdown_item"
:data-qa-group-name="ns"
@click="$emit('update-target-namespace', ns)"
>
{{ ns }}
</gl-dropdown-item>
</template>
</import-group-dropdown>
<div
class="import-entities-target-select-separator gl-h-7 gl-px-3 gl-display-flex gl-align-items-center gl-border-solid gl-border-0 gl-border-t-1 gl-border-b-1"
>
/
</div>
<div class="gl-flex-grow-1">
<gl-form-input
class="gl-rounded-top-left-none gl-rounded-bottom-left-none"
:class="{ 'is-invalid': isInvalid && !isAlreadyImported }"
:disabled="isAlreadyImported"
:value="importTarget.new_name"
@input="$emit('update-new-name', $event)"
/>
<p v-if="isInvalid" class="gl-text-red-500 gl-m-0 gl-mt-2">
<template v-if="!isNameValid">
{{ groupUrlErrorMessage }}
</template>
<template v-else-if="invalidNameValidationMessage">
{{ invalidNameValidationMessage }}
</template>
</p>
</div>
</div>
</td>
<td class="gl-p-4 gl-white-space-nowrap" data-qa-selector="import_status_indicator">
<import-status :status="group.progress.status" class="gl-mt-2" />
</td>
<td class="gl-p-4">
<gl-button
v-if="!isAlreadyImported"
:disabled="isInvalid"
variant="confirm"
category="secondary"
data-qa-selector="import_group_button"
@click="$emit('import-group')"
>{{ __('Import') }}</gl-button
>
</td>
</tr>
</template>
<script>
import {
GlDropdownDivider,
GlDropdownItem,
GlDropdownSectionHeader,
GlLink,
GlFormInput,
} from '@gitlab/ui';
import { joinPaths } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import ImportGroupDropdown from '../../components/group_dropdown.vue';
import { STATUSES } from '../../constants';
import { isInvalid, getInvalidNameValidationMessage, isNameValid } from '../utils';
export default {
components: {
ImportGroupDropdown,
GlDropdownDivider,
GlDropdownItem,
GlDropdownSectionHeader,
GlLink,
GlFormInput,
},
props: {
group: {
type: Object,
required: true,
},
availableNamespaces: {
type: Array,
required: true,
},
groupPathRegex: {
type: RegExp,
required: true,
},
groupUrlErrorMessage: {
type: String,
required: true,
},
},
computed: {
availableNamespaceNames() {
return this.availableNamespaces.map((ns) => ns.full_path);
},
importTarget() {
return this.group.import_target;
},
invalidNameValidationMessage() {
return getInvalidNameValidationMessage(this.group);
},
isInvalid() {
return isInvalid(this.group, this.groupPathRegex);
},
isNameValid() {
return isNameValid(this.group, this.groupPathRegex);
},
isAlreadyImported() {
return this.group.progress.status !== STATUSES.NONE;
},
isFinished() {
return this.group.progress.status === STATUSES.FINISHED;
},
fullPath() {
return `${this.importTarget.target_namespace}/${this.importTarget.new_name}`;
},
absolutePath() {
return joinPaths(gon.relative_url_root || '/', this.fullPath);
},
},
i18n: {
NAME_ALREADY_EXISTS: s__('BulkImport|Name already exists.'),
},
};
</script>
<template>
<gl-link
v-if="isFinished"
class="gl-display-flex gl-align-items-center gl-h-7"
:href="absolutePath"
>
{{ fullPath }}
</gl-link>
<div
v-else
class="gl-display-flex gl-align-items-stretch"
:class="{
disabled: isAlreadyImported,
}"
>
<import-group-dropdown
#default="{ namespaces }"
:text="importTarget.target_namespace"
:disabled="isAlreadyImported"
:namespaces="availableNamespaceNames"
toggle-class="gl-rounded-top-right-none! gl-rounded-bottom-right-none!"
class="gl-h-7 gl-flex-grow-1"
data-qa-selector="target_namespace_selector_dropdown"
>
<gl-dropdown-item @click="$emit('update-target-namespace', '')">{{
s__('BulkImport|No parent')
}}</gl-dropdown-item>
<template v-if="namespaces.length">
<gl-dropdown-divider />
<gl-dropdown-section-header>
{{ s__('BulkImport|Existing groups') }}
</gl-dropdown-section-header>
<gl-dropdown-item
v-for="ns in namespaces"
:key="ns"
data-qa-selector="target_group_dropdown_item"
:data-qa-group-name="ns"
@click="$emit('update-target-namespace', ns)"
>
{{ ns }}
</gl-dropdown-item>
</template>
</import-group-dropdown>
<div
class="gl-h-7 gl-px-3 gl-display-flex gl-align-items-center gl-border-solid gl-border-0 gl-border-t-1 gl-border-b-1 gl-bg-gray-10"
:class="{
'gl-text-gray-400 gl-border-gray-100': isAlreadyImported,
'gl-border-gray-200': !isAlreadyImported,
}"
>
/
</div>
<div class="gl-flex-grow-1">
<gl-form-input
class="gl-rounded-top-left-none gl-rounded-bottom-left-none"
:class="{
'gl-inset-border-1-gray-200!': !isAlreadyImported,
'gl-inset-border-1-gray-100!': isAlreadyImported,
'is-invalid': isInvalid && !isAlreadyImported,
}"
:disabled="isAlreadyImported"
:value="importTarget.new_name"
@input="$emit('update-new-name', $event)"
/>
<p v-if="isInvalid" class="gl-text-red-500 gl-m-0 gl-mt-2">
<template v-if="!isNameValid">
{{ groupUrlErrorMessage }}
</template>
<template v-else-if="invalidNameValidationMessage">
{{ invalidNameValidationMessage }}
</template>
</p>
</div>
</div>
</template>
...@@ -3,3 +3,5 @@ import { s__ } from '~/locale'; ...@@ -3,3 +3,5 @@ import { s__ } from '~/locale';
export const i18n = { export const i18n = {
NAME_ALREADY_EXISTS: s__('BulkImport|Name already exists.'), NAME_ALREADY_EXISTS: s__('BulkImport|Name already exists.'),
}; };
export const NEW_NAME_FIELD = 'new_name';
...@@ -4,7 +4,7 @@ import axios from '~/lib/utils/axios_utils'; ...@@ -4,7 +4,7 @@ import axios from '~/lib/utils/axios_utils';
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { STATUSES } from '../../constants'; import { STATUSES } from '../../constants';
import { i18n } from '../constants'; import { i18n, NEW_NAME_FIELD } from '../constants';
import bulkImportSourceGroupItemFragment from './fragments/bulk_import_source_group_item.fragment.graphql'; import bulkImportSourceGroupItemFragment from './fragments/bulk_import_source_group_item.fragment.graphql';
import addValidationErrorMutation from './mutations/add_validation_error.mutation.graphql'; import addValidationErrorMutation from './mutations/add_validation_error.mutation.graphql';
import removeValidationErrorMutation from './mutations/remove_validation_error.mutation.graphql'; import removeValidationErrorMutation from './mutations/remove_validation_error.mutation.graphql';
...@@ -61,7 +61,7 @@ async function checkImportTargetIsValid({ client, newName, targetNamespace, sour ...@@ -61,7 +61,7 @@ async function checkImportTargetIsValid({ client, newName, targetNamespace, sour
}); });
const variables = { const variables = {
field: 'new_name', field: NEW_NAME_FIELD,
sourceGroupId, sourceGroupId,
}; };
......
import { NEW_NAME_FIELD } from './constants';
export function isNameValid(group, validationRegex) {
return validationRegex.test(group.import_target[NEW_NAME_FIELD]);
}
export function getInvalidNameValidationMessage(group) {
return group.validation_errors.find(({ field }) => field === NEW_NAME_FIELD)?.message;
}
export function isInvalid(group, validationRegex) {
return Boolean(!isNameValid(group, validationRegex) || getInvalidNameValidationMessage(group));
}
...@@ -22,16 +22,11 @@ ...@@ -22,16 +22,11 @@
.import-entities-target-select { .import-entities-target-select {
&.disabled { &.disabled {
.import-entities-target-select-separator, .import-entities-target-select-separator {
.select2-container.select2-container-disabled .select2-choice {
color: var(--gray-400, $gray-400); color: var(--gray-400, $gray-400);
border-color: var(--gray-100, $gray-100); border-color: var(--gray-100, $gray-100);
background-color: var(--gray-10, $gray-10); background-color: var(--gray-10, $gray-10);
} }
.select2-container.select2-container-disabled .select2-choice .select2-arrow {
background-color: var(--gray-10, $gray-10);
}
} }
.import-entities-target-select-separator { .import-entities-target-select-separator {
...@@ -39,20 +34,6 @@ ...@@ -39,20 +34,6 @@
background-color: var(--gray-10, $gray-10); background-color: var(--gray-10, $gray-10);
} }
.select2-container {
> .select2-choice {
.select2-arrow {
background-color: var(--white, $white);
}
border-color: var(--gray-200, $gray-200);
color: var(--gray-900, $gray-900) !important;
background-color: var(--white, $white) !important;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}
.gl-form-input { .gl-form-input {
box-shadow: inset 0 0 0 1px var(--gray-200, $gray-200); box-shadow: inset 0 0 0 1px var(--gray-200, $gray-200);
} }
......
- add_to_breadcrumbs _('New group'), new_group_path - add_to_breadcrumbs _('New group'), new_group_path
- add_page_specific_style 'page_bundles/import' - add_page_specific_style 'page_bundles/import'
- breadcrumb_title _('Import groups') - page_title _('Import groups')
#import-groups-mount-element{ data: { status_path: status_import_bulk_imports_path(format: :json), #import-groups-mount-element{ data: { status_path: status_import_bulk_imports_path(format: :json),
available_namespaces_path: import_available_namespaces_path(format: :json), available_namespaces_path: import_available_namespaces_path(format: :json),
......
...@@ -6,13 +6,13 @@ module QA ...@@ -6,13 +6,13 @@ module QA
class BulkImport < Page::Base class BulkImport < Page::Base
view "app/assets/javascripts/import_entities/import_groups/components/import_table.vue" do view "app/assets/javascripts/import_entities/import_groups/components/import_table.vue" do
element :import_table element :import_table
element :import_item
element :import_group_button
element :import_status_indicator
end end
view "app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue" do view "app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue" do
element :import_item
element :target_group_dropdown_item element :target_group_dropdown_item
element :import_status_indicator
element :import_group_button
end end
view "app/assets/javascripts/import_entities/components/group_dropdown.vue" do view "app/assets/javascripts/import_entities/components/group_dropdown.vue" do
......
...@@ -3,18 +3,18 @@ import { ...@@ -3,18 +3,18 @@ import {
GlEmptyState, GlEmptyState,
GlLoadingIcon, GlLoadingIcon,
GlSearchBoxByClick, GlSearchBoxByClick,
GlSprintf,
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { mount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import stubChildren from 'helpers/stub_children';
import { stubComponent } from 'helpers/stub_component'; import { stubComponent } from 'helpers/stub_component';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import { STATUSES } from '~/import_entities/constants'; import { STATUSES } from '~/import_entities/constants';
import ImportTable from '~/import_entities/import_groups/components/import_table.vue'; import ImportTable from '~/import_entities/import_groups/components/import_table.vue';
import ImportTableRow from '~/import_entities/import_groups/components/import_table_row.vue'; import ImportTargetCell from '~/import_entities/import_groups/components/import_target_cell.vue';
import importGroupsMutation from '~/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql'; import importGroupsMutation from '~/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql';
import setImportTargetMutation from '~/import_entities/import_groups/graphql/mutations/set_import_target.mutation.graphql'; import setImportTargetMutation from '~/import_entities/import_groups/graphql/mutations/set_import_target.mutation.graphql';
import PaginationLinks from '~/vue_shared/components/pagination_links.vue'; import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
...@@ -57,15 +57,17 @@ describe('import table', () => { ...@@ -57,15 +57,17 @@ describe('import table', () => {
}, },
}); });
wrapper = shallowMount(ImportTable, { wrapper = mount(ImportTable, {
propsData: { propsData: {
groupPathRegex: /.*/, groupPathRegex: /.*/,
sourceUrl: SOURCE_URL, sourceUrl: SOURCE_URL,
groupUrlErrorMessage: 'Please choose a group URL with no special characters or spaces.', groupUrlErrorMessage: 'Please choose a group URL with no special characters or spaces.',
}, },
stubs: { stubs: {
GlSprintf, ...stubChildren(ImportTable),
GlSprintf: false,
GlDropdown: GlDropdownStub, GlDropdown: GlDropdownStub,
GlTable: false,
}, },
localVue, localVue,
apolloProvider, apolloProvider,
...@@ -115,7 +117,7 @@ describe('import table', () => { ...@@ -115,7 +117,7 @@ describe('import table', () => {
}); });
await waitForPromises(); await waitForPromises();
expect(wrapper.findAll(ImportTableRow)).toHaveLength(FAKE_GROUPS.length); expect(wrapper.findAll('tbody tr')).toHaveLength(FAKE_GROUPS.length);
}); });
it('does not render status string when result list is empty', async () => { it('does not render status string when result list is empty', async () => {
...@@ -142,16 +144,29 @@ describe('import table', () => { ...@@ -142,16 +144,29 @@ describe('import table', () => {
event | payload | mutation | variables event | payload | mutation | variables
${'update-target-namespace'} | ${'new-namespace'} | ${setImportTargetMutation} | ${{ sourceGroupId: FAKE_GROUP.id, targetNamespace: 'new-namespace', newName: 'group1' }} ${'update-target-namespace'} | ${'new-namespace'} | ${setImportTargetMutation} | ${{ sourceGroupId: FAKE_GROUP.id, targetNamespace: 'new-namespace', newName: 'group1' }}
${'update-new-name'} | ${'new-name'} | ${setImportTargetMutation} | ${{ sourceGroupId: FAKE_GROUP.id, targetNamespace: 'root', newName: 'new-name' }} ${'update-new-name'} | ${'new-name'} | ${setImportTargetMutation} | ${{ sourceGroupId: FAKE_GROUP.id, targetNamespace: 'root', newName: 'new-name' }}
${'import-group'} | ${undefined} | ${importGroupsMutation} | ${{ sourceGroupIds: [FAKE_GROUP.id] }}
`('correctly maps $event to mutation', async ({ event, payload, mutation, variables }) => { `('correctly maps $event to mutation', async ({ event, payload, mutation, variables }) => {
jest.spyOn(apolloProvider.defaultClient, 'mutate'); jest.spyOn(apolloProvider.defaultClient, 'mutate');
wrapper.find(ImportTableRow).vm.$emit(event, payload); wrapper.find(ImportTargetCell).vm.$emit(event, payload);
await waitForPromises(); await waitForPromises();
expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({ expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({
mutation, mutation,
variables, variables,
}); });
}); });
it('invokes importGroups mutation when row button is clicked', async () => {
jest.spyOn(apolloProvider.defaultClient, 'mutate');
const triggerImportButton = wrapper
.findAllComponents(GlButton)
.wrappers.find((w) => w.text() === 'Import');
triggerImportButton.vm.$emit('click');
await waitForPromises();
expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({
mutation: importGroupsMutation,
variables: { sourceGroupIds: [FAKE_GROUP.id] },
});
});
}); });
describe('pagination', () => { describe('pagination', () => {
......
...@@ -4,7 +4,7 @@ import Vue, { nextTick } from 'vue'; ...@@ -4,7 +4,7 @@ import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import ImportGroupDropdown from '~/import_entities/components/group_dropdown.vue'; import ImportGroupDropdown from '~/import_entities/components/group_dropdown.vue';
import { STATUSES } from '~/import_entities/constants'; import { STATUSES } from '~/import_entities/constants';
import ImportTableRow from '~/import_entities/import_groups/components/import_table_row.vue'; import ImportTargetCell from '~/import_entities/import_groups/components/import_target_cell.vue';
import { availableNamespacesFixture } from '../graphql/fixtures'; import { availableNamespacesFixture } from '../graphql/fixtures';
Vue.use(VueApollo); Vue.use(VueApollo);
...@@ -22,19 +22,18 @@ const getFakeGroup = (status) => ({ ...@@ -22,19 +22,18 @@ const getFakeGroup = (status) => ({
progress: { status }, progress: { status },
}); });
describe('import table row', () => { describe('import target cell', () => {
let wrapper; let wrapper;
let group; let group;
const findByText = (cmp, text) => { const findByText = (cmp, text) => {
return wrapper.findAll(cmp).wrappers.find((node) => node.text().indexOf(text) === 0); return wrapper.findAll(cmp).wrappers.find((node) => node.text().indexOf(text) === 0);
}; };
const findImportButton = () => findByText(GlButton, 'Import');
const findNameInput = () => wrapper.find(GlFormInput); const findNameInput = () => wrapper.find(GlFormInput);
const findNamespaceDropdown = () => wrapper.find(ImportGroupDropdown); const findNamespaceDropdown = () => wrapper.find(ImportGroupDropdown);
const createComponent = (props) => { const createComponent = (props) => {
wrapper = shallowMount(ImportTableRow, { wrapper = shallowMount(ImportTargetCell, {
stubs: { ImportGroupDropdown }, stubs: { ImportGroupDropdown },
propsData: { propsData: {
availableNamespaces: availableNamespacesFixture, availableNamespaces: availableNamespacesFixture,
...@@ -56,14 +55,10 @@ describe('import table row', () => { ...@@ -56,14 +55,10 @@ describe('import table row', () => {
createComponent({ group }); createComponent({ group });
}); });
it.each` it('invokes $event', () => {
selector | sourceEvent | payload | event findNameInput().vm.$emit('input', 'demo');
${findNameInput} | ${'input'} | ${'demo'} | ${'update-new-name'} expect(wrapper.emitted('update-new-name')).toBeDefined();
${findImportButton} | ${'click'} | ${undefined} | ${'import-group'} expect(wrapper.emitted('update-new-name')[0][0]).toBe('demo');
`('invokes $event', ({ selector, sourceEvent, payload, event }) => {
selector().vm.$emit(sourceEvent, payload);
expect(wrapper.emitted(event)).toBeDefined();
expect(wrapper.emitted(event)[0][0]).toBe(payload);
}); });
it('emits update-target-namespace when dropdown option is clicked', () => { it('emits update-target-namespace when dropdown option is clicked', () => {
...@@ -83,10 +78,6 @@ describe('import table row', () => { ...@@ -83,10 +78,6 @@ describe('import table row', () => {
createComponent({ group }); createComponent({ group });
}); });
it('renders Import button', () => {
expect(findByText(GlButton, 'Import').exists()).toBe(true);
});
it('renders namespace dropdown as not disabled', () => { it('renders namespace dropdown as not disabled', () => {
expect(findNamespaceDropdown().attributes('disabled')).toBe(undefined); expect(findNamespaceDropdown().attributes('disabled')).toBe(undefined);
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment