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:
Csaba Tuncsik 2023-07-04 09:42:58 +02:00 committed by कारतोफ्फेलस्क्रिप्ट™
parent 4253b48b26
commit 2580286a19
3 changed files with 86 additions and 71 deletions

View file

@ -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') }} #{{

View file

@ -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 = () =>
Array.from({ length: 2 }, () => ({
count: 20, count: 20,
results: Array.from({ length: 10 }, executionDataFactory), results: Array.from({ length: 10 }, executionDataFactory),
estimated: false, estimated: false,
})); }));
const renderOptions = { const renderComponent = async () => {
pinia: createTestingPinia({ const renderResult = render(
initialState: { ExecutionsList,
[STORES.SETTINGS]: { {
settings: { pinia,
templates: {
enabled: true,
host: 'https://api.n8n.io/api/',
},
license: {
environment: 'development',
},
deployment: {
type: 'default',
},
enterprise: {
advancedExecutionFilters: true,
},
},
},
},
}),
propsData: { propsData: {
autoRefreshEnabled: false, autoRefreshEnabled: false,
}, },
i18n: i18nInstance, i18n: i18nInstance,
stubs: ['font-awesome-icon'], stubs: ['font-awesome-icon'],
mixins: [externalHooks, genericHelpers, executionHelpers], },
}; (vue) => {
vue.use(PiniaVuePlugin);
function TelemetryPlugin(vue: typeof Vue): void { vue.prototype.$telemetry = {
Object.defineProperty(vue, '$telemetry', {
get() {
return {
track: () => {}, track: () => {},
}; };
}, },
}); );
Object.defineProperty(vue.prototype, '$telemetry', {
get() {
return {
track: () => {},
};
},
});
}
const renderComponent = async () => {
const renderResult = render(ExecutionsList, renderOptions);
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);
});
}); });

View file

@ -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;