mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(editor): Fix sub-execution links in empty output tables (#12781)
Some checks failed
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions
Benchmark Docker Image CI / build (push) Has been cancelled
Some checks failed
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions
Benchmark Docker Image CI / build (push) Has been cancelled
This commit is contained in:
parent
358d2843e5
commit
114ed88368
110
packages/editor-ui/src/components/RunDataTable.test.ts
Normal file
110
packages/editor-ui/src/components/RunDataTable.test.ts
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
|
import RunDataTable from '@/components/RunDataTable.vue';
|
||||||
|
import { createTestingPinia } from '@pinia/testing';
|
||||||
|
import { cleanup } from '@testing-library/vue';
|
||||||
|
|
||||||
|
vi.mock('vue-router', () => {
|
||||||
|
const push = vi.fn();
|
||||||
|
const resolve = vi.fn().mockReturnValue({ href: 'https://test.com' });
|
||||||
|
return {
|
||||||
|
useRouter: () => ({
|
||||||
|
push,
|
||||||
|
resolve,
|
||||||
|
}),
|
||||||
|
useRoute: () => ({}),
|
||||||
|
RouterLink: vi.fn(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const { trackOpeningRelatedExecution, resolveRelatedExecutionUrl } = vi.hoisted(() => ({
|
||||||
|
trackOpeningRelatedExecution: vi.fn(),
|
||||||
|
resolveRelatedExecutionUrl: vi.fn().mockReturnValue('https://test.com'),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@/composables/useExecutionHelpers', () => ({
|
||||||
|
useExecutionHelpers: () => ({
|
||||||
|
trackOpeningRelatedExecution,
|
||||||
|
resolveRelatedExecutionUrl,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const renderComponent = createComponentRenderer(RunDataTable, {
|
||||||
|
props: {
|
||||||
|
node: {
|
||||||
|
parameters: {
|
||||||
|
keepOnlySet: false,
|
||||||
|
values: {},
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
id: '820ea733-d8a6-4379-8e73-88a2347ea003',
|
||||||
|
name: 'Set',
|
||||||
|
type: 'n8n-nodes-base.set',
|
||||||
|
typeVersion: 1,
|
||||||
|
position: [380, 1060],
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
distanceFromActive: 0,
|
||||||
|
pageOffset: 0,
|
||||||
|
runIndex: 0,
|
||||||
|
totalRuns: 0,
|
||||||
|
mappingEnabled: false,
|
||||||
|
hasDefaultHoverState: false,
|
||||||
|
search: '',
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
plugins: [createTestingPinia()],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('RunDataTable.vue', () => {
|
||||||
|
beforeEach(cleanup);
|
||||||
|
|
||||||
|
it('renders empty table correctly', () => {
|
||||||
|
const emptyInputData = [
|
||||||
|
{
|
||||||
|
json: {},
|
||||||
|
index: 0,
|
||||||
|
pairedItem: { item: 0 },
|
||||||
|
metadata: { subExecution: { executionId: '123', workflowId: '123abcd' } },
|
||||||
|
},
|
||||||
|
,
|
||||||
|
];
|
||||||
|
const emptyMessage = "This is an item, but it's empty.";
|
||||||
|
|
||||||
|
const { getByTestId, getByText } = renderComponent({
|
||||||
|
props: {
|
||||||
|
inputData: emptyInputData,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(getByText(emptyMessage)).toBeInTheDocument();
|
||||||
|
// Sub-execution button should be link (ADO-3057)
|
||||||
|
expect(getByTestId('debug-sub-execution')).toBeInTheDocument();
|
||||||
|
expect(getByTestId('debug-sub-execution').tagName).toBe('A');
|
||||||
|
expect(getByTestId('debug-sub-execution').getAttribute('href')).toBe('https://test.com');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders table with items correctly', () => {
|
||||||
|
const inputData = {
|
||||||
|
json: { firstName: 'John', lastName: 'Doe' },
|
||||||
|
index: 0,
|
||||||
|
pairedItem: { item: 0 },
|
||||||
|
metadata: { subExecution: { executionId: '123', workflowId: '123abcd' } },
|
||||||
|
};
|
||||||
|
const { getByTestId, getAllByText } = renderComponent({
|
||||||
|
props: {
|
||||||
|
inputData: [inputData],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(getByTestId('debug-sub-execution')).toBeInTheDocument();
|
||||||
|
expect(getByTestId('debug-sub-execution').tagName).toBe('A');
|
||||||
|
expect(getByTestId('debug-sub-execution').getAttribute('href')).toBe('https://test.com');
|
||||||
|
// All keys from the input data should be rendered
|
||||||
|
Object.keys(inputData.json).forEach((key) => {
|
||||||
|
expect(getAllByText(key)).not.toHaveLength(0);
|
||||||
|
});
|
||||||
|
// Also, all values from the input data should be rendered
|
||||||
|
Object.values(inputData.json).forEach((value) => {
|
||||||
|
expect(getAllByText(value)).not.toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -451,12 +451,13 @@ watch(focusedMappableInput, (curr) => {
|
||||||
<N8nIconButton
|
<N8nIconButton
|
||||||
v-if="tableData.metadata.data[index1]"
|
v-if="tableData.metadata.data[index1]"
|
||||||
v-show="showExecutionLink(index1)"
|
v-show="showExecutionLink(index1)"
|
||||||
|
element="a"
|
||||||
type="secondary"
|
type="secondary"
|
||||||
icon="external-link-alt"
|
icon="external-link-alt"
|
||||||
data-test-id="debug-sub-execution"
|
data-test-id="debug-sub-execution"
|
||||||
size="mini"
|
size="mini"
|
||||||
:href="resolveRelatedExecutionUrl(tableData.metadata.data[index1])"
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
:href="resolveRelatedExecutionUrl(tableData.metadata.data[index1])"
|
||||||
@click="trackOpeningRelatedExecution(tableData.metadata.data[index1], 'table')"
|
@click="trackOpeningRelatedExecution(tableData.metadata.data[index1], 'table')"
|
||||||
/>
|
/>
|
||||||
</N8nTooltip>
|
</N8nTooltip>
|
||||||
|
@ -584,20 +585,18 @@ watch(focusedMappableInput, (curr) => {
|
||||||
placement="left"
|
placement="left"
|
||||||
:hide-after="0"
|
:hide-after="0"
|
||||||
>
|
>
|
||||||
<a
|
<N8nIconButton
|
||||||
v-if="tableData.metadata.data[index1]"
|
v-if="tableData.metadata.data[index1]"
|
||||||
v-show="showExecutionLink(index1)"
|
v-show="showExecutionLink(index1)"
|
||||||
:href="resolveRelatedExecutionUrl(tableData.metadata.data[index1])"
|
element="a"
|
||||||
|
type="secondary"
|
||||||
|
icon="external-link-alt"
|
||||||
|
data-test-id="debug-sub-execution"
|
||||||
|
size="mini"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
:href="resolveRelatedExecutionUrl(tableData.metadata.data[index1])"
|
||||||
@click="trackOpeningRelatedExecution(tableData.metadata.data[index1], 'table')"
|
@click="trackOpeningRelatedExecution(tableData.metadata.data[index1], 'table')"
|
||||||
>
|
/>
|
||||||
<N8nIconButton
|
|
||||||
type="secondary"
|
|
||||||
icon="external-link-alt"
|
|
||||||
data-test-id="debug-sub-execution"
|
|
||||||
size="mini"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</N8nTooltip>
|
</N8nTooltip>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
|
|
Loading…
Reference in a new issue