mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
feat(editor): Add telemetry for schema preview (no-changelog) (#13901)
This commit is contained in:
@@ -31,6 +31,7 @@ const mockNode1 = createTestNode({
|
|||||||
type: MANUAL_TRIGGER_NODE_TYPE,
|
type: MANUAL_TRIGGER_NODE_TYPE,
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
credentials: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mockNode2 = createTestNode({
|
const mockNode2 = createTestNode({
|
||||||
@@ -419,40 +420,111 @@ describe('VirtualSchema.vue', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle drop event', async () => {
|
describe('telemetry', () => {
|
||||||
const ndvStore = useNDVStore();
|
function dragDropPill(pill: HTMLElement) {
|
||||||
useWorkflowsStore().pinData({
|
const ndvStore = useNDVStore();
|
||||||
node: mockNode1,
|
const reset = vi.spyOn(ndvStore, 'resetMappingTelemetry');
|
||||||
data: [{ json: { name: 'John', age: 22, hobbies: ['surfing', 'traveling'] } }],
|
fireEvent(pill, new MouseEvent('mousedown', { bubbles: true }));
|
||||||
|
fireEvent(window, new MouseEvent('mousemove', { bubbles: true }));
|
||||||
|
expect(reset).toHaveBeenCalled();
|
||||||
|
|
||||||
|
vi.useFakeTimers({ toFake: ['setTimeout'] });
|
||||||
|
fireEvent(window, new MouseEvent('mouseup', { bubbles: true }));
|
||||||
|
vi.advanceTimersByTime(250);
|
||||||
|
vi.useRealTimers();
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should track data pill drag and drop', async () => {
|
||||||
|
useWorkflowsStore().pinData({
|
||||||
|
node: mockNode1,
|
||||||
|
data: [{ json: { name: 'John', age: 22, hobbies: ['surfing', 'traveling'] } }],
|
||||||
|
});
|
||||||
|
const telemetry = useTelemetry();
|
||||||
|
const trackSpy = vi.spyOn(telemetry, 'track');
|
||||||
|
|
||||||
|
const { getAllByTestId } = renderComponent();
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(getAllByTestId('run-data-schema-item')).toHaveLength(6);
|
||||||
|
});
|
||||||
|
const items = getAllByTestId('run-data-schema-item');
|
||||||
|
|
||||||
|
expect(items[0].className).toBe('schema-item draggable');
|
||||||
|
expect(items[0]).toHaveTextContent('nameJohn');
|
||||||
|
|
||||||
|
const pill = items[0].querySelector('.pill') as HTMLElement;
|
||||||
|
dragDropPill(pill);
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(trackSpy).toHaveBeenCalledWith(
|
||||||
|
'User dragged data for mapping',
|
||||||
|
expect.objectContaining({
|
||||||
|
src_view: 'schema',
|
||||||
|
src_field_name: 'name',
|
||||||
|
src_field_nest_level: 0,
|
||||||
|
src_node_type: 'n8n-nodes-base.manualTrigger',
|
||||||
|
src_nodes_back: '1',
|
||||||
|
src_has_credential: false,
|
||||||
|
}),
|
||||||
|
{ withPostHog: true },
|
||||||
|
),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
const telemetry = useTelemetry();
|
|
||||||
const trackSpy = vi.spyOn(telemetry, 'track');
|
|
||||||
const reset = vi.spyOn(ndvStore, 'resetMappingTelemetry');
|
|
||||||
const { getAllByTestId } = renderComponent();
|
|
||||||
|
|
||||||
await waitFor(() => {
|
it('should track data pill drag and drop for schema preview', async () => {
|
||||||
expect(getAllByTestId('run-data-schema-item')).toHaveLength(6);
|
useWorkflowsStore().pinData({
|
||||||
|
node: {
|
||||||
|
...mockNode2,
|
||||||
|
credentials: { myCredential: { id: 'myCredential', name: 'myCredential' } },
|
||||||
|
},
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const telemetry = useTelemetry();
|
||||||
|
const trackSpy = vi.spyOn(telemetry, 'track');
|
||||||
|
const posthogStore = usePostHog();
|
||||||
|
|
||||||
|
vi.spyOn(posthogStore, 'isFeatureEnabled').mockReturnValue(true);
|
||||||
|
const schemaPreviewStore = useSchemaPreviewStore();
|
||||||
|
vi.spyOn(schemaPreviewStore, 'getSchemaPreview').mockResolvedValue(
|
||||||
|
createResultOk({
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
account: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const { getAllByTestId } = renderComponent({
|
||||||
|
props: {
|
||||||
|
nodes: [{ name: mockNode2.name, indicies: [], depth: 1 }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(getAllByTestId('run-data-schema-item')).toHaveLength(2);
|
||||||
|
});
|
||||||
|
const pill = getAllByTestId('run-data-schema-item')[0].querySelector('.pill') as HTMLElement;
|
||||||
|
dragDropPill(pill);
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(trackSpy).toHaveBeenCalledWith(
|
||||||
|
'User dragged data for mapping',
|
||||||
|
expect.objectContaining({
|
||||||
|
src_view: 'schema_preview',
|
||||||
|
src_has_credential: true,
|
||||||
|
}),
|
||||||
|
{ withPostHog: true },
|
||||||
|
),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
const items = getAllByTestId('run-data-schema-item');
|
|
||||||
|
|
||||||
expect(items[0].className).toBe('schema-item draggable');
|
|
||||||
expect(items[0]).toHaveTextContent('nameJohn');
|
|
||||||
|
|
||||||
const pill = items[0].querySelector('.pill') as Element;
|
|
||||||
|
|
||||||
fireEvent(pill, new MouseEvent('mousedown', { bubbles: true }));
|
|
||||||
fireEvent(window, new MouseEvent('mousemove', { bubbles: true }));
|
|
||||||
expect(reset).toHaveBeenCalled();
|
|
||||||
|
|
||||||
fireEvent(window, new MouseEvent('mouseup', { bubbles: true }));
|
|
||||||
|
|
||||||
await waitFor(() =>
|
|
||||||
expect(trackSpy).toHaveBeenCalledWith(
|
|
||||||
'User dragged data for mapping',
|
|
||||||
expect.any(Object),
|
|
||||||
expect.any(Object),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should expand all nodes when searching', async () => {
|
it('should expand all nodes when searching', async () => {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import { useSchemaPreviewStore } from '@/stores/schemaPreview.store';
|
|||||||
import { asyncComputed } from '@vueuse/core';
|
import { asyncComputed } from '@vueuse/core';
|
||||||
import { usePostHog } from '@/stores/posthog.store';
|
import { usePostHog } from '@/stores/posthog.store';
|
||||||
import { SCHEMA_PREVIEW_EXPERIMENT } from '@/constants';
|
import { SCHEMA_PREVIEW_EXPERIMENT } from '@/constants';
|
||||||
|
import { isEmpty } from '@/utils/typesUtils';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
nodes?: IConnectedNode[];
|
nodes?: IConnectedNode[];
|
||||||
@@ -244,6 +245,11 @@ const onDragStart = () => {
|
|||||||
const onDragEnd = (el: HTMLElement) => {
|
const onDragEnd = (el: HTMLElement) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const mappingTelemetry = ndvStore.mappingTelemetry;
|
const mappingTelemetry = ndvStore.mappingTelemetry;
|
||||||
|
const parentNode = nodesSchemas.value.find(({ node }) => node.name === el.dataset.nodeName);
|
||||||
|
|
||||||
|
const isPreview = parentNode?.preview ?? false;
|
||||||
|
const hasCredential = !isEmpty(parentNode?.node.credentials);
|
||||||
|
|
||||||
const telemetryPayload = {
|
const telemetryPayload = {
|
||||||
src_node_type: el.dataset.nodeType,
|
src_node_type: el.dataset.nodeType,
|
||||||
src_field_name: el.dataset.name ?? '',
|
src_field_name: el.dataset.name ?? '',
|
||||||
@@ -251,7 +257,8 @@ const onDragEnd = (el: HTMLElement) => {
|
|||||||
src_run_index: props.runIndex,
|
src_run_index: props.runIndex,
|
||||||
src_runs_total: props.totalRuns,
|
src_runs_total: props.totalRuns,
|
||||||
src_field_nest_level: el.dataset.level ?? 0,
|
src_field_nest_level: el.dataset.level ?? 0,
|
||||||
src_view: 'schema',
|
src_view: isPreview ? 'schema_preview' : 'schema',
|
||||||
|
src_has_credential: hasCredential,
|
||||||
src_element: el,
|
src_element: el,
|
||||||
success: false,
|
success: false,
|
||||||
...mappingTelemetry,
|
...mappingTelemetry,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ type Props = {
|
|||||||
id: string;
|
id: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
collapsable?: boolean;
|
collapsable?: boolean;
|
||||||
|
nodeName?: string;
|
||||||
nodeType?: string;
|
nodeType?: string;
|
||||||
highlight?: boolean;
|
highlight?: boolean;
|
||||||
draggable?: boolean;
|
draggable?: boolean;
|
||||||
@@ -21,6 +22,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
click: [];
|
click: [];
|
||||||
}>();
|
}>();
|
||||||
@@ -41,6 +43,7 @@ const emit = defineEmits<{
|
|||||||
:data-nest-level="level"
|
:data-nest-level="level"
|
||||||
:data-value="expression"
|
:data-value="expression"
|
||||||
:data-node-type="nodeType"
|
:data-node-type="nodeType"
|
||||||
|
:data-node-name="nodeName"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
class="pill"
|
class="pill"
|
||||||
:class="{
|
:class="{
|
||||||
|
|||||||
@@ -200,6 +200,7 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
|
|||||||
data-depth="1"
|
data-depth="1"
|
||||||
data-name="account"
|
data-name="account"
|
||||||
data-nest-level="1"
|
data-nest-level="1"
|
||||||
|
data-node-name="Manual Trigger"
|
||||||
data-node-type="n8n-nodes-base.manualTrigger"
|
data-node-type="n8n-nodes-base.manualTrigger"
|
||||||
data-path=".account"
|
data-path=".account"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
@@ -272,6 +273,7 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
|
|||||||
data-depth="1"
|
data-depth="1"
|
||||||
data-name="id"
|
data-name="id"
|
||||||
data-nest-level="2"
|
data-nest-level="2"
|
||||||
|
data-node-name="Manual Trigger"
|
||||||
data-node-type="n8n-nodes-base.manualTrigger"
|
data-node-type="n8n-nodes-base.manualTrigger"
|
||||||
data-path=".account.id"
|
data-path=".account.id"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
@@ -525,6 +527,7 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
|
|||||||
data-depth="2"
|
data-depth="2"
|
||||||
data-name="account"
|
data-name="account"
|
||||||
data-nest-level="1"
|
data-nest-level="1"
|
||||||
|
data-node-name="Set2"
|
||||||
data-node-type="n8n-nodes-base.set"
|
data-node-type="n8n-nodes-base.set"
|
||||||
data-path=".account"
|
data-path=".account"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
@@ -597,6 +600,7 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
|
|||||||
data-depth="2"
|
data-depth="2"
|
||||||
data-name="id"
|
data-name="id"
|
||||||
data-nest-level="2"
|
data-nest-level="2"
|
||||||
|
data-node-name="Set2"
|
||||||
data-node-type="n8n-nodes-base.set"
|
data-node-type="n8n-nodes-base.set"
|
||||||
data-path=".account.id"
|
data-path=".account.id"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
@@ -882,6 +886,7 @@ exports[`VirtualSchema.vue > renders schema in output pane 1`] = `
|
|||||||
data-depth="0"
|
data-depth="0"
|
||||||
data-name="name"
|
data-name="name"
|
||||||
data-nest-level="0"
|
data-nest-level="0"
|
||||||
|
data-node-name=""
|
||||||
data-node-type=""
|
data-node-type=""
|
||||||
data-path=".name"
|
data-path=".name"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
@@ -959,6 +964,7 @@ exports[`VirtualSchema.vue > renders schema in output pane 1`] = `
|
|||||||
data-depth="0"
|
data-depth="0"
|
||||||
data-name="age"
|
data-name="age"
|
||||||
data-nest-level="0"
|
data-nest-level="0"
|
||||||
|
data-node-name=""
|
||||||
data-node-type=""
|
data-node-type=""
|
||||||
data-path=".age"
|
data-path=".age"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
@@ -1062,6 +1068,7 @@ exports[`VirtualSchema.vue > renders schema in output pane 1`] = `
|
|||||||
data-depth="0"
|
data-depth="0"
|
||||||
data-name="hobbies"
|
data-name="hobbies"
|
||||||
data-nest-level="0"
|
data-nest-level="0"
|
||||||
|
data-node-name=""
|
||||||
data-node-type=""
|
data-node-type=""
|
||||||
data-path=".hobbies"
|
data-path=".hobbies"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
@@ -1134,6 +1141,7 @@ exports[`VirtualSchema.vue > renders schema in output pane 1`] = `
|
|||||||
data-depth="0"
|
data-depth="0"
|
||||||
data-name="hobbies[0]"
|
data-name="hobbies[0]"
|
||||||
data-nest-level="1"
|
data-nest-level="1"
|
||||||
|
data-node-name=""
|
||||||
data-node-type=""
|
data-node-type=""
|
||||||
data-path=".hobbies[0]"
|
data-path=".hobbies[0]"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
@@ -1211,6 +1219,7 @@ exports[`VirtualSchema.vue > renders schema in output pane 1`] = `
|
|||||||
data-depth="0"
|
data-depth="0"
|
||||||
data-name="hobbies[1]"
|
data-name="hobbies[1]"
|
||||||
data-nest-level="1"
|
data-nest-level="1"
|
||||||
|
data-node-name=""
|
||||||
data-node-type=""
|
data-node-type=""
|
||||||
data-path=".hobbies[1]"
|
data-path=".hobbies[1]"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
@@ -1446,6 +1455,7 @@ exports[`VirtualSchema.vue > renders schema with spaces and dots 1`] = `
|
|||||||
data-depth="1"
|
data-depth="1"
|
||||||
data-name="hello world"
|
data-name="hello world"
|
||||||
data-nest-level="1"
|
data-nest-level="1"
|
||||||
|
data-node-name="Manual Trigger"
|
||||||
data-node-type="n8n-nodes-base.manualTrigger"
|
data-node-type="n8n-nodes-base.manualTrigger"
|
||||||
data-path="['hello world']"
|
data-path="['hello world']"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
@@ -1544,6 +1554,7 @@ exports[`VirtualSchema.vue > renders schema with spaces and dots 1`] = `
|
|||||||
data-depth="1"
|
data-depth="1"
|
||||||
data-name="hello world[0]"
|
data-name="hello world[0]"
|
||||||
data-nest-level="2"
|
data-nest-level="2"
|
||||||
|
data-node-name="Manual Trigger"
|
||||||
data-node-type="n8n-nodes-base.manualTrigger"
|
data-node-type="n8n-nodes-base.manualTrigger"
|
||||||
data-path="['hello world'][0]"
|
data-path="['hello world'][0]"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
@@ -1642,6 +1653,7 @@ exports[`VirtualSchema.vue > renders schema with spaces and dots 1`] = `
|
|||||||
data-depth="1"
|
data-depth="1"
|
||||||
data-name="test"
|
data-name="test"
|
||||||
data-nest-level="3"
|
data-nest-level="3"
|
||||||
|
data-node-name="Manual Trigger"
|
||||||
data-node-type="n8n-nodes-base.manualTrigger"
|
data-node-type="n8n-nodes-base.manualTrigger"
|
||||||
data-path="['hello world'][0].test"
|
data-path="['hello world'][0].test"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
@@ -1714,6 +1726,7 @@ exports[`VirtualSchema.vue > renders schema with spaces and dots 1`] = `
|
|||||||
data-depth="1"
|
data-depth="1"
|
||||||
data-name="more to think about"
|
data-name="more to think about"
|
||||||
data-nest-level="4"
|
data-nest-level="4"
|
||||||
|
data-node-name="Manual Trigger"
|
||||||
data-node-type="n8n-nodes-base.manualTrigger"
|
data-node-type="n8n-nodes-base.manualTrigger"
|
||||||
data-path="['hello world'][0].test['more to think about']"
|
data-path="['hello world'][0].test['more to think about']"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
@@ -1791,6 +1804,7 @@ exports[`VirtualSchema.vue > renders schema with spaces and dots 1`] = `
|
|||||||
data-depth="1"
|
data-depth="1"
|
||||||
data-name="test.how"
|
data-name="test.how"
|
||||||
data-nest-level="3"
|
data-nest-level="3"
|
||||||
|
data-node-name="Manual Trigger"
|
||||||
data-node-type="n8n-nodes-base.manualTrigger"
|
data-node-type="n8n-nodes-base.manualTrigger"
|
||||||
data-path="['hello world'][0]['test.how']"
|
data-path="['hello world'][0]['test.how']"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ export type RenderItem = {
|
|||||||
id: string;
|
id: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
collapsable?: boolean;
|
collapsable?: boolean;
|
||||||
|
nodeName?: string;
|
||||||
nodeType?: INodeUi['type'];
|
nodeType?: INodeUi['type'];
|
||||||
preview?: boolean;
|
preview?: boolean;
|
||||||
type: 'item';
|
type: 'item';
|
||||||
@@ -371,6 +372,7 @@ export const useFlattenSchema = () => {
|
|||||||
icon: getIconBySchemaType(schema.type),
|
icon: getIconBySchemaType(schema.type),
|
||||||
id,
|
id,
|
||||||
collapsable: true,
|
collapsable: true,
|
||||||
|
nodeName: node.name,
|
||||||
nodeType: node.type,
|
nodeType: node.type,
|
||||||
type: 'item',
|
type: 'item',
|
||||||
preview,
|
preview,
|
||||||
@@ -409,6 +411,7 @@ export const useFlattenSchema = () => {
|
|||||||
icon: getIconBySchemaType(schema.type),
|
icon: getIconBySchemaType(schema.type),
|
||||||
collapsable: false,
|
collapsable: false,
|
||||||
nodeType: node.type,
|
nodeType: node.type,
|
||||||
|
nodeName: node.name,
|
||||||
type: 'item',
|
type: 'item',
|
||||||
preview,
|
preview,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user