mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-19 11:01:15 +00:00
fix(editor): Render HTML in the log view (#17586)
This commit is contained in:
@@ -130,14 +130,18 @@ export const defaultNodeDescriptions = Object.values(defaultNodeTypes).map(
|
||||
({ type }) => type.description,
|
||||
) as INodeTypeDescription[];
|
||||
|
||||
const nodeTypes = mock<INodeTypes>({
|
||||
getByName(nodeType) {
|
||||
return defaultNodeTypes[nodeType].type;
|
||||
},
|
||||
getByNameAndVersion(nodeType: string, version?: number): INodeType {
|
||||
return NodeHelpers.getVersionedNodeType(defaultNodeTypes[nodeType].type, version);
|
||||
},
|
||||
});
|
||||
export function createMockNodeTypes(data: INodeTypeData) {
|
||||
return mock<INodeTypes>({
|
||||
getByName(nodeType) {
|
||||
return data[nodeType].type;
|
||||
},
|
||||
getByNameAndVersion(nodeType: string, version?: number): INodeType {
|
||||
return NodeHelpers.getVersionedNodeType(data[nodeType].type, version);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const nodeTypes = createMockNodeTypes(defaultNodeTypes);
|
||||
|
||||
export function createTestWorkflowObject({
|
||||
id = uuid(),
|
||||
@@ -148,6 +152,7 @@ export function createTestWorkflowObject({
|
||||
staticData = {},
|
||||
settings = {},
|
||||
pinData = {},
|
||||
...rest
|
||||
}: {
|
||||
id?: string;
|
||||
name?: string;
|
||||
@@ -157,6 +162,7 @@ export function createTestWorkflowObject({
|
||||
staticData?: IDataObject;
|
||||
settings?: IWorkflowSettings;
|
||||
pinData?: IPinData;
|
||||
nodeTypes?: INodeTypes;
|
||||
} = {}) {
|
||||
return new Workflow({
|
||||
id,
|
||||
@@ -167,7 +173,7 @@ export function createTestWorkflowObject({
|
||||
staticData,
|
||||
settings,
|
||||
pinData,
|
||||
nodeTypes,
|
||||
nodeTypes: rest.nodeTypes ?? nodeTypes,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -112,3 +112,8 @@ Object.defineProperty(HTMLCanvasElement.prototype, 'getContext', {
|
||||
writable: true,
|
||||
value: vi.fn(),
|
||||
});
|
||||
|
||||
Object.defineProperty(HTMLElement.prototype, 'scrollTo', {
|
||||
writable: true,
|
||||
value: vi.fn(),
|
||||
});
|
||||
|
||||
@@ -721,7 +721,6 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
if (props.paneType === 'output') {
|
||||
setDisplayMode();
|
||||
activatePane();
|
||||
}
|
||||
|
||||
@@ -1231,6 +1230,10 @@ function init() {
|
||||
if (isNDVV2.value) {
|
||||
pageSize.value = RUN_DATA_DEFAULT_PAGE_SIZE;
|
||||
}
|
||||
|
||||
if (props.paneType === 'output') {
|
||||
setDisplayMode();
|
||||
}
|
||||
}
|
||||
|
||||
function closeBinaryDataDisplay() {
|
||||
@@ -1337,14 +1340,14 @@ function enableNode() {
|
||||
}
|
||||
}
|
||||
|
||||
const shouldDisplayHtml = computed(
|
||||
() =>
|
||||
node.value?.type === HTML_NODE_TYPE &&
|
||||
node.value.parameters.operation === 'generateHtmlTemplate',
|
||||
);
|
||||
|
||||
function setDisplayMode() {
|
||||
if (!activeNode.value) return;
|
||||
|
||||
const shouldDisplayHtml =
|
||||
activeNode.value.type === HTML_NODE_TYPE &&
|
||||
activeNode.value.parameters.operation === 'generateHtmlTemplate';
|
||||
|
||||
if (shouldDisplayHtml) {
|
||||
if (shouldDisplayHtml.value) {
|
||||
emit('displayModeChange', 'html');
|
||||
}
|
||||
}
|
||||
@@ -1461,10 +1464,7 @@ defineExpose({ enterEditMode });
|
||||
:value="displayMode"
|
||||
:has-binary-data="binaryData.length > 0"
|
||||
:pane-type="paneType"
|
||||
:node-generates-html="
|
||||
activeNode?.type === HTML_NODE_TYPE &&
|
||||
activeNode.parameters.operation === 'generateHtmlTemplate'
|
||||
"
|
||||
:node-generates-html="shouldDisplayHtml"
|
||||
:has-renderable-data="hasParsedAiContent"
|
||||
@change="onDisplayModeChange"
|
||||
/>
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import { fireEvent, within } from '@testing-library/vue';
|
||||
import { fireEvent, waitFor, within } from '@testing-library/vue';
|
||||
import { renderComponent } from '@/__tests__/render';
|
||||
import LogDetailsPanel from './LogDetailsPanel.vue';
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import { createTestingPinia, type TestingPinia } from '@pinia/testing';
|
||||
import { h } from 'vue';
|
||||
import {
|
||||
createMockNodeTypes,
|
||||
createTestNode,
|
||||
createTestTaskData,
|
||||
createTestWorkflow,
|
||||
createTestWorkflowObject,
|
||||
defaultNodeTypes,
|
||||
mockLoadedNodeType,
|
||||
} from '@/__tests__/mocks';
|
||||
import { LOG_DETAILS_PANEL_STATE } from '@/features/logs/logs.constants';
|
||||
import type { LogEntry } from '../logs.types';
|
||||
import { createTestLogEntry } from '../__test__/mocks';
|
||||
import { NodeConnectionTypes } from 'n8n-workflow';
|
||||
import { HTML_NODE_TYPE } from '@/constants';
|
||||
|
||||
describe('LogDetailsPanel', () => {
|
||||
let pinia: TestingPinia;
|
||||
@@ -182,4 +186,47 @@ describe('LogDetailsPanel', () => {
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render output data in HTML mode for HTML node', async () => {
|
||||
const nodeA = createTestNode({ name: 'A' });
|
||||
const nodeB = createTestNode({
|
||||
name: 'B',
|
||||
type: HTML_NODE_TYPE,
|
||||
});
|
||||
const runDataA = createTestTaskData({ data: { [NodeConnectionTypes.Main]: [[{ json: {} }]] } });
|
||||
const runDataB = createTestTaskData({
|
||||
data: { [NodeConnectionTypes.Main]: [[{ json: { html: '<h1>Hi!</h1>' } }]] },
|
||||
source: [{ previousNode: 'A' }],
|
||||
});
|
||||
const workflow = createTestWorkflowObject({
|
||||
nodes: [nodeA, nodeB],
|
||||
nodeTypes: createMockNodeTypes({
|
||||
...defaultNodeTypes,
|
||||
[HTML_NODE_TYPE]: mockLoadedNodeType(HTML_NODE_TYPE),
|
||||
}),
|
||||
});
|
||||
const execution = { resultData: { runData: { A: [runDataA], B: [runDataB] } } };
|
||||
const logA = createLogEntry({ node: nodeA, runData: runDataA, workflow, execution });
|
||||
const logB = createLogEntry({ node: nodeB, runData: runDataB, workflow, execution });
|
||||
|
||||
// HACK: Setting parameters after creating workflow because validation removes parameters that are not define in node types.
|
||||
nodeB.parameters = { operation: 'generateHtmlTemplate' };
|
||||
|
||||
const props = {
|
||||
isOpen: true,
|
||||
panels: LOG_DETAILS_PANEL_STATE.BOTH,
|
||||
collapsingInputTableColumnName: null,
|
||||
collapsingOutputTableColumnName: null,
|
||||
};
|
||||
|
||||
const rendered = render({ ...props, logEntry: logB });
|
||||
|
||||
await waitFor(() => expect(rendered.container.querySelectorAll('iframe')).toHaveLength(1));
|
||||
await rendered.rerender({ ...props, logEntry: logA });
|
||||
await waitFor(() => expect(rendered.container.querySelectorAll('iframe')).toHaveLength(0));
|
||||
|
||||
// Re-selecting node B should render HTML again
|
||||
await rendered.rerender({ ...props, logEntry: logB });
|
||||
await waitFor(() => expect(rendered.container.querySelectorAll('iframe')).toHaveLength(1));
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user