You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular+Jest测试Rete.js组件遇导入路径错误求助

Angular + Rete.js 组件测试导入路径错误问题

我正在为一个基于Rete.js的Angular组件编写简单测试,但测试一直失败,原因是无法解析某条导入路径。哪怕在测试里Mock了EditorCreatorService而不是直接注入,这个导入错误还是存在。以下是相关代码和错误截图:

组件代码

import { Component, ElementRef, Injector, ViewChild } from '@angular/core';
import { EditorCreatorService } from './edtitor-creator';

@Component({
  selector: 'exb-visual-workflow-editor',
  templateUrl: 'visual-workflow-editor.component.html',
  styleUrls: ['visual-workflow-editor.component.scss'],
})
export class VisualWorkflowEditorComponent {
  @ViewChild('rete')
  container!: ElementRef;

  constructor(private injector: Injector, private readonly editorCreatorService: EditorCreatorService) {}

  ngAfterViewInit(): void {
    const el = this.container.nativeElement;

    if (el) {
      this.editorCreatorService.createEditor(el, this.injector);
    }
  }
}

服务代码

import { NodeEditor, GetSchemes, ClassicPreset } from 'rete';
import { Injector, Injectable } from '@angular/core';
import { AreaPlugin } from 'rete-area-plugin';
import { ConnectionPlugin, Presets as ConnectionPresets } from 'rete-connection-plugin';

import { AngularPlugin, AngularArea2D, Presets as AngularPresets } from 'rete-angular-plugin/16'; // --> 导入错误

import { CustomSocketComponent } from './customization/custom-socket/custom-socket.component';
import { CustomNodeComponent } from './customization/custom-node/custom-node.component';
import { CustomConnectionComponent } from './customization/custom-connection/custom-connection.component';

import { addCustomBackground } from './customization/custom-background';

type Schemes = GetSchemes<ClassicPreset.Node, ClassicPreset.Connection<ClassicPreset.Node, ClassicPreset.Node>>;
type AreaExtra = AngularArea2D<Schemes>;

const socket = new ClassicPreset.Socket('socket');

@Injectable()
export class EditorCreatorService {

  async createEditor(container: HTMLElement, injector: Injector) {
    const editor = new NodeEditor<Schemes>();
    const area = new AreaPlugin<Schemes, AreaExtra>(container);
    const connection = new ConnectionPlugin<Schemes, AreaExtra>();

    const angularRender = new AngularPlugin<Schemes, AreaExtra>({ injector });

    angularRender.addPreset(
      AngularPresets.classic.setup({
        customize: {
          node() {
            return CustomNodeComponent;
          },
          connection() {
            return CustomConnectionComponent;
          },
          socket() {
            return CustomSocketComponent;
          },
        },
      }),
    );

    connection.addPreset(ConnectionPresets.classic.setup());

    addCustomBackground(area);

    editor.use(area);
    area.use(connection);

    area.use(angularRender);

    const aLabel = 'Custom';
    const bLabel = 'Custom';

    const a = new ClassicPreset.Node(aLabel);
    a.addOutput('a', new ClassicPreset.Output(socket));
    a.addInput('a', new ClassicPreset.Input(socket));
    await editor.addNode(a);

    const b = new ClassicPreset.Node(bLabel);
    b.addOutput('a', new ClassicPreset.Output(socket));
    b.addInput('a', new ClassicPreset.Input(socket));
    await editor.addNode(b);

    await area.translate(a.id, { x: 0, y: 0 });
    await area.translate(b.id, { x: 300, y: 0 });

    await editor.addConnection(new ClassicPreset.Connection(a, 'a', b, 'a'));

    return {
      destroy: () => area.destroy(),
    };
  }

}

测试代码

import { EditorCreatorService } from './edtitor-creator';
import { VisualWorkflowEditorComponent } from './visual-workflow-editor.component';
import { render, RenderResult } from '@testing-library/angular';

describe('WorkflowEditorComponent', () => {
  let component: RenderResult<VisualWorkflowEditorComponent>;
  let createEditorSpy: jest.SpyInstance;
  let editorCreatorServiceMock: Partial<EditorCreatorService>;

  beforeEach(async () => {
    editorCreatorServiceMock = {
      createEditor: jest.fn().mockReturnValue({}),
    };

    component = await render(VisualWorkflowEditorComponent, {
      providers: [
        {
          provide: editorCreatorServiceMock,
        },
      ],
    });
    createEditorSpy = jest.spyOn(editorCreatorServiceMock, 'createEditor');
  });

  it('should create', () => {
    expect(createEditorSpy).toHaveBeenCalledTimes(1);
  });
});

错误截图

错误截图


解决方法

1. 先修复测试里的Provider配置错误

你测试代码里的Provider写反了,provide字段要填服务的类EditorCreatorService,useValue才是你的mock对象,正确写法:

providers: [
  {
    provide: EditorCreatorService,
    useValue: editorCreatorServiceMock,
  },
]

原代码直接把mock对象传给provide,Angular压根不知道要替换哪个服务,导致测试时还是会加载真实的EditorCreatorService类,自然触发导入错误。

2. 处理Rete插件的导入路径问题

如果修复Provider后还是报错,说明测试环境认不出rete-angular-plugin/16这个路径,试试下面的方案:

  • 核对依赖版本:路径里的16对应Angular 16,确认你安装的rete-angular-plugin版本和Angular版本匹配,不匹配的话要么换插件版本,要么调整Angular版本。
  • 添加路径映射:在tsconfig.spec.json里加路径映射,告诉测试环境怎么找这个模块:
    {
      "compilerOptions": {
        "paths": {
          "rete-angular-plugin/16": ["node_modules/rete-angular-plugin/dist/16"]
        }
      }
    }
    
  • Mock整个Rete模块:如果测试只需要验证组件调用了createEditor,不需要真的初始化Rete编辑器,可以在测试文件顶部Mock所有Rete相关依赖:
    // 先Mock rete-angular-plugin
    jest.mock('rete-angular-plugin/16', () => ({
      AngularPlugin: jest.fn(),
      AngularArea2D: jest.fn(),
      Presets: {
        classic: { setup: jest.fn().mockReturnValue({}) }
      }
    }));
    
    // 再Mock其他Rete相关依赖
    jest.mock('rete', () => ({
      NodeEditor: jest.fn(),
      GetSchemes: jest.fn(),
      ClassicPreset: {
        Socket: jest.fn(),
        Node: jest.fn(),
        Output: jest.fn(),
        Input: jest.fn(),
        Connection: jest.fn()
      }
    }));
    
    jest.mock('rete-area-plugin', () => ({ AreaPlugin: jest.fn() }));
    jest.mock('rete-connection-plugin', () => ({
      ConnectionPlugin: jest.fn(),
      Presets: { classic: { setup: jest.fn() } }
    }));
    
    这样测试时就不会加载真实的Rete模块,也就不会触发导入错误了。

3. 检查测试环境的构建配置

如果用Angular CLI的测试环境,看看angular.json里的test配置,确保包含了rete-angular-plugin的文件,或者在tsconfig.spec.json的include字段里加上必要的路径。


内容的提问来源于stack exchange,提问作者Miguel Frias

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 16:53:13