我正在实现一个Angular 为15.2.9的functional router guard,它判断用户是否已登录.如果不是这样,警卫应该返回falseUrlTree(即重定向到登录页面),具体取决于参数redirectToLogin: boolean.

我正在使用一个工厂函数来实现功能路由守卫,它类似于百分之一:

export const isLoggedIn = (redirectToLogin: boolean): CanActivateFn => {
  return (next: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
    const isLoggedIn = !!inject(CookieService).check('some_cookie');

    if (isLoggedIn) return true;
    if (!isLoggedIn && !redirectToLogin) return false;

    const redirectUrl = inject(Router).createUrlTree(['user', 'sign_in'], {
      queryParams: { next: encodeURIComponent(`/${next.url.join('/')}`) },
      queryParamsHandling: 'merge',
    });
    return redirectUrl;
  };
};

它像预期的那样工作,但我不能为它写specs:

  let cookieServiceSpy;
  let activatedRouteSnapshot;

  beforeEach(() => {
    cookieServiceSpy = jasmine.createSpyObj<CookieService>(['check']);
    activatedRouteSnapshot = { url: [], data: {} };
    TestBed.configureTestingModule({
      providers: [
        isLoggedIn,
        { provide: CookieService, useValue: cookieServiceSpy },
        {
          provide: ActivatedRouteSnapshot,
          useValue: activatedRouteSnapshot,
        },
      ],
    });
  });

  it('should return true when secure_user_id cookie is present and redirectToLogin is false', () => {
    // given
    cookieServiceSpy.check.and.returnValue(true);
    activatedRouteSnapshot.data.redirectToLogin = false;
    // when
    const result = TestBed.runInInjectionContext(isLoggedIn); // <-- this line is the problem
    // then
    expect(result).toBeTrue();
  });

导致打字错误:Argument of type '(redirectToLogin: boolean) => CanActivateFn' is not assignable to parameter of type '() => CanActivateFn'. Target signature provides too few arguments. Expected 1 or more, but got 0.

我还try 了其他几种 Select :

  • TestBed.runInInjectionContext(isLoggedIn as any)个➔Can't resolve all parameters for isLoggedIn: (?).正在试运行
  • TestBed.runInInjectionContext(isLoggedIn(false))%Argument of type 'CanActivateFn' is not assignable to parameter of type '() => boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree>'. Target signature provides too few arguments. Expected 2 or more, but got 0.
  • TestBed.runInInjectionContext(isLoggedIn(false) as any)个➔Can't resolve all parameters for isLoggedIn: (?).正在试运行
  • TestBed.runInInjectionContext(isLoggedIn(false)(activatedRouteSnapshot, null))%Argument of type 'boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree>' is not assignable to parameter of type '() => unknown'. Type 'boolean' is not assignable to type '() => unknown'.
  • TestBed.runInInjectionContext(isLoggedIn(false)(activatedRouteSnapshot, null) as any);%inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with 101.
  • TestBed.runInInjectionContext(isLoggedIn(false) as any)(activatedRouteSnapshot, null);%This expression is not callable. Type '{}' has no call signatures.
  • (TestBed.runInInjectionContext(isLoggedIn(false) as any) as any)(activatedRouteSnapshot, null)%Can't resolve all parameters for isLoggedIn: (?).

我还try 省略工厂函数,并使用Routing Data作为参数,如图here所示,但注入ActivatedRouteSnapshot是不可能的,使用ActivatedRoute会产生嵌套的可观察对象……一定有一种更简单的方法.

How can I test the functional router guard?

推荐答案

您观察到的错误有两个原因:

  • 您提供了一个未修饰的符号isLoggedIn
  • 通过调用TestBed.runInInjectionContext,您初始化了注入器,并且注入器试图将提供程序转换为工厂,但失败了,因为isLoggedIn没有注释,并且有一个或多个参数(这里只有1个).

如果您确实需要注入CanActivateFn,您可以使用{provide: isLoggedIn, useValue: () => { ... },但在我们的例子中,您不是在注入函数,而是直接调用它.

因此不需要提供该函数,且调用直接向前:

  beforeEach(() => {
    cookieServiceSpy = jasmine.createSpyObj<CookieService>(['check']);
    activatedRouteSnapshot = { url: [], data: {} };
    TestBed.configureTestingModule({
      providers: [
        { provide: CookieService, useValue: cookieServiceSpy },
      ],
    });
  });

  it('should return true when secure_user_id cookie is present and redirectToLogin is false', async () => {
    // given
    cookieServiceSpy.check.and.returnValue(true);
    activatedRouteSnapshot.data.redirectToLogin = false;
    // when
    const result = TestBed.runInInjectionContext(() => isLoggedIn(true)(activatedRouteSnapshot, undefined));  // Double call  to retrieve the result. 
    // then
    expect(result).toBeTrue();
  });

Typescript相关问答推荐

如何在不使用变量的情况下静态判断运算式的类型?

ReturnType此索引方法与点表示法之间的差异

为什么我的Set-Cookie在我执行下一次请求后被擦除

有条件地删除区分的联合类型中的属性的可选属性

访问继承接口的属性时在html中出错.角形

从TypeScrip中的其他类型检索泛型类型

使用KeyOf和Never的循环引用

try 呈现应用程序获取-错误:动作必须是普通对象.使用自定义中间件进行MySQL操作

我在Angular 17中的environment.ts文件中得到了一个属性端点错误

NPM使用Vite Reaction应用程序运行预览有效,但我无法将其部署到Netlify

在单独的组件中定义React Router路由

字符串文字联合的分布式条件类型

为什么TypeScrip假定{...省略类型,缺少类型,...选取类型,添加&>}为省略类型,缺少类型?

有没有一种更好的方法来存储内存中的数据,而不是在类型脚本中使用数组和基于索引的函数?

如何使用angular15取消选中位于其他组件中的复选框

基于属性值的条件类型

Vite+Chrome扩展 list v3-;不能在模块外使用import语句;对于inpage脚本

Karma Angular Unit测试如何创建未解析的promise并在单个测试中解决它,以判断当时的代码是否只在解决后运行

有没有办法从不同长度的元组的联合中提取带有类型的最后一个元素?

将函数签名合并到重载中