diff --git a/packages/frontend/editor-ui/src/features/ai/evaluation.ee/views/EvaluationsRootView.test.ts b/packages/frontend/editor-ui/src/features/ai/evaluation.ee/views/EvaluationsRootView.test.ts index f60527aa1f5..7bddd685a1b 100644 --- a/packages/frontend/editor-ui/src/features/ai/evaluation.ee/views/EvaluationsRootView.test.ts +++ b/packages/frontend/editor-ui/src/features/ai/evaluation.ee/views/EvaluationsRootView.test.ts @@ -18,6 +18,10 @@ import { EVALUATION_NODE_TYPE, EVALUATION_TRIGGER_NODE_TYPE, NodeHelpers } from import { mockNodeTypeDescription } from '@/__tests__/mocks'; import type { SourceControlPreferences } from '@/features/integrations/sourceControl.ee/sourceControl.types'; +const { showError } = vi.hoisted(() => ({ + showError: vi.fn(), +})); + vi.mock('vue-router', () => ({ useRoute: () => ({ query: {}, @@ -38,7 +42,7 @@ vi.mock('@/app/composables/useTelemetry', () => { vi.mock('@/app/composables/useToast', () => ({ useToast: () => ({ - showError: vi.fn(), + showError, showMessage: vi.fn(), }), })); @@ -92,6 +96,8 @@ describe('EvaluationsRootView', () => { createTestingPinia(); vi.clearAllMocks(); + mockedStore(useWorkflowsStore).isWorkflowSaved = { [mockWorkflow.id]: true }; + vi.spyOn(NodeHelpers, 'getNodeParameters').mockReturnValue({ assignments: { assignments: [ @@ -125,6 +131,24 @@ describe('EvaluationsRootView', () => { expect(evaluationStore.fetchTestRuns).toHaveBeenCalledWith(mockWorkflow.id); }); + it('should not fetch test runs for an unsaved workflow', async () => { + const workflowsStore = mockedStore(useWorkflowsStore); + const usageStore = mockedStore(useUsageStore); + const evaluationStore = mockedStore(useEvaluationStore); + + workflowsStore.isWorkflowSaved = {}; + usageStore.getLicenseInfo.mockResolvedValue(undefined); + evaluationStore.fetchTestRuns.mockRejectedValue(new Error('Workflow not found')); + + renderComponent({ props: { workflowId: mockWorkflow.id } }); + + await flushPromises(); + + expect(usageStore.getLicenseInfo).toHaveBeenCalled(); + expect(evaluationStore.fetchTestRuns).not.toHaveBeenCalled(); + expect(showError).not.toHaveBeenCalled(); + }); + it('should load test data', async () => { const evaluationStore = mockedStore(useEvaluationStore); evaluationStore.fetchTestRuns.mockResolvedValue(mockTestRuns); diff --git a/packages/frontend/editor-ui/src/features/ai/evaluation.ee/views/EvaluationsRootView.vue b/packages/frontend/editor-ui/src/features/ai/evaluation.ee/views/EvaluationsRootView.vue index 89707dec07f..fc323b0ab43 100644 --- a/packages/frontend/editor-ui/src/features/ai/evaluation.ee/views/EvaluationsRootView.vue +++ b/packages/frontend/editor-ui/src/features/ai/evaluation.ee/views/EvaluationsRootView.vue @@ -7,6 +7,7 @@ import { useToast } from '@/app/composables/useToast'; import { useI18n } from '@n8n/i18n'; import { useEvaluationStore } from '../evaluation.store'; import { useSourceControlStore } from '@/features/integrations/sourceControl.ee/sourceControl.store'; +import { useWorkflowsStore } from '@/app/stores/workflows.store'; import { computed, watch } from 'vue'; import EvaluationsPaywall from '../components/Paywall/EvaluationsPaywall.vue'; @@ -19,6 +20,7 @@ const props = defineProps<{ const usageStore = useUsageStore(); const evaluationStore = useEvaluationStore(); +const workflowsStore = useWorkflowsStore(); const telemetry = useTelemetry(); const toast = useToast(); const locale = useI18n(); @@ -32,6 +34,8 @@ const isProtectedEnvironment = computed(() => { return sourceControlStore.preferences.branchReadOnly; }); +const workflowIsSaved = computed(() => workflowsStore.isWorkflowSaved[props.workflowId] === true); + const runs = computed(() => { return Object.values(evaluationStore.testRunsById ?? {}).filter( ({ workflowId }) => workflowId === props.workflowId, @@ -46,6 +50,8 @@ const showWizard = computed(() => !hasRuns.value); // Method to run a test - will be used by the SetupWizard component async function runTest() { + if (!workflowIsSaved.value) return; + try { await evaluationStore.startTestRun(props.workflowId); } catch (error) { @@ -67,15 +73,32 @@ const evaluationsQuotaExceeded = computed(() => { ); }); -const { isReady } = useAsyncState(async () => { +async function fetchTestRuns() { + if (!workflowIsSaved.value) return; + try { - await usageStore.getLicenseInfo(); await evaluationStore.fetchTestRuns(props.workflowId); } catch (error) { toast.showError(error, locale.baseText('evaluation.listRuns.error.cantFetchTestRuns')); } +} + +const { isReady } = useAsyncState(async () => { + try { + await usageStore.getLicenseInfo(); + } catch (error) { + toast.showError(error, locale.baseText('evaluation.listRuns.error.cantFetchTestRuns')); + } + + await fetchTestRuns(); }, undefined); +watch(workflowIsSaved, async (isSaved) => { + if (isSaved) { + await fetchTestRuns(); + } +}); + watch( isReady, (ready) => {