Skip to content

Commit

Permalink
Merge branch 'master' into node-1586-fix-flaky-mapping-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
elsmr committed Aug 16, 2024
2 parents 22606e6 + b857c2c commit 1611124
Show file tree
Hide file tree
Showing 12 changed files with 345 additions and 51 deletions.
21 changes: 21 additions & 0 deletions cypress/e2e/1858-PAY-can-use-context-menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';

const WorkflowPage = new WorkflowPageClass();

describe('PAY-1858 context menu', () => {
it('can use context menu on saved workflow', () => {
WorkflowPage.actions.visit();
cy.createFixtureWorkflow('Test_workflow_filter.json', 'test');

WorkflowPage.getters.canvasNodes().should('have.length', 5);
WorkflowPage.actions.deleteNodeFromContextMenu('Then');
WorkflowPage.getters.canvasNodes().should('have.length', 4);

WorkflowPage.actions.hitSaveWorkflow();

cy.reload();
WorkflowPage.getters.canvasNodes().should('have.length', 4);
WorkflowPage.actions.deleteNodeFromContextMenu('Code');
WorkflowPage.getters.canvasNodes().should('have.length', 3);
});
});
3 changes: 2 additions & 1 deletion cypress/e2e/33-settings-personal.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ describe('Personal Settings', () => {
successToast().find('.el-notification__closeBtn').click();
});
});
it('not allow malicious values for personal data', () => {
// eslint-disable-next-line n8n-local-rules/no-skipped-tests
it.skip('not allow malicious values for personal data', () => {
cy.visit('/settings/personal');
INVALID_NAMES.forEach((name) => {
cy.getByTestId('personal-data-form').find('input[name="firstName"]').clear().type(name);
Expand Down
31 changes: 30 additions & 1 deletion packages/editor-ui/src/__tests__/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
SET_NODE_TYPE,
STICKY_NODE_TYPE,
} from '@/constants';
import type { INodeUi } from '@/Interface';
import type { INodeUi, IWorkflowDb } from '@/Interface';

export const mockNode = ({
id = uuid(),
Expand Down Expand Up @@ -147,6 +147,35 @@ export function createTestWorkflowObject({
});
}

export function createTestWorkflow({
id = uuid(),
name = 'Test Workflow',
nodes = [],
connections = {},
active = false,
settings = {
timezone: 'DEFAULT',
executionOrder: 'v1',
},
pinData = {},
...rest
}: Partial<IWorkflowDb> = {}): IWorkflowDb {
return {
createdAt: '',
updatedAt: '',
id,
name,
nodes,
connections,
active,
settings,
versionId: '1',
meta: {},
pinData,
...rest,
};
}

export function createTestNode(node: Partial<INode> = {}): INode {
return {
id: uuid(),
Expand Down
9 changes: 8 additions & 1 deletion packages/editor-ui/src/api/assistant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ export function chatWithAssistant(
onDone: () => void,
onError: (e: Error) => void,
): void {
void streamRequest(ctx, '/ai-assistant/chat', payload, onMessageUpdated, onDone, onError);
void streamRequest<ChatRequest.ResponsePayload>(
ctx,
'/ai-assistant/chat',
payload,
onMessageUpdated,
onDone,
onError,
);
}

export async function replaceCode(
Expand Down
20 changes: 11 additions & 9 deletions packages/editor-ui/src/components/RunData.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,17 @@
data-test-id="run-data-pane-header"
@click.stop
>
<LazyRunDataSearch
v-if="showIOSearch"
v-model="search"
:class="$style.search"
:pane-type="paneType"
:display-mode="displayMode"
:is-area-active="isPaneActive"
@focus="activatePane"
/>
<Suspense>
<LazyRunDataSearch
v-if="showIOSearch"
v-model="search"
:class="$style.search"
:pane-type="paneType"
:display-mode="displayMode"
:is-area-active="isPaneActive"
@focus="activatePane"
/>
</Suspense>

<n8n-radio-buttons
v-show="
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ function openContextMenu(event: MouseEvent) {
top: 100%;
position: absolute;
width: 100%;
min-width: 200px;
min-width: calc(var(--canvas-node--width) * 2);
margin-top: var(--spacing-2xs);
display: flex;
flex-direction: column;
Expand All @@ -223,16 +223,27 @@ function openContextMenu(event: MouseEvent) {
.label {
font-size: var(--font-size-m);
line-height: var(--font-line-height-compact);
text-align: center;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
overflow-wrap: anywhere;
font-weight: var(--font-weight-bold);
line-height: var(--font-line-height-compact);
}
.subtitle {
width: 100%;
text-align: center;
color: var(--color-text-light);
font-size: var(--font-size-xs);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: var(--font-line-height-compact);
font-weight: 400;
}
.statusIcons {
Expand Down
138 changes: 115 additions & 23 deletions packages/editor-ui/src/composables/__tests__/useWorkflowHelpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
import router from '@/router';
import { createTestingPinia } from '@pinia/testing';
import { setActivePinia } from 'pinia';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useWorkflowsEEStore } from '@/stores/workflows.ee.store';
import { useTagsStore } from '@/stores/tags.store';
import { createTestWorkflow } from '@/__tests__/mocks';

const getDuplicateTestWorkflow = (): IWorkflowDataUpdate => ({
name: 'Duplicate webhook test',
Expand Down Expand Up @@ -50,32 +54,23 @@ const getDuplicateTestWorkflow = (): IWorkflowDataUpdate => ({
connections: {},
});

vi.mock('@/stores/workflows.store', () => ({
useWorkflowsStore: vi.fn(() => ({
workflowsById: {},
createNewWorkflow: vi.fn(() => {}),
addWorkflow: vi.fn(() => {}),
setActive: vi.fn(() => {}),
setWorkflowId: vi.fn(() => {}),
setWorkflowVersionId: vi.fn(() => {}),
setWorkflowName: vi.fn(() => {}),
setWorkflowSettings: vi.fn(() => {}),
setNodeValue: vi.fn(() => {}),
setWorkflowTagIds: vi.fn(() => {}),
getCurrentWorkflow: vi.fn(() => ({})),
})),
}));

describe('useWorkflowHelpers', () => {
describe('saveAsNewWorkflow', () => {
beforeAll(() => {
setActivePinia(createTestingPinia());
});
let workflowsStore: ReturnType<typeof useWorkflowsStore>;
let workflowsEEStore: ReturnType<typeof useWorkflowsEEStore>;
let tagsStore: ReturnType<typeof useTagsStore>;

afterEach(() => {
vi.clearAllMocks();
});
beforeAll(() => {
setActivePinia(createTestingPinia());
workflowsStore = useWorkflowsStore();
workflowsEEStore = useWorkflowsEEStore();
tagsStore = useTagsStore();
});

afterEach(() => {
vi.clearAllMocks();
});

describe('saveAsNewWorkflow', () => {
it('should respect `resetWebhookUrls: false` when duplicating workflows', async () => {
const workflow = getDuplicateTestWorkflow();
if (!workflow.nodes) {
Expand Down Expand Up @@ -120,4 +115,101 @@ describe('useWorkflowHelpers', () => {
expect(pathsPreSave).not.toEqual(pathsPostSave);
});
});

describe('initState', () => {
it('should initialize workflow state with provided data', () => {
const { initState } = useWorkflowHelpers({ router });

const workflowData = createTestWorkflow({
id: '1',
name: 'Test Workflow',
active: true,
pinData: {},
meta: {},
scopes: ['workflow:create'],
usedCredentials: [],
sharedWithProjects: [],
tags: [],
});
const addWorkflowSpy = vi.spyOn(workflowsStore, 'addWorkflow');
const setActiveSpy = vi.spyOn(workflowsStore, 'setActive');
const setWorkflowIdSpy = vi.spyOn(workflowsStore, 'setWorkflowId');
const setWorkflowNameSpy = vi.spyOn(workflowsStore, 'setWorkflowName');
const setWorkflowSettingsSpy = vi.spyOn(workflowsStore, 'setWorkflowSettings');
const setWorkflowPinDataSpy = vi.spyOn(workflowsStore, 'setWorkflowPinData');
const setWorkflowVersionIdSpy = vi.spyOn(workflowsStore, 'setWorkflowVersionId');
const setWorkflowMetadataSpy = vi.spyOn(workflowsStore, 'setWorkflowMetadata');
const setWorkflowScopesSpy = vi.spyOn(workflowsStore, 'setWorkflowScopes');
const setUsedCredentialsSpy = vi.spyOn(workflowsStore, 'setUsedCredentials');
const setWorkflowSharedWithSpy = vi.spyOn(workflowsEEStore, 'setWorkflowSharedWith');
const setWorkflowTagIdsSpy = vi.spyOn(workflowsStore, 'setWorkflowTagIds');
const upsertTagsSpy = vi.spyOn(tagsStore, 'upsertTags');

initState(workflowData);

expect(addWorkflowSpy).toHaveBeenCalledWith(workflowData);
expect(setActiveSpy).toHaveBeenCalledWith(true);
expect(setWorkflowIdSpy).toHaveBeenCalledWith('1');
expect(setWorkflowNameSpy).toHaveBeenCalledWith({
newName: 'Test Workflow',
setStateDirty: false,
});
expect(setWorkflowSettingsSpy).toHaveBeenCalledWith({
executionOrder: 'v1',
timezone: 'DEFAULT',
});
expect(setWorkflowPinDataSpy).toHaveBeenCalledWith({});
expect(setWorkflowVersionIdSpy).toHaveBeenCalledWith('1');
expect(setWorkflowMetadataSpy).toHaveBeenCalledWith({});
expect(setWorkflowScopesSpy).toHaveBeenCalledWith(['workflow:create']);
expect(setUsedCredentialsSpy).toHaveBeenCalledWith([]);
expect(setWorkflowSharedWithSpy).toHaveBeenCalledWith({
workflowId: '1',
sharedWithProjects: [],
});
expect(setWorkflowTagIdsSpy).toHaveBeenCalledWith([]);
expect(upsertTagsSpy).toHaveBeenCalledWith([]);
});

it('should handle missing `usedCredentials` and `sharedWithProjects` gracefully', () => {
const { initState } = useWorkflowHelpers({ router });

const workflowData = createTestWorkflow({
id: '1',
name: 'Test Workflow',
active: true,
pinData: {},
meta: {},
scopes: [],
tags: [],
});
const setUsedCredentialsSpy = vi.spyOn(workflowsStore, 'setUsedCredentials');
const setWorkflowSharedWithSpy = vi.spyOn(workflowsEEStore, 'setWorkflowSharedWith');

initState(workflowData);

expect(setUsedCredentialsSpy).not.toHaveBeenCalled();
expect(setWorkflowSharedWithSpy).not.toHaveBeenCalled();
});

it('should handle missing `tags` gracefully', () => {
const { initState } = useWorkflowHelpers({ router });

const workflowData = createTestWorkflow({
id: '1',
name: 'Test Workflow',
active: true,
pinData: {},
meta: {},
scopes: [],
});
const setWorkflowTagIdsSpy = vi.spyOn(workflowsStore, 'setWorkflowTagIds');
const upsertTagsSpy = vi.spyOn(tagsStore, 'upsertTags');

initState(workflowData);

expect(setWorkflowTagIdsSpy).toHaveBeenCalledWith([]);
expect(upsertTagsSpy).toHaveBeenCalledWith([]);
});
});
});
1 change: 1 addition & 0 deletions packages/editor-ui/src/composables/useWorkflowHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,7 @@ export function useWorkflowHelpers(options: { router: ReturnType<typeof useRoute
workflowsStore.setWorkflowPinData(workflowData.pinData ?? {});
workflowsStore.setWorkflowVersionId(workflowData.versionId);
workflowsStore.setWorkflowMetadata(workflowData.meta);
workflowsStore.setWorkflowScopes(workflowData.scopes);

if (workflowData.usedCredentials) {
workflowsStore.setUsedCredentials(workflowData.usedCredentials);
Expand Down
4 changes: 2 additions & 2 deletions packages/editor-ui/src/plugins/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,15 @@
"auth.signup.setupYourAccount": "Set up your account",
"auth.signup.setupYourAccountError": "Problem setting up your account",
"auth.signup.tokenValidationError": "Issue validating invite token",
"aiAssistant.name": "Ava",
"aiAssistant.name": "Assistant",
"aiAssistant.assistant": "AI Assistant",
"aiAssistant.newSessionModal.title.part1": "Start new",
"aiAssistant.newSessionModal.title.part2": "session",
"aiAssistant.newSessionModal.message": "You already have an active AI Assistant session. Starting a new session will clear your current conversation history.",
"aiAssistant.newSessionModal.question": "Are you sure you want to start a new session?",
"aiAssistant.newSessionModal.confirm": "Start new session",
"aiAssistant.serviceError.message": "Unable to connect to n8n's AI service",
"aiAssistant.codeUpdated.message.title": "Ava modified workflow",
"aiAssistant.codeUpdated.message.title": "Assistant modified workflow",
"aiAssistant.codeUpdated.message.body": "Open the <a data-action='openNodeDetail' data-action-parameter-node='{nodeName}'>{nodeName}</a> node to see the changes",
"banners.confirmEmail.message.1": "To secure your account and prevent future access issues, please confirm your",
"banners.confirmEmail.message.2": "email address.",
Expand Down
5 changes: 5 additions & 0 deletions packages/editor-ui/src/stores/workflows.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,10 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
};
}

function setWorkflowScopes(scopes: IWorkflowDb['scopes']): void {
workflow.value.scopes = scopes;
}

function setWorkflowMetadata(metadata: WorkflowMetadata | undefined): void {
workflow.value.meta = metadata;
}
Expand Down Expand Up @@ -1634,6 +1638,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
setWorkflowTagIds,
addWorkflowTagIds,
removeWorkflowTagId,
setWorkflowScopes,
setWorkflowMetadata,
addToWorkflowMetadata,
setWorkflow,
Expand Down
Loading

0 comments on commit 1611124

Please sign in to comment.