diff --git a/packages/editor-ui/src/components/SourceControlPushModal.ee.test.ts b/packages/editor-ui/src/components/SourceControlPushModal.ee.test.ts new file mode 100644 index 0000000000000..c7691340fd2a7 --- /dev/null +++ b/packages/editor-ui/src/components/SourceControlPushModal.ee.test.ts @@ -0,0 +1,150 @@ +import { within } from '@testing-library/dom'; +import userEvent from '@testing-library/user-event'; +import { useRoute } from 'vue-router'; +import { createComponentRenderer } from '@/__tests__/render'; +import SourceControlPushModal from '@/components/SourceControlPushModal.ee.vue'; +import { createTestingPinia } from '@pinia/testing'; +import { createEventBus } from 'n8n-design-system'; +import type { SourceControlAggregatedFile } from '@/Interface'; + +const eventBus = createEventBus(); + +vi.mock('vue-router', () => ({ + useRoute: vi.fn().mockReturnValue({ + params: vi.fn(), + fullPath: vi.fn(), + }), + RouterLink: vi.fn(), +})); + +let route: ReturnType; + +const renderModal = createComponentRenderer(SourceControlPushModal, { + global: { + stubs: { + Modal: { + template: ` +
+ + + + +
+ `, + }, + }, + }, +}); + +describe('SourceControlPushModal', () => { + beforeEach(() => { + route = useRoute(); + }); + + it('mounts', () => { + vi.spyOn(route, 'fullPath', 'get').mockReturnValue(''); + + const { getByTitle } = renderModal({ + pinia: createTestingPinia(), + props: { + data: { + eventBus, + status: [], + }, + }, + }); + expect(getByTitle('Commit and push changes')).toBeInTheDocument(); + }); + + it('should toggle checkboxes', async () => { + const status: SourceControlAggregatedFile[] = [ + { + id: 'gTbbBkkYTnNyX1jD', + name: 'My workflow 1', + type: 'workflow', + status: 'created', + location: 'local', + conflict: false, + file: '/home/user/.n8n/git/workflows/gTbbBkkYTnNyX1jD.json', + updatedAt: '2024-09-20T10:31:40.000Z', + }, + { + id: 'JIGKevgZagmJAnM6', + name: 'My workflow 2', + type: 'workflow', + status: 'created', + location: 'local', + conflict: false, + file: '/home/user/.n8n/git/workflows/JIGKevgZagmJAnM6.json', + updatedAt: '2024-09-20T14:42:51.968Z', + }, + ]; + + vi.spyOn(route, 'fullPath', 'get').mockReturnValue('/home/workflows'); + + const { getByTestId, getAllByTestId } = renderModal({ + pinia: createTestingPinia(), + props: { + data: { + eventBus, + status, + }, + }, + }); + + const files = getAllByTestId('source-control-push-modal-file-checkbox'); + expect(files).toHaveLength(2); + + await userEvent.click(files[0]); + expect(within(files[0]).getByRole('checkbox')).toBeChecked(); + expect(within(files[1]).getByRole('checkbox')).not.toBeChecked(); + + await userEvent.click(within(files[0]).getByRole('checkbox')); + expect(within(files[0]).getByRole('checkbox')).not.toBeChecked(); + expect(within(files[1]).getByRole('checkbox')).not.toBeChecked(); + + await userEvent.click(within(files[1]).getByRole('checkbox')); + expect(within(files[0]).getByRole('checkbox')).not.toBeChecked(); + expect(within(files[1]).getByRole('checkbox')).toBeChecked(); + + await userEvent.click(files[1]); + expect(within(files[0]).getByRole('checkbox')).not.toBeChecked(); + expect(within(files[1]).getByRole('checkbox')).not.toBeChecked(); + + await userEvent.click(within(files[0]).getByText('My workflow 2')); + expect(within(files[0]).getByRole('checkbox')).toBeChecked(); + expect(within(files[1]).getByRole('checkbox')).not.toBeChecked(); + + await userEvent.click(within(files[1]).getByText('My workflow 1')); + expect(within(files[0]).getByRole('checkbox')).toBeChecked(); + expect(within(files[1]).getByRole('checkbox')).toBeChecked(); + + await userEvent.click(within(files[1]).getByText('My workflow 1')); + expect(within(files[0]).getByRole('checkbox')).toBeChecked(); + expect(within(files[1]).getByRole('checkbox')).not.toBeChecked(); + + await userEvent.click(getByTestId('source-control-push-modal-toggle-all')); + expect(within(files[0]).getByRole('checkbox')).toBeChecked(); + expect(within(files[1]).getByRole('checkbox')).toBeChecked(); + + await userEvent.click(within(files[0]).getByText('My workflow 2')); + await userEvent.click(within(files[1]).getByText('My workflow 1')); + expect(within(files[0]).getByRole('checkbox')).not.toBeChecked(); + expect(within(files[1]).getByRole('checkbox')).not.toBeChecked(); + expect( + within(getByTestId('source-control-push-modal-toggle-all')).getByRole('checkbox'), + ).not.toBeChecked(); + + await userEvent.click(within(files[0]).getByText('My workflow 2')); + await userEvent.click(within(files[1]).getByText('My workflow 1')); + expect(within(files[0]).getByRole('checkbox')).toBeChecked(); + expect(within(files[1]).getByRole('checkbox')).toBeChecked(); + expect( + within(getByTestId('source-control-push-modal-toggle-all')).getByRole('checkbox'), + ).toBeChecked(); + + await userEvent.click(getByTestId('source-control-push-modal-toggle-all')); + expect(within(files[0]).getByRole('checkbox')).not.toBeChecked(); + expect(within(files[1]).getByRole('checkbox')).not.toBeChecked(); + }); +}); diff --git a/packages/editor-ui/src/components/SourceControlPushModal.ee.vue b/packages/editor-ui/src/components/SourceControlPushModal.ee.vue index 2f15af68133b1..0a0d99389a330 100644 --- a/packages/editor-ui/src/components/SourceControlPushModal.ee.vue +++ b/packages/editor-ui/src/components/SourceControlPushModal.ee.vue @@ -262,66 +262,66 @@ function getStatusText(file: SourceControlAggregatedFile): string {
- + {{ i18n.baseText('settings.sourceControl.modals.push.description') }} {{ i18n.baseText('settings.sourceControl.modals.push.description.learnMore') }} -
- - - {{ i18n.baseText('settings.sourceControl.modals.push.workflowsToCommit') }} - - - ({{ stagedWorkflowFiles.length }}/{{ workflowFiles.length }}) - - -
- + + {{ i18n.baseText('settings.sourceControl.modals.push.workflowsToCommit') }} + + + ({{ stagedWorkflowFiles.length }}/{{ workflowFiles.length }}) + + + + -
- -
- - Deleted Workflow: - Deleted Credential: - {{ file.name || file.id }} - - {{ file.name }} -
- - {{ renderUpdatedAt(file) }} - -
-
-
- - Current workflow - - - {{ getStatusText(file) }} - -
-
-
+ + + Deleted Workflow: + Deleted Credential: + {{ file.name || file.id }} + + {{ file.name }} + + {{ renderUpdatedAt(file) }} + + + + + Current workflow + + + {{ getStatusText(file) }} + + +
@@ -380,11 +380,15 @@ function getStatusText(file: SourceControlAggregatedFile): string { } .listItem { - margin-top: var(--spacing-2xs); - margin-bottom: var(--spacing-2xs); + display: flex; + width: 100%; + align-items: center; + margin: var(--spacing-2xs) 0 var(--spacing-2xs); + padding: var(--spacing-xs); cursor: pointer; transition: border 0.3s ease; - padding: var(--spacing-xs); + border-radius: var(--border-radius-large); + border: var(--border-base); &:hover { border-color: var(--color-foreground-dark); @@ -397,22 +401,16 @@ function getStatusText(file: SourceControlAggregatedFile): string { &:last-child { margin-bottom: 0; } -} -.listItemBody { - display: flex; - flex-direction: row; - align-items: center; -} - -.listItemCheckbox { - display: inline-flex !important; - margin-bottom: 0 !important; - margin-right: var(--spacing-2xs) !important; + &.hiddenListItem { + display: none !important; + } } -.listItemStatus { - margin-left: auto; +.selectAll { + float: left; + clear: both; + margin: 0 0 var(--spacing-2xs); } .footer { @@ -421,3 +419,12 @@ function getStatusText(file: SourceControlAggregatedFile): string { justify-content: flex-end; } + +