mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-21 02:56:40 -08:00
fix(editor): Show retry information in execution list only when it exists (#6587)
* fix(editor): Show retry information in execution list only when it exists * build: Fix checking for test files
This commit is contained in:
parent
4253b48b26
commit
2580286a19
|
@ -126,13 +126,13 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span v-if="execution.id">#{{ execution.id }}</span>
|
<span v-if="execution.id">#{{ execution.id }}</span>
|
||||||
<span v-if="execution.retryOf !== undefined">
|
<span v-if="execution.retryOf">
|
||||||
<br />
|
<br />
|
||||||
<small>
|
<small>
|
||||||
({{ $locale.baseText('executionsList.retryOf') }} #{{ execution.retryOf }})
|
({{ $locale.baseText('executionsList.retryOf') }} #{{ execution.retryOf }})
|
||||||
</small>
|
</small>
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="execution.retrySuccessId !== undefined">
|
<span v-else-if="execution.retrySuccessId">
|
||||||
<br />
|
<br />
|
||||||
<small>
|
<small>
|
||||||
({{ $locale.baseText('executionsList.successRetry') }} #{{
|
({{ $locale.baseText('executionsList.successRetry') }} #{{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { vi, describe, it, expect } from 'vitest';
|
import { vi, describe, it, expect } from 'vitest';
|
||||||
import Vue from 'vue';
|
import { merge } from 'lodash-es';
|
||||||
import { PiniaVuePlugin } from 'pinia';
|
import { PiniaVuePlugin } from 'pinia';
|
||||||
import { createTestingPinia } from '@pinia/testing';
|
import { createTestingPinia } from '@pinia/testing';
|
||||||
import { render } from '@testing-library/vue';
|
import { render } from '@testing-library/vue';
|
||||||
|
@ -7,15 +7,29 @@ import userEvent from '@testing-library/user-event';
|
||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
import { STORES } from '@/constants';
|
import { STORES } from '@/constants';
|
||||||
import ExecutionsList from '@/components/ExecutionsList.vue';
|
import ExecutionsList from '@/components/ExecutionsList.vue';
|
||||||
import { externalHooks } from '@/mixins/externalHooks';
|
|
||||||
import { genericHelpers } from '@/mixins/genericHelpers';
|
|
||||||
import { executionHelpers } from '@/mixins/executionsHelpers';
|
|
||||||
import { i18nInstance } from '@/plugins/i18n';
|
import { i18nInstance } from '@/plugins/i18n';
|
||||||
import type { IWorkflowDb } from '@/Interface';
|
import type { IWorkflowDb } from '@/Interface';
|
||||||
import type { IExecutionsSummary } from 'n8n-workflow';
|
import type { IExecutionsSummary } from 'n8n-workflow';
|
||||||
import { retry, waitAllPromises } from '@/__tests__/utils';
|
import { retry, SETTINGS_STORE_DEFAULT_STATE, waitAllPromises } from '@/__tests__/utils';
|
||||||
import { useWorkflowsStore } from '@/stores';
|
import { useWorkflowsStore } from '@/stores';
|
||||||
|
|
||||||
|
let pinia: ReturnType<typeof createTestingPinia>;
|
||||||
|
|
||||||
|
const generateUndefinedNullOrString = () => {
|
||||||
|
switch (Math.floor(Math.random() * 4)) {
|
||||||
|
case 0:
|
||||||
|
return undefined;
|
||||||
|
case 1:
|
||||||
|
return null;
|
||||||
|
case 2:
|
||||||
|
return faker.datatype.uuid();
|
||||||
|
case 3:
|
||||||
|
return '';
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const workflowDataFactory = (): IWorkflowDb => ({
|
const workflowDataFactory = (): IWorkflowDb => ({
|
||||||
createdAt: faker.date.past().toDateString(),
|
createdAt: faker.date.past().toDateString(),
|
||||||
updatedAt: faker.date.past().toDateString(),
|
updatedAt: faker.date.past().toDateString(),
|
||||||
|
@ -38,83 +52,71 @@ const executionDataFactory = (): IExecutionsSummary => ({
|
||||||
workflowName: faker.datatype.string(),
|
workflowName: faker.datatype.string(),
|
||||||
status: faker.helpers.arrayElement(['failed', 'success']),
|
status: faker.helpers.arrayElement(['failed', 'success']),
|
||||||
nodeExecutionStatus: {},
|
nodeExecutionStatus: {},
|
||||||
|
retryOf: generateUndefinedNullOrString(),
|
||||||
|
retrySuccessId: generateUndefinedNullOrString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const workflowsData = Array.from({ length: 10 }, workflowDataFactory);
|
const generateWorkflowsData = () => Array.from({ length: 10 }, workflowDataFactory);
|
||||||
|
|
||||||
const executionsData = Array.from({ length: 2 }, () => ({
|
const generateExecutionsData = () =>
|
||||||
count: 20,
|
Array.from({ length: 2 }, () => ({
|
||||||
results: Array.from({ length: 10 }, executionDataFactory),
|
count: 20,
|
||||||
estimated: false,
|
results: Array.from({ length: 10 }, executionDataFactory),
|
||||||
}));
|
estimated: false,
|
||||||
|
}));
|
||||||
const renderOptions = {
|
|
||||||
pinia: createTestingPinia({
|
|
||||||
initialState: {
|
|
||||||
[STORES.SETTINGS]: {
|
|
||||||
settings: {
|
|
||||||
templates: {
|
|
||||||
enabled: true,
|
|
||||||
host: 'https://api.n8n.io/api/',
|
|
||||||
},
|
|
||||||
license: {
|
|
||||||
environment: 'development',
|
|
||||||
},
|
|
||||||
deployment: {
|
|
||||||
type: 'default',
|
|
||||||
},
|
|
||||||
enterprise: {
|
|
||||||
advancedExecutionFilters: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
propsData: {
|
|
||||||
autoRefreshEnabled: false,
|
|
||||||
},
|
|
||||||
i18n: i18nInstance,
|
|
||||||
stubs: ['font-awesome-icon'],
|
|
||||||
mixins: [externalHooks, genericHelpers, executionHelpers],
|
|
||||||
};
|
|
||||||
|
|
||||||
function TelemetryPlugin(vue: typeof Vue): void {
|
|
||||||
Object.defineProperty(vue, '$telemetry', {
|
|
||||||
get() {
|
|
||||||
return {
|
|
||||||
track: () => {},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Object.defineProperty(vue.prototype, '$telemetry', {
|
|
||||||
get() {
|
|
||||||
return {
|
|
||||||
track: () => {},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderComponent = async () => {
|
const renderComponent = async () => {
|
||||||
const renderResult = render(ExecutionsList, renderOptions);
|
const renderResult = render(
|
||||||
|
ExecutionsList,
|
||||||
|
{
|
||||||
|
pinia,
|
||||||
|
propsData: {
|
||||||
|
autoRefreshEnabled: false,
|
||||||
|
},
|
||||||
|
i18n: i18nInstance,
|
||||||
|
stubs: ['font-awesome-icon'],
|
||||||
|
},
|
||||||
|
(vue) => {
|
||||||
|
vue.use(PiniaVuePlugin);
|
||||||
|
vue.prototype.$telemetry = {
|
||||||
|
track: () => {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
await waitAllPromises();
|
await waitAllPromises();
|
||||||
return renderResult;
|
return renderResult;
|
||||||
};
|
};
|
||||||
|
|
||||||
Vue.use(TelemetryPlugin);
|
|
||||||
Vue.use(PiniaVuePlugin);
|
|
||||||
|
|
||||||
describe('ExecutionsList.vue', () => {
|
describe('ExecutionsList.vue', () => {
|
||||||
const workflowsStore: ReturnType<typeof useWorkflowsStore> = useWorkflowsStore();
|
let workflowsStore: ReturnType<typeof useWorkflowsStore>;
|
||||||
|
let workflowsData: IWorkflowDb[];
|
||||||
|
let executionsData: Array<{
|
||||||
|
count: number;
|
||||||
|
results: IExecutionsSummary[];
|
||||||
|
estimated: boolean;
|
||||||
|
}>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
workflowsData = generateWorkflowsData();
|
||||||
|
executionsData = generateExecutionsData();
|
||||||
|
|
||||||
|
pinia = createTestingPinia({
|
||||||
|
initialState: {
|
||||||
|
[STORES.SETTINGS]: {
|
||||||
|
settings: merge(SETTINGS_STORE_DEFAULT_STATE.settings, {
|
||||||
|
enterprise: {
|
||||||
|
advancedExecutionFilters: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
workflowsStore = useWorkflowsStore();
|
||||||
|
|
||||||
vi.spyOn(workflowsStore, 'fetchAllWorkflows').mockResolvedValue(workflowsData);
|
vi.spyOn(workflowsStore, 'fetchAllWorkflows').mockResolvedValue(workflowsData);
|
||||||
vi.spyOn(workflowsStore, 'getCurrentExecutions').mockResolvedValue([]);
|
vi.spyOn(workflowsStore, 'getCurrentExecutions').mockResolvedValue([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
vi.clearAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render empty list', async () => {
|
it('should render empty list', async () => {
|
||||||
vi.spyOn(workflowsStore, 'getPastExecutions').mockResolvedValueOnce({
|
vi.spyOn(workflowsStore, 'getPastExecutions').mockResolvedValueOnce({
|
||||||
count: 0,
|
count: 0,
|
||||||
|
@ -182,4 +184,17 @@ describe('ExecutionsList.vue', () => {
|
||||||
expect(getByTestId('select-visible-executions-checkbox')).toBeInTheDocument();
|
expect(getByTestId('select-visible-executions-checkbox')).toBeInTheDocument();
|
||||||
expect(queryByTestId('select-all-executions-checkbox')).not.toBeInTheDocument();
|
expect(queryByTestId('select-all-executions-checkbox')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should show "retry" data when appropriate', async () => {
|
||||||
|
vi.spyOn(workflowsStore, 'getPastExecutions').mockResolvedValue(executionsData[0]);
|
||||||
|
const retryOf = executionsData[0].results.filter((execution) => execution.retryOf);
|
||||||
|
const retrySuccessId = executionsData[0].results.filter(
|
||||||
|
(execution) => !execution.retryOf && execution.retrySuccessId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { queryAllByText } = await renderComponent();
|
||||||
|
|
||||||
|
expect(queryAllByText(/Retry of/).length).toBe(retryOf.length);
|
||||||
|
expect(queryAllByText(/Success retry/).length).toBe(retrySuccessId.length);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1959,8 +1959,8 @@ export interface IExecutionsSummary {
|
||||||
id: string;
|
id: string;
|
||||||
finished?: boolean;
|
finished?: boolean;
|
||||||
mode: WorkflowExecuteMode;
|
mode: WorkflowExecuteMode;
|
||||||
retryOf?: string;
|
retryOf?: string | null;
|
||||||
retrySuccessId?: string;
|
retrySuccessId?: string | null;
|
||||||
waitTill?: Date;
|
waitTill?: Date;
|
||||||
startedAt: Date;
|
startedAt: Date;
|
||||||
stoppedAt?: Date;
|
stoppedAt?: Date;
|
||||||
|
|
Loading…
Reference in a new issue