Skip to content

Commit

Permalink
fix(editor): Improve commit modal user facing messaging (#12161)
Browse files Browse the repository at this point in the history
  • Loading branch information
r00gm authored Dec 17, 2024
1 parent 9180b46 commit ad39243
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 50 deletions.
36 changes: 18 additions & 18 deletions cypress/e2e/39-projects.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,15 +498,15 @@ describe('Projects', { disableAutoLogin: true }, () => {
projects
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Move workflow")')
.contains('button', 'Move workflow')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 5)
.filter(':contains("Project 1")')
.click();
projects.getResourceMoveModal().find('button:contains("Move workflow")').click();
projects.getResourceMoveModal().contains('button', 'Move workflow').click();
clearNotifications();

workflowsPage.getters
Expand All @@ -524,15 +524,15 @@ describe('Projects', { disableAutoLogin: true }, () => {
projects
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Move workflow")')
.contains('button', 'Move workflow')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 5)
.filter(':contains("Project 2")')
.click();
projects.getResourceMoveModal().find('button:contains("Move workflow")').click();
projects.getResourceMoveModal().contains('button', 'Move workflow').click();

// Move the workflow from Project 2 to a member user
projects.getMenuItems().last().click();
Expand All @@ -544,7 +544,7 @@ describe('Projects', { disableAutoLogin: true }, () => {
projects
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Move workflow")')
.contains('button', 'Move workflow')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
Expand All @@ -553,7 +553,7 @@ describe('Projects', { disableAutoLogin: true }, () => {
.filter(`:contains("${INSTANCE_MEMBERS[0].email}")`)
.click();

projects.getResourceMoveModal().find('button:contains("Move workflow")').click();
projects.getResourceMoveModal().contains('button', 'Move workflow').click();
workflowsPage.getters.workflowCards().should('have.length', 1);

// Move the workflow from member user back to Home
Expand All @@ -569,7 +569,7 @@ describe('Projects', { disableAutoLogin: true }, () => {
projects
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Move workflow")')
.contains('button', 'Move workflow')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
Expand All @@ -578,7 +578,7 @@ describe('Projects', { disableAutoLogin: true }, () => {
.filter(`:contains("${INSTANCE_OWNER.email}")`)
.click();

projects.getResourceMoveModal().find('button:contains("Move workflow")').click();
projects.getResourceMoveModal().contains('button', 'Move workflow').click();
clearNotifications();
workflowsPage.getters
.workflowCards()
Expand All @@ -596,15 +596,15 @@ describe('Projects', { disableAutoLogin: true }, () => {
projects
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Move credential")')
.contains('button', 'Move credential')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 5)
.filter(':contains("Project 2")')
.click();
projects.getResourceMoveModal().find('button:contains("Move credential")').click();
projects.getResourceMoveModal().contains('button', 'Move credential').click();
clearNotifications();
credentialsPage.getters.credentialCards().should('not.have.length');

Expand All @@ -619,15 +619,15 @@ describe('Projects', { disableAutoLogin: true }, () => {
projects
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Move credential")')
.contains('button', 'Move credential')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 5)
.filter(`:contains("${INSTANCE_ADMIN.email}")`)
.click();
projects.getResourceMoveModal().find('button:contains("Move credential")').click();
projects.getResourceMoveModal().contains('button', 'Move credential').click();
credentialsPage.getters.credentialCards().should('have.length', 1);

// Move the credential from admin user back to instance owner
Expand All @@ -641,15 +641,15 @@ describe('Projects', { disableAutoLogin: true }, () => {
projects
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Move credential")')
.contains('button', 'Move credential')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 5)
.filter(`:contains("${INSTANCE_OWNER.email}")`)
.click();
projects.getResourceMoveModal().find('button:contains("Move credential")').click();
projects.getResourceMoveModal().contains('button', 'Move credential').click();

clearNotifications();

Expand All @@ -666,15 +666,15 @@ describe('Projects', { disableAutoLogin: true }, () => {
projects
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Move credential")')
.contains('button', 'Move credential')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 5)
.filter(':contains("Project 1")')
.click();
projects.getResourceMoveModal().find('button:contains("Move credential")').click();
projects.getResourceMoveModal().contains('button', 'Move credential').click();

projects.getMenuItems().first().click();
projects.getProjectTabCredentials().click();
Expand Down Expand Up @@ -721,15 +721,15 @@ describe('Projects', { disableAutoLogin: true }, () => {
projects
.getResourceMoveModal()
.should('be.visible')
.find('button:contains("Move workflow")')
.contains('button', 'Move workflow')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect()
.find('li')
.should('have.length', 4)
.filter(':contains("Project 1")')
.click();
projects.getResourceMoveModal().find('button:contains("Move workflow")').click();
projects.getResourceMoveModal().contains('button', 'Move workflow').click();

workflowsPage.getters
.workflowCards()
Expand Down
4 changes: 3 additions & 1 deletion packages/design-system/src/components/N8nNotice/Notice.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ interface NoticeProps {
theme?: 'success' | 'warning' | 'danger' | 'info';
content?: string;
fullContent?: string;
compact?: boolean;
}
const props = withDefaults(defineProps<NoticeProps>(), {
id: () => uid('notice'),
theme: 'warning',
content: '',
fullContent: '',
compact: true,
});
const emit = defineEmits<{
Expand Down Expand Up @@ -68,7 +70,7 @@ const onClick = (event: MouseEvent) => {
<template>
<div :id="id" :class="classes" role="alert" @click="onClick">
<div class="notice-content">
<N8nText size="small" :compact="true">
<N8nText size="small" :compact="compact">
<slot>
<span
:id="`${id}-content`"
Expand Down
27 changes: 25 additions & 2 deletions packages/editor-ui/src/components/MainSidebarSourceControl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,17 @@ let sourceControlStore: ReturnType<typeof useSourceControlStore>;
let uiStore: ReturnType<typeof useUIStore>;
let rbacStore: ReturnType<typeof useRBACStore>;

const showMessage = vi.fn();
const showError = vi.fn();
vi.mock('@/composables/useToast', () => ({
useToast: () => ({ showMessage, showError }),
}));

const renderComponent = createComponentRenderer(MainSidebarSourceControl);

describe('MainSidebarSourceControl', () => {
beforeEach(() => {
vi.resetAllMocks();
pinia = createTestingPinia({
initialState: {
[STORES.SETTINGS]: {
Expand Down Expand Up @@ -75,13 +82,13 @@ describe('MainSidebarSourceControl', () => {
vi.spyOn(sourceControlStore, 'pullWorkfolder').mockRejectedValueOnce({
response: { status: 400 },
});
const { getAllByRole, getByRole } = renderComponent({
const { getAllByRole } = renderComponent({
pinia,
props: { isCollapsed: false },
});

await userEvent.click(getAllByRole('button')[0]);
await waitFor(() => expect(getByRole('alert')).toBeInTheDocument());
await waitFor(() => expect(showError).toHaveBeenCalled());
});

it('should show confirm if pull response http status code is 409', async () => {
Expand All @@ -108,5 +115,21 @@ describe('MainSidebarSourceControl', () => {
),
);
});

it('should show toast when there are no changes', async () => {
vi.spyOn(sourceControlStore, 'getAggregatedStatus').mockResolvedValueOnce([]);

const { getAllByRole } = renderComponent({
pinia,
props: { isCollapsed: false },
});

await userEvent.click(getAllByRole('button')[1]);
await waitFor(() =>
expect(showMessage).toHaveBeenCalledWith(
expect.objectContaining({ title: 'No changes to commit' }),
),
);
});
});
});
10 changes: 10 additions & 0 deletions packages/editor-ui/src/components/MainSidebarSourceControl.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ async function pushWorkfolder() {
try {
const status = await sourceControlStore.getAggregatedStatus();
if (!status.length) {
toast.showMessage({
title: 'No changes to commit',
message: 'Everything is up to date',
type: 'info',
});
return;
}
uiStore.openModalWithData({
name: SOURCE_CONTROL_PUSH_MODAL_KEY,
data: { eventBus, status },
Expand All @@ -68,6 +77,7 @@ async function pullWorkfolder() {
const statusWithoutLocallyCreatedWorkflows = status.filter((file) => {
return !(file.type === 'workflow' && file.status === 'created' && file.location === 'local');
});
if (statusWithoutLocallyCreatedWorkflows.length === 0) {
toast.showMessage({
title: i18n.baseText('settings.sourceControl.pull.upToDate.title'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,9 @@ describe('SourceControlPushModal', () => {
const submitButton = getByTestId('source-control-push-modal-submit');
const commitMessage = 'commit message';
expect(submitButton).toBeDisabled();
expect(
getByText(
'No workflow changes to push. Only modified credentials, variables, and tags will be pushed.',
),
).toBeInTheDocument();
expect(getByText('1 new credentials added, 0 deleted and 0 changed')).toBeInTheDocument();
expect(getByText('At least one new variable has been added or modified')).toBeInTheDocument();
expect(getByText('At least one new tag has been added or modified')).toBeInTheDocument();

await userEvent.type(getByTestId('source-control-push-modal-commit'), commitMessage);

Expand Down Expand Up @@ -375,5 +373,57 @@ describe('SourceControlPushModal', () => {
expect(items[0]).toHaveTextContent('Created Workflow');
});
});

it('should reset', async () => {
const status: SourceControlAggregatedFile[] = [
{
id: 'JIGKevgZagmJAnM6',
name: 'Modified workflow',
type: 'workflow',
status: 'modified',
location: 'local',
conflict: false,
file: '/home/user/.n8n/git/workflows/JIGKevgZagmJAnM6.json',
updatedAt: '2024-09-20T14:42:51.968Z',
},
];

const { getByTestId, getAllByTestId, queryAllByTestId } = renderModal({
props: {
data: {
eventBus,
status,
},
},
});

expect(getAllByTestId('source-control-push-modal-file-checkbox')).toHaveLength(1);

await userEvent.click(getByTestId('source-control-filter-dropdown'));

expect(getByTestId('source-control-status-filter')).toBeVisible();

await userEvent.click(
within(getByTestId('source-control-status-filter')).getByRole('combobox'),
);

await waitFor(() =>
expect(getAllByTestId('source-control-status-filter-option')[0]).toBeVisible(),
);

const menu = getAllByTestId('source-control-status-filter-option')[0]
.parentElement as HTMLElement;

await userEvent.click(within(menu).getByText('New'));
await waitFor(() => {
expect(queryAllByTestId('source-control-push-modal-file-checkbox')).toHaveLength(0);
expect(getByTestId('source-control-filters-reset')).toBeInTheDocument();
});

await userEvent.click(getByTestId('source-control-filters-reset'));

const items = getAllByTestId('source-control-push-modal-file-checkbox');
expect(items).toHaveLength(1);
});
});
});
Loading

0 comments on commit ad39243

Please sign in to comment.