Tech stack:Angel V15和Cypress V12.

My example component that I'm testing:

import { Component } from '@angular/core';
import { UserHttp } from '../../services';

@Component({
  selector: 'example-view',
  templateUrl: './example.component.html',
})
export class ExampleComponent {
  constructor(
    private userHttp: UserHttp,
  ) { }
}

My example component test

import { HttpClientModule } from '@angular/common/http';

import { ExampleComponent } from './example.component';
import { UserHttp } from '../../services';

describe('Example component', () => {
  beforeEach(() => {
    cy.mount(ExampleComponent, {
      providers: [UserHttp],
      imports: [HttpClientModule]
    });
  });

  it('should display default title', () => {
    cy.get('h2').should('exist');
  });
});

My UserHttp service that I'm injecting:

import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';

@Injectable()
export class UserHttp {

  test(): Observable<any> {
    return of({});
  }
}

Current status:

The above test will fail if I leave the import as: import { Http } from '..';
But if I change it to this it works: import { Http } from './http';

我将其作为从‘..’导入的{http}的原因是因为我使用索引文件来导出所有服务,如下所示:

// index.ts: services:
import { UserHttp } from './http/user.http';
import { StorageService } from './storage.service';

export * from './http/user.http';
export * from './storage.service';

export const SERVICES = [
  StorageService,
  UserHttp,
];

My StorageService:

import { Injectable } from '@angular/core';

import { environment } from '../../environments/environment';

@Injectable()
export class StorageService {
  baseKey: string = environment.baseStorageKey;

  constructor() {}

  setLocalStorage(key: string, value: any): void {
    this.removeLocalStorage(key);
    localStorage.setItem(`${this.baseKey}${key}`, JSON.stringify(value));
  }

  getLocalStorage(key: string): any {
    const item = localStorage.getItem(`${this.baseKey}${key}`);
    return item !== null ? JSON.parse(item) : '';
  }

  removeLocalStorage(key: string): void {
    localStorage.removeItem(`${this.baseKey}${key}`);
  }

  removeBatchLocalStorage(keys: string[]): void {
    keys.forEach((key: string) => {
      localStorage.removeItem(`${this.baseKey}${key}`);
    });
  }
}

This is my cypress config:

import { defineConfig } from "cypress";

export default defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      // implement node event listeners here
    },
  },
  chromeWebSecurity: false,
  screenshotsFolder: "cypress/snapshots",
  trashAssetsBeforeRuns: true,
  viewportWidth: 1400,
  viewportHeight: 1200,
  video: false,
  env: {
    local: "http://localhost:4200/",
    staging: "https://hidden.co.uk/",
    user: {
      email: "hidden",
      password: "hidden",
    },
  },
  component: {
    devServer: {
      framework: "angular",
      bundler: "webpack",
    },
    specPattern: "**/*.cy.ts",
  },
});

有没有办法将index.ts文件方法设置为在tsconfig或cypress配置中工作?

Current Error I get:

     TypeError
The following error originated from your test code, not from Cypress.

  > Cannot read properties of undefined (reading 'StorageService')

When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.

Cypress could not associate this error to any specific test.

We dynamically generated a new test to display this failure.

My TS Config file:

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "forceConsistentCasingInFileNames": true,
    "strict": false,
    "strictPropertyInitialization": false,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "target": "es2020",
    "module": "es2020",
    "skipLibCheck": true,
    "allowJs": true,
    "types": [
      "node"
    ],
    "lib": [
      "es2018",
      "dom"
    ],
    "paths": {
      "@app/*": ["src/app/*"],
      "@services/*": ["src/app/services/*"]
    }
  },
  "angularCompilerOptions": {
    "enableI18nLegacyMessageIdFormat": false,
    "strictInjectionParameters": false,
    "strictInputAccessModifiers": false,
    "strictTemplates": false
  }
}

My http.ts server that is extended by the userHttp service:

import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

import { environment } from '../../../environments/environment';

@Injectable()
export abstract class Http {
  protected baseUrl = environment.baseUrl;
  protected headers: HttpHeaders;

  constructor(public httpClient: HttpClient) {}

  protected get<T>(path: string, options: any = {}, noBaseUrl = false): Observable<T> {
    const url: string = this.createUrlString(path, noBaseUrl);
    const params: HttpParams = this.getHttpParams(options.queryString);
    return this.httpClient.get<T>(url, { params });
  }

  protected post<T>(path: string, data = {}, noBaseUrl = false): Observable<T> {
    const url: string = this.createUrlString(path, noBaseUrl);
    const options = { headers: this.headers };
    return this.httpClient.post<T>(url, { ...data, lang: 'uk' }, options);
  }

  protected createUrlString(resourcePath: string, noBaseUrl: boolean): string {
    return noBaseUrl ? `${resourcePath}` : `${this.baseUrl}${resourcePath}`;
  }

  protected getHttpParams(params: any): HttpParams {
    let httpParams: HttpParams = new HttpParams();

    if (params) {
      for (const prop in params) {
        if (params.hasOwnProperty(prop)) {
          const parameterValue: string = params[prop].toString();
          httpParams = httpParams.append(prop, parameterValue);
        }
      }
    }
    return httpParams;
  }
}

Gui error message

enter image description here

推荐答案

我发现问题出在打字配置设置上.

我发现了这个解决方案,只使用 cypress 制作了一个新的angular版本15应用程序,它起到了作用.

因此,我随后比较了两个tsfig.json文件.

以下是解决此问题的有效tsconfig.json文件:

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "forceConsistentCasingInFileNames": true,
    "strict": false,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": false,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "target": "es2017",
    "module": "es2020",
    "allowJs": true,
    "lib": [
      "es2020",
      "dom"
    ]
  },
  "angularCompilerOptions": {
    "enableI18nLegacyMessageIdFormat": false,
    "strictInjectionParameters": false,
    "strictInputAccessModifiers": false,
    "strictTemplates": false
  }
}

Typescript相关问答推荐

如何在函数参数中使用(或模仿)`success`的行为?

类型脚本强制泛型类型安全

如何为父类构造函数中的修饰属性赋值?

如何通过TypeScript中的工厂函数将区分的联合映射到具体的类型(如类)?

使用泛型keyof索引类型以在if-condition内类型推断

TypeScrip表示该类型不可赋值

Material UI / MUI系统:我如何告诉TypeScript主题是由提供程序传递的?

使用Dockerfile运行Vite React应用程序时访问env变量

用于验证字符串变量的接口成员

有没有可能产生输出T,它在视觉上省略了T中的一些键?

垫表页脚角v15

如何在ANGLE中注册自定义验证器

@TANSTACK/REACT-QUERY中发生Mutations 后的数据刷新问题

如何获取受类型脚本泛型约束的有效输入参数

Cypress-验证别名的存在或将别名中的文本与';if';语句中的字符串进行比较

两个名称不同的相同打字界面-如何使其干燥/避免重复?

窄SomeType与SomeType[]

如何将对象的字符串文字属性用作同一对象中的键类型

我如何键入它,以便具有字符串或数字构造函数的数组可以作为字符串或数字键入s或n

在 TypeScript 中实现类型级别深度优先搜索