fix(plugin-action-duplicate): avoid duplicate error notifications (#10327)

* fix(plugin-action-duplicate): show duplicate failure message

* fix(plugin-action-duplicate): avoid duplicate error notification
This commit is contained in:
Katherine
2026-08-11 11:28:35 +08:00
committed by GitHub
parent b150ab3592
commit 73dfce4fd0
2 changed files with 74 additions and 1 deletions
@@ -530,6 +530,12 @@ DuplicateActionModel.registerFlow({
params.requestConfig,
);
ctx.message.success(ctx.t('Saved successfully'));
} catch (error) {
if (!(error instanceof Error) || error.name !== 'ResponseError') {
const errorMessage = error instanceof Error && error.message ? error.message : ctx.t('Save failed');
ctx.message.error(errorMessage);
}
throw error;
} finally {
ctx.model.duplicateLoading = false;
ctx.model.rerender();
@@ -7,7 +7,7 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { FlowEngine } from '@nocobase/flow-engine';
import { FlowEngine, ResourceError } from '@nocobase/flow-engine';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { DuplicateActionModel } from '../DuplicateActionModel';
@@ -203,6 +203,73 @@ describe('DuplicateActionModel', () => {
expect(ctx.model.rerender).toHaveBeenCalledTimes(2);
});
it.each([
{ failureStage: 'template fetch', shouldFailFetch: true },
{ failureStage: 'record creation', shouldFailFetch: false },
])(
'keeps only the API error notification and resets loading when $failureStage fails',
async ({ shouldFailFetch }) => {
const flow: any = createModel({ duplicateFields: ['title'] }).getFlow('duplicateSettings');
const step: any = flow.getStep('duplicate');
const handler = step.serialize().handler;
const error = new ResourceError({
response: {
data: {
errors: [{ message: 'Duplicate failed' }],
},
},
});
const runAction = shouldFailFetch
? vi.fn().mockRejectedValue(error)
: vi.fn(async () => ({ data: { title: 'Copy' } }));
const create = shouldFailFetch ? vi.fn(async () => undefined) : vi.fn().mockRejectedValue(error);
const resource = {
setDataSourceKey: vi.fn(),
setResourceName: vi.fn(),
runAction,
};
const ctx = {
model: {
props: {
duplicateFields: ['title'],
},
duplicateLoading: false,
rerender: vi.fn(),
},
blockModel: {
collection: {
filterTargetKey: 'id',
dataSourceKey: 'main',
name: 'posts',
},
resource: {
create,
},
},
record: {
id: 100,
__collection: 'posts',
},
collection: {
fields: new Map([['title', { name: 'title' }]]),
},
createResource: vi.fn(() => resource),
message: {
error: vi.fn(),
success: vi.fn(),
},
t: (key: string) => key,
};
await expect(handler(ctx, {})).rejects.toThrow('Duplicate failed');
expect(ctx.message.error).not.toHaveBeenCalled();
expect(ctx.message.success).not.toHaveBeenCalled();
expect(ctx.model.duplicateLoading).toBe(false);
expect(ctx.model.rerender).toHaveBeenCalledTimes(2);
},
);
it('delegates openView when popupTemplateUid is provided', async () => {
const model = createModel({ duplicateFields: ['title'] });
const flow: any = model.getFlow('popupSettings');