mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
refactor/add tests
This commit is contained in:
parent
e661444272
commit
17a14d84ef
|
@ -8,10 +8,14 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import { createTestingPinia } from '@pinia/testing';
|
import { createTestingPinia } from '@pinia/testing';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { waitFor } from '@testing-library/vue';
|
import { waitFor } from '@testing-library/vue';
|
||||||
import type { INodeExecutionData } from 'n8n-workflow';
|
import type { INodeExecutionData, ITaskData, ITaskMetadata } from 'n8n-workflow';
|
||||||
import { setActivePinia } from 'pinia';
|
import { setActivePinia } from 'pinia';
|
||||||
import { useNodeTypesStore } from '../stores/nodeTypes.store';
|
import { useNodeTypesStore } from '../stores/nodeTypes.store';
|
||||||
|
|
||||||
|
const { openRelatedExecution } = vi.hoisted(() => ({
|
||||||
|
openRelatedExecution: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
vi.mock('vue-router', () => {
|
vi.mock('vue-router', () => {
|
||||||
return {
|
return {
|
||||||
useRouter: () => ({}),
|
useRouter: () => ({}),
|
||||||
|
@ -20,6 +24,12 @@ vi.mock('vue-router', () => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
vi.mock('@/composables/useExecutionHelpers', () => ({
|
||||||
|
useExecutionHelpers: () => ({
|
||||||
|
openRelatedExecution,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
const nodes = [
|
const nodes = [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
|
@ -33,49 +43,50 @@ const nodes = [
|
||||||
|
|
||||||
describe('RunData', () => {
|
describe('RunData', () => {
|
||||||
it("should render pin button in output panel disabled when there's binary data", () => {
|
it("should render pin button in output panel disabled when there's binary data", () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId } = render({
|
||||||
[
|
defaultRunItems: [
|
||||||
{
|
{
|
||||||
json: {},
|
json: {},
|
||||||
binary: {
|
binary: {
|
||||||
data: {
|
data: {
|
||||||
fileName: 'test.xyz',
|
fileName: 'test.xyz',
|
||||||
mimeType: 'application/octet-stream',
|
mimeType: 'application/octet-stream',
|
||||||
|
data: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'binary',
|
displayMode: 'binary',
|
||||||
);
|
});
|
||||||
|
|
||||||
expect(getByTestId('ndv-pin-data')).toBeInTheDocument();
|
expect(getByTestId('ndv-pin-data')).toBeInTheDocument();
|
||||||
expect(getByTestId('ndv-pin-data')).toHaveAttribute('disabled');
|
expect(getByTestId('ndv-pin-data')).toHaveAttribute('disabled');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not render pin button in input panel when there's binary data", () => {
|
it("should not render pin button in input panel when there's binary data", () => {
|
||||||
const { queryByTestId } = render(
|
const { queryByTestId } = render({
|
||||||
[
|
defaultRunItems: [
|
||||||
{
|
{
|
||||||
json: {},
|
json: {},
|
||||||
binary: {
|
binary: {
|
||||||
data: {
|
data: {
|
||||||
fileName: 'test.xyz',
|
fileName: 'test.xyz',
|
||||||
mimeType: 'application/octet-stream',
|
mimeType: 'application/octet-stream',
|
||||||
|
data: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'binary',
|
displayMode: 'binary',
|
||||||
undefined,
|
paneType: 'input',
|
||||||
'input',
|
});
|
||||||
);
|
|
||||||
|
|
||||||
expect(queryByTestId('ndv-pin-data')).not.toBeInTheDocument();
|
expect(queryByTestId('ndv-pin-data')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render data correctly even when "item.json" has another "json" key', async () => {
|
it('should render data correctly even when "item.json" has another "json" key', async () => {
|
||||||
const { getByText, getAllByTestId, getByTestId } = render(
|
const { getByText, getAllByTestId, getByTestId } = render({
|
||||||
[
|
defaultRunItems: [
|
||||||
{
|
{
|
||||||
json: {
|
json: {
|
||||||
id: 1,
|
id: 1,
|
||||||
|
@ -95,8 +106,8 @@ describe('RunData', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'schema',
|
displayMode: 'schema',
|
||||||
);
|
});
|
||||||
|
|
||||||
await userEvent.click(getByTestId('ndv-pin-data'));
|
await userEvent.click(getByTestId('ndv-pin-data'));
|
||||||
await waitFor(() => getAllByTestId('run-data-schema-item'), { timeout: 1000 });
|
await waitFor(() => getAllByTestId('run-data-schema-item'), { timeout: 1000 });
|
||||||
|
@ -105,8 +116,8 @@ describe('RunData', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render view and download buttons for PDFs', async () => {
|
it('should render view and download buttons for PDFs', async () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId } = render({
|
||||||
[
|
defaultRunItems: [
|
||||||
{
|
{
|
||||||
json: {},
|
json: {},
|
||||||
binary: {
|
binary: {
|
||||||
|
@ -118,8 +129,8 @@ describe('RunData', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'binary',
|
displayMode: 'binary',
|
||||||
);
|
});
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(getByTestId('ndv-view-binary-data')).toBeInTheDocument();
|
expect(getByTestId('ndv-view-binary-data')).toBeInTheDocument();
|
||||||
|
@ -129,20 +140,21 @@ describe('RunData', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not render a view button for unknown content-type', async () => {
|
it('should not render a view button for unknown content-type', async () => {
|
||||||
const { getByTestId, queryByTestId } = render(
|
const { getByTestId, queryByTestId } = render({
|
||||||
[
|
defaultRunItems: [
|
||||||
{
|
{
|
||||||
json: {},
|
json: {},
|
||||||
binary: {
|
binary: {
|
||||||
data: {
|
data: {
|
||||||
fileName: 'test.xyz',
|
fileName: 'test.xyz',
|
||||||
mimeType: 'application/octet-stream',
|
mimeType: 'application/octet-stream',
|
||||||
|
data: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'binary',
|
displayMode: 'binary',
|
||||||
);
|
});
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(queryByTestId('ndv-view-binary-data')).not.toBeInTheDocument();
|
expect(queryByTestId('ndv-view-binary-data')).not.toBeInTheDocument();
|
||||||
|
@ -152,25 +164,32 @@ describe('RunData', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not render pin data button when there is no output data', async () => {
|
it('should not render pin data button when there is no output data', async () => {
|
||||||
const { queryByTestId } = render([], 'table');
|
const { queryByTestId } = render({ defaultRunItems: [], displayMode: 'table' });
|
||||||
expect(queryByTestId('ndv-pin-data')).not.toBeInTheDocument();
|
expect(queryByTestId('ndv-pin-data')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should disable pin data button when data is pinned', async () => {
|
it('should disable pin data button when data is pinned', async () => {
|
||||||
const { getByTestId } = render([], 'table', [{ json: { name: 'Test' } }]);
|
const { getByTestId } = render({
|
||||||
|
defaultRunItems: [],
|
||||||
|
displayMode: 'table',
|
||||||
|
pinnedData: [{ json: { name: 'Test' } }],
|
||||||
|
});
|
||||||
const pinDataButton = getByTestId('ndv-pin-data');
|
const pinDataButton = getByTestId('ndv-pin-data');
|
||||||
expect(pinDataButton).toBeDisabled();
|
expect(pinDataButton).toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should enable pin data button when data is not pinned', async () => {
|
it('should enable pin data button when data is not pinned', async () => {
|
||||||
const { getByTestId } = render([{ json: { name: 'Test' } }], 'table');
|
const { getByTestId } = render({
|
||||||
|
defaultRunItems: [{ json: { name: 'Test' } }],
|
||||||
|
displayMode: 'table',
|
||||||
|
});
|
||||||
const pinDataButton = getByTestId('ndv-pin-data');
|
const pinDataButton = getByTestId('ndv-pin-data');
|
||||||
expect(pinDataButton).toBeEnabled();
|
expect(pinDataButton).toBeEnabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not render pagination on binary tab', async () => {
|
it('should not render pagination on binary tab', async () => {
|
||||||
const { queryByTestId } = render(
|
const { queryByTestId } = render({
|
||||||
Array.from({ length: 11 }).map((_, i) => ({
|
defaultRunItems: Array.from({ length: 11 }).map((_, i) => ({
|
||||||
json: {
|
json: {
|
||||||
data: {
|
data: {
|
||||||
id: i,
|
id: i,
|
||||||
|
@ -180,17 +199,19 @@ describe('RunData', () => {
|
||||||
binary: {
|
binary: {
|
||||||
data: {
|
data: {
|
||||||
a: 'b',
|
a: 'b',
|
||||||
|
data: '',
|
||||||
|
mimeType: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
'binary',
|
displayMode: 'binary',
|
||||||
);
|
});
|
||||||
expect(queryByTestId('ndv-data-pagination')).not.toBeInTheDocument();
|
expect(queryByTestId('ndv-data-pagination')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render pagination with binary data on non-binary tab', async () => {
|
it('should render pagination with binary data on non-binary tab', async () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId } = render({
|
||||||
Array.from({ length: 11 }).map((_, i) => ({
|
defaultRunItems: Array.from({ length: 11 }).map((_, i) => ({
|
||||||
json: {
|
json: {
|
||||||
data: {
|
data: {
|
||||||
id: i,
|
id: i,
|
||||||
|
@ -200,20 +221,171 @@ describe('RunData', () => {
|
||||||
binary: {
|
binary: {
|
||||||
data: {
|
data: {
|
||||||
a: 'b',
|
a: 'b',
|
||||||
|
data: '',
|
||||||
|
mimeType: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
'json',
|
displayMode: 'json',
|
||||||
);
|
});
|
||||||
expect(getByTestId('ndv-data-pagination')).toBeInTheDocument();
|
expect(getByTestId('ndv-data-pagination')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
const render = (
|
it('should render sub-execution link in header', async () => {
|
||||||
outputData: unknown[],
|
const metadata: ITaskMetadata = {
|
||||||
displayMode: IRunDataDisplayMode,
|
subExecution: {
|
||||||
pinnedData?: INodeExecutionData[],
|
workflowId: 'xyz',
|
||||||
paneType: NodePanelType = 'output',
|
executionId: '123',
|
||||||
) => {
|
},
|
||||||
|
subExecutionsCount: 1,
|
||||||
|
};
|
||||||
|
const { getByTestId } = render({
|
||||||
|
defaultRunItems: [
|
||||||
|
{
|
||||||
|
json: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayMode: 'table',
|
||||||
|
paneType: 'output',
|
||||||
|
metadata,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(getByTestId('related-execution-link')).toBeInTheDocument();
|
||||||
|
expect(getByTestId('related-execution-link')).toHaveTextContent('Inspect Sub-Execution 123');
|
||||||
|
|
||||||
|
expect(getByTestId('ndv-items-count')).toHaveTextContent('1 item, 1 sub-execution');
|
||||||
|
|
||||||
|
getByTestId('related-execution-link').click();
|
||||||
|
expect(openRelatedExecution).toHaveBeenCalledWith(metadata, 'table');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render parent-execution link in header', async () => {
|
||||||
|
const metadata: ITaskMetadata = {
|
||||||
|
parentExecution: {
|
||||||
|
workflowId: 'xyz',
|
||||||
|
executionId: '123',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { getByTestId } = render({
|
||||||
|
defaultRunItems: [
|
||||||
|
{
|
||||||
|
json: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayMode: 'table',
|
||||||
|
paneType: 'output',
|
||||||
|
metadata,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(getByTestId('related-execution-link')).toBeInTheDocument();
|
||||||
|
expect(getByTestId('related-execution-link')).toHaveTextContent('Inspect Parent Execution 123');
|
||||||
|
|
||||||
|
expect(getByTestId('ndv-items-count')).toHaveTextContent('1 item');
|
||||||
|
|
||||||
|
getByTestId('related-execution-link').click();
|
||||||
|
expect(openRelatedExecution).toHaveBeenCalledWith(metadata, 'table');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render sub-execution link in header with multiple items', async () => {
|
||||||
|
const metadata: ITaskMetadata = {
|
||||||
|
subExecution: {
|
||||||
|
workflowId: 'xyz',
|
||||||
|
executionId: '123',
|
||||||
|
},
|
||||||
|
subExecutionsCount: 3,
|
||||||
|
};
|
||||||
|
const { getByTestId } = render({
|
||||||
|
defaultRunItems: [
|
||||||
|
{
|
||||||
|
json: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
json: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayMode: 'json',
|
||||||
|
paneType: 'output',
|
||||||
|
metadata,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(getByTestId('related-execution-link')).toBeInTheDocument();
|
||||||
|
expect(getByTestId('related-execution-link')).toHaveTextContent('Inspect Sub-Execution 123');
|
||||||
|
|
||||||
|
expect(getByTestId('ndv-items-count')).toHaveTextContent('2 items, 3 sub-executions');
|
||||||
|
|
||||||
|
getByTestId('related-execution-link').click();
|
||||||
|
expect(openRelatedExecution).toHaveBeenCalledWith(metadata, 'json');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render sub-execution link in header with multiple runs', async () => {
|
||||||
|
const metadata: ITaskMetadata = {
|
||||||
|
subExecution: {
|
||||||
|
workflowId: 'xyz',
|
||||||
|
executionId: '123',
|
||||||
|
},
|
||||||
|
subExecutionsCount: 3,
|
||||||
|
};
|
||||||
|
const { getByTestId, queryByTestId } = render({
|
||||||
|
runs: [
|
||||||
|
{
|
||||||
|
startTime: new Date().getTime(),
|
||||||
|
executionTime: new Date().getTime(),
|
||||||
|
data: {
|
||||||
|
main: [[{ json: {} }]],
|
||||||
|
},
|
||||||
|
source: [null],
|
||||||
|
metadata,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
startTime: new Date().getTime(),
|
||||||
|
executionTime: new Date().getTime(),
|
||||||
|
data: {
|
||||||
|
main: [[{ json: {} }]],
|
||||||
|
},
|
||||||
|
source: [null],
|
||||||
|
metadata,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayMode: 'json',
|
||||||
|
paneType: 'output',
|
||||||
|
metadata,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(getByTestId('related-execution-link')).toBeInTheDocument();
|
||||||
|
expect(getByTestId('related-execution-link')).toHaveTextContent('Inspect Sub-Execution 123');
|
||||||
|
|
||||||
|
expect(queryByTestId('ndv-items-count')).not.toBeInTheDocument();
|
||||||
|
expect(getByTestId('run-selector')).toBeInTheDocument();
|
||||||
|
|
||||||
|
getByTestId('related-execution-link').click();
|
||||||
|
expect(openRelatedExecution).toHaveBeenCalledWith(metadata, 'json');
|
||||||
|
});
|
||||||
|
|
||||||
|
const render = ({
|
||||||
|
defaultRunItems,
|
||||||
|
displayMode,
|
||||||
|
pinnedData,
|
||||||
|
paneType = 'output',
|
||||||
|
metadata,
|
||||||
|
runs,
|
||||||
|
}: {
|
||||||
|
defaultRunItems?: INodeExecutionData[];
|
||||||
|
displayMode: IRunDataDisplayMode;
|
||||||
|
pinnedData?: INodeExecutionData[];
|
||||||
|
paneType?: NodePanelType;
|
||||||
|
metadata?: ITaskMetadata;
|
||||||
|
runs?: ITaskData[];
|
||||||
|
}) => {
|
||||||
|
const defaultRun: ITaskData = {
|
||||||
|
startTime: new Date().getTime(),
|
||||||
|
executionTime: new Date().getTime(),
|
||||||
|
data: {
|
||||||
|
main: [defaultRunItems ?? [{ json: {} }]],
|
||||||
|
},
|
||||||
|
source: [null],
|
||||||
|
metadata,
|
||||||
|
};
|
||||||
|
|
||||||
const pinia = createTestingPinia({
|
const pinia = createTestingPinia({
|
||||||
stubActions: false,
|
stubActions: false,
|
||||||
initialState: {
|
initialState: {
|
||||||
|
@ -246,16 +418,7 @@ describe('RunData', () => {
|
||||||
data: {
|
data: {
|
||||||
resultData: {
|
resultData: {
|
||||||
runData: {
|
runData: {
|
||||||
'Test Node': [
|
'Test Node': runs ?? [defaultRun],
|
||||||
{
|
|
||||||
startTime: new Date().getTime(),
|
|
||||||
executionTime: new Date().getTime(),
|
|
||||||
data: {
|
|
||||||
main: [outputData],
|
|
||||||
},
|
|
||||||
source: [null],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1413,6 +1413,7 @@ defineExpose({ enterEditMode });
|
||||||
activeTaskMetadata && hasReleatedExectuion && !(paneType === 'input' && hasInputOverwrite)
|
activeTaskMetadata && hasReleatedExectuion && !(paneType === 'input' && hasInputOverwrite)
|
||||||
"
|
"
|
||||||
:class="$style.relatedExecutionInfo"
|
:class="$style.relatedExecutionInfo"
|
||||||
|
data-test-id="related-execution-link"
|
||||||
@click.stop="openRelatedExecution(activeTaskMetadata, displayMode)"
|
@click.stop="openRelatedExecution(activeTaskMetadata, displayMode)"
|
||||||
>
|
>
|
||||||
<N8nIcon icon="external-link-alt" size="xsmall" />
|
<N8nIcon icon="external-link-alt" size="xsmall" />
|
||||||
|
@ -1494,6 +1495,7 @@ defineExpose({ enterEditMode });
|
||||||
activeTaskMetadata && hasReleatedExectuion && !(paneType === 'input' && hasInputOverwrite)
|
activeTaskMetadata && hasReleatedExectuion && !(paneType === 'input' && hasInputOverwrite)
|
||||||
"
|
"
|
||||||
:class="$style.relatedExecutionInfo"
|
:class="$style.relatedExecutionInfo"
|
||||||
|
data-test-id="related-execution-link"
|
||||||
@click.stop="openRelatedExecution(activeTaskMetadata, displayMode)"
|
@click.stop="openRelatedExecution(activeTaskMetadata, displayMode)"
|
||||||
>
|
>
|
||||||
<N8nIcon icon="external-link-alt" size="xsmall" />
|
<N8nIcon icon="external-link-alt" size="xsmall" />
|
||||||
|
|
Loading…
Reference in a new issue