typescript Cypress组件测试-当使用index.ts文件导入服务并导出时,测试失败,

hwazgwia  于 2023-06-07  发布在  TypeScript
关注(0)|答案(2)|浏览(242)

bounty还有7天到期。回答此问题可获得+300声望奖励。AngularM希望引起更多注意这个问题:我希望解决Cypress组件测试版本12中的这个导入问题。总结一下,这个问题似乎可以通过使用绝对url来解决,但我需要使用索引文件来解决这个问题,否则我需要更新我项目中的许多路由,只是为了让cypress对导入感到高兴。
**技术栈:**Angular v15和Cypress v12。
我正在测试的示例组件:

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

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

我的示例组件测试

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');
  });
});

我注入的UserHttp服务:

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

@Injectable()
export class UserHttp {

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

当前状态:

如果我将import保留为:import { Http } from '..';
但是如果我把它改成这样,它就可以工作了:import { Http } from './http';
我将其作为import { Http } from '..'的原因是因为我使用索引文件来导出所有服务,如下所示:

// 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,
];

我的存储服务:

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}`);
    });
  }
}

这是我的cypress配置:

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配置中工作?

当前错误:

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.

我的TS配置文件:

{
  "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
  }
}

userHttp服务扩展的我的http.ts服务器:

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错误信息

nfs0ujit

nfs0ujit1#

如果我正确地再现了错误(看起来是这样,因为对您提到的导入的更改修复了它),那么错误是
参考错误
以下错误源自您的测试代码,而不是Cypress。
无法在初始化前访问“UserHttp”
您可以通过服务索引导入UserHttp,而不是直接从它的实现中导入,从而在组件中修复它。

示例组件

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

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

我认为,原因是UserHttp依赖于services/index,但是这个索引并没有被测试直接引用,因此没有被编译。
错误消息看起来确实令人困惑,它应该抱怨Http.ts而不是UserHttp.ts

h79rfbju

h79rfbju2#

我发现问题出在typescript配置设置上。
我发现这个解决方案是通过制作一个新的angular版本15的应用程序,它只使用Cypress,并且它工作了。
然后我比较了两个tsconfig.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
  }
}

相关问题