From b6c9bf2496c4d9f50d18e407838687c281ea2951 Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Fri, 28 Apr 2023 13:12:19 +0200 Subject: [PATCH 1/4] fix(editor): Remove duplicate mapping of `item.json` key in data pinning --- packages/editor-ui/src/stores/workflows.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor-ui/src/stores/workflows.ts b/packages/editor-ui/src/stores/workflows.ts index b6aa9a1b0f6fc..95522e93a9d7c 100644 --- a/packages/editor-ui/src/stores/workflows.ts +++ b/packages/editor-ui/src/stores/workflows.ts @@ -994,7 +994,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, { pinDataByNodeName(nodeName: string): INodeExecutionData[] | undefined { if (!this.workflow.pinData || !this.workflow.pinData[nodeName]) return undefined; - return this.workflow.pinData[nodeName].map((item) => item.json) as INodeExecutionData[]; + return this.workflow.pinData[nodeName]; }, activeNode(): INodeUi | null { From 515743ad9e78838931f8bbca33ca0edef52c10c4 Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Fri, 28 Apr 2023 16:23:21 +0200 Subject: [PATCH 2/4] fix(editor): Remove duplicate mapping of `item.json` key in data pinning --- packages/editor-ui/src/components/RunData.vue | 6 ++---- packages/editor-ui/src/stores/workflows.ts | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/editor-ui/src/components/RunData.vue b/packages/editor-ui/src/components/RunData.vue index 41b3291afb899..c012418a627af 100644 --- a/packages/editor-ui/src/components/RunData.vue +++ b/packages/editor-ui/src/components/RunData.vue @@ -1062,16 +1062,14 @@ export default mixins(externalHooks, genericHelpers, nodeHelpers, pinData).exten return; } - const data = executionDataToJson(this.rawInputData) as INodeExecutionData[]; - - if (!this.isValidPinDataSize(data)) { + if (!this.isValidPinDataSize(this.inputData)) { this.onDataPinningError({ errorType: 'data-too-large', source: 'pin-icon-click' }); return; } this.onDataPinningSuccess({ source: 'pin-icon-click' }); - this.workflowsStore.pinData({ node: this.node, data }); + this.workflowsStore.pinData({ node: this.node, this.inputData }); if (this.maxRunIndex > 0) { this.$showToast({ diff --git a/packages/editor-ui/src/stores/workflows.ts b/packages/editor-ui/src/stores/workflows.ts index 95522e93a9d7c..b6aa9a1b0f6fc 100644 --- a/packages/editor-ui/src/stores/workflows.ts +++ b/packages/editor-ui/src/stores/workflows.ts @@ -994,7 +994,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, { pinDataByNodeName(nodeName: string): INodeExecutionData[] | undefined { if (!this.workflow.pinData || !this.workflow.pinData[nodeName]) return undefined; - return this.workflow.pinData[nodeName]; + return this.workflow.pinData[nodeName].map((item) => item.json) as INodeExecutionData[]; }, activeNode(): INodeUi | null { From 9d81c49a173b16702661607ac0543eb12db5af0b Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Fri, 28 Apr 2023 16:26:16 +0200 Subject: [PATCH 3/4] fix(editor): Remove duplicate mapping of `item.json` key in data pinning --- packages/editor-ui/src/components/RunData.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor-ui/src/components/RunData.vue b/packages/editor-ui/src/components/RunData.vue index c012418a627af..8c61488ff4f62 100644 --- a/packages/editor-ui/src/components/RunData.vue +++ b/packages/editor-ui/src/components/RunData.vue @@ -1069,7 +1069,7 @@ export default mixins(externalHooks, genericHelpers, nodeHelpers, pinData).exten this.onDataPinningSuccess({ source: 'pin-icon-click' }); - this.workflowsStore.pinData({ node: this.node, this.inputData }); + this.workflowsStore.pinData({ node: this.node, data: this.inputData }); if (this.maxRunIndex > 0) { this.$showToast({ From 964ae360d924dd7e7e851c664eed199e734ad60c Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Wed, 3 May 2023 15:47:40 +0200 Subject: [PATCH 4/4] test(editor): Unit test the fix of duplicate mapping of `item.json` key in data pinning --- .../src/components/__tests__/RunData.test.ts | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 packages/editor-ui/src/components/__tests__/RunData.test.ts diff --git a/packages/editor-ui/src/components/__tests__/RunData.test.ts b/packages/editor-ui/src/components/__tests__/RunData.test.ts new file mode 100644 index 0000000000000..f6e16fb004750 --- /dev/null +++ b/packages/editor-ui/src/components/__tests__/RunData.test.ts @@ -0,0 +1,164 @@ +import type Vue from 'vue'; +import { defineComponent } from 'vue'; +import { PiniaVuePlugin } from 'pinia'; +import { render, waitFor } from '@testing-library/vue'; +import userEvent from '@testing-library/user-event'; +import { createTestingPinia } from '@pinia/testing'; +import { merge } from 'lodash-es'; +import RunData from '@/components/RunData.vue'; +import { STORES, VIEWS } from '@/constants'; +import { useSSOStore } from '@/stores/sso'; +import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils'; +import { externalHooks } from '@/mixins/externalHooks'; +import { genericHelpers } from '@/mixins/genericHelpers'; +import { pinData } from '@/mixins/pinData'; +import { useNDVStore, useWorkflowsStore } from '@/stores'; + +let pinia: ReturnType; +let ssoStore: ReturnType; +let workflowsStore: ReturnType; +let ndvStore: ReturnType; + +function TelemetryPlugin(vue: typeof Vue): void { + Object.defineProperty(vue, '$telemetry', { + get() { + return { + track: () => {}, + }; + }, + }); + Object.defineProperty(vue.prototype, '$telemetry', { + get() { + return { + track: () => {}, + }; + }, + }); +} + +const nodeHelpers = defineComponent({ + methods: { + getNodeInputData: vi.fn().mockReturnValue([ + { + json: { + id: 1, + name: 'Test 1', + json: { + data: 'Json data 1', + }, + }, + }, + { + json: { + id: 2, + name: 'Test 2', + json: { + data: 'Json data 2', + }, + }, + }, + ]), + }, +}); + +const renderComponent = (renderOptions: Parameters[1] = {}) => + render( + RunData, + merge( + { + pinia, + mocks: { + $route: { + name: VIEWS.WORKFLOW, + }, + }, + mixins: [externalHooks, genericHelpers, nodeHelpers, pinData], + }, + renderOptions, + ), + (vue) => { + vue.use(TelemetryPlugin); + vue.use(PiniaVuePlugin); + }, + ); + +describe('RunData', () => { + beforeEach(() => { + pinia = createTestingPinia({ + initialState: { + [STORES.SETTINGS]: { + settings: merge({}, SETTINGS_STORE_DEFAULT_STATE.settings), + }, + }, + }); + ssoStore = useSSOStore(); + workflowsStore = useWorkflowsStore(); + ndvStore = useNDVStore(); + + vi.spyOn(workflowsStore, 'getWorkflowExecution', 'get').mockReturnValue({ + id: '1', + finished: true, + mode: 'trigger', + startedAt: new Date(), + workflowData: { + id: '1', + name: 'Test Workflow', + versionId: '1', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + active: false, + nodes: [], + connections: {}, + }, + data: { + resultData: { + runData: { + 'Test Node': [ + { + startTime: new Date().getTime(), + executionTime: new Date().getTime(), + data: {}, + source: [null], + }, + ], + }, + }, + }, + }); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should render data correctly even when "item.json" has another "json" key', async () => { + vi.spyOn(ndvStore, 'getPanelDisplayMode').mockReturnValue('schema'); + vi.spyOn(ndvStore, 'activeNode', 'get').mockReturnValue({ + id: '1', + typeVersion: 1, + name: 'Test Node', + position: [0, 0], + type: 'test', + parameters: {}, + }); + + const { getByText, getAllByTestId, getByTestId } = renderComponent({ + props: { + nodeUi: { + name: 'Test Node', + position: [0, 0], + }, + runIndex: 0, + paneType: 'output', + isExecuting: false, + mappingEnabled: true, + distanceFromActive: 0, + }, + }); + + await userEvent.click(getByTestId('ndv-pin-data')); + await waitFor(() => getAllByTestId('run-data-schema-item')); + expect(getByText('Test 1')).toBeInTheDocument(); + expect(getByText('Json data 1')).toBeInTheDocument(); + }); +});