ng g @angular/core:standalone应该正确迁移测试组件,

soat7uwm  于 6个月前  发布在  Angular
关注(0)|答案(4)|浏览(57)

哪个@angular/*包是bug的来源?

core

这是个回归吗?

描述

ng new standalone-migration --defaults
cd standalone-migration
ng update @angular/core@next @angular/cli@next --force
ng g c hello

使用以下内容更新 HelloComponent 的测试:

import { Component } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { HelloComponent } from './hello.component';

@Component({
  template: '<app-hello></app-hello>'
})
class TestComponent { }

describe('HelloComponent', () => {
  beforeEach(() => TestBed.configureTestingModule({
    declarations: [TestComponent, HelloComponent]
  }));

  it('should display hello', () => {
    const fixture = TestBed.createComponent(TestComponent);
    expect(fixture.nativeElement.querySelector('p').textContent).toContain('hello works!');
  });
});

然后使用 ng g @angular/core:standalone --defaults 运行迁移。
它将迁移到:

import { Component } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { HelloComponent } from './hello.component';

@Component({
    template: '<app-hello></app-hello>',
    standalone: true
})
class TestComponent { }

describe('HelloComponent', () => {
  beforeEach(() => TestBed.configureTestingModule({
    imports: [TestComponent, HelloComponent]
}));

  it('should display hello', () => {
    const fixture = TestBed.createComponent(TestComponent);
    expect(fixture.nativeElement.querySelector('p').textContent).toContain('hello works!');
  });
});

请注意,TestComponent 现在是独立的,但它不导入 HelloComponent
因此会抛出:

Chrome 110.0.0.0 (Mac OS 10.15.7) HelloComponent should display hello FAILED
	Error: NG0304: 'app-hello' is not a known element (used in the 'TestComponent' component template):

这可以通过将 imports: [HelloComponent] 添加到 TestComponent 来修复。
另外,请注意,在理想情况下,configureTestingModule 可以简化为:

beforeEach(() => TestBed.configureTestingModule({
    imports: [TestComponent] // no need for HelloComponent
  }));

请提供一个最小复现bug的链接

  • 无响应*

请提供您看到的异常或错误

  • 无响应*

请提供您发现此bug的环境(运行 ng version )

Angular CLI: 15.2.0-next.3
Node: 16.17.0
Package Manager: npm 8.19.3
OS: darwin arm64

Angular: 15.2.0-next.4
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1502.0-next.3
@angular-devkit/build-angular   15.2.0-next.3
@angular-devkit/core            15.2.0-next.3
@angular-devkit/schematics      15.2.0-next.3
@angular/cli                    15.2.0-next.3
@schematics/angular             15.2.0-next.3
rxjs                            7.8.0
typescript                      4.9.5

还有其他要说的吗?

  • 无响应*
db2dz4w8

db2dz4w81#

很遗憾,我们对此无能为力。我们使用Angular编译器来确定要添加到imports数组的内容,而编译器不会分析测试模块。迁移尝试通过将测试模块的imports数组中的任何内容添加到组件中来处理这个问题,但这并不总是正确的。

2w3rbyxf

2w3rbyxf2#

感谢@crisbeto的回答。

当将HelloComponent添加到测试模块的imports中时,是否也可以将其添加到TestComponent的imports中?

aij0ehis

aij0ehis3#

它没有被添加,因为在TestComponent被使用的各个测试中,可能加载了具有相同选择器的不同的组件。通常,迁移的目标不是让一切都100%正确,而是让你大部分达到目标,并可能需要一些手动修复。

mctunoxg

mctunoxg4#

对于未来的读者:我制作了一个脚本,将所有的 TestHostComponents 还原为正常组件。这对其他人可能有用:

import { Project, SyntaxKind } from 'ts-morph';
const prettier = require('prettier');
const path = require('path');

console.info('💡 start TestHostComponent standalone demigratie');

const project = new Project();

project.addSourceFilesAtPaths('**/*.spec.ts');

project.getSourceFiles().forEach((sourceFile) => {
    const classesInSpec = sourceFile.getChildrenOfKind(SyntaxKind.ClassDeclaration);

    const testHostComponents = classesInSpec.filter((c) => c.getName() === 'TestHostComponent');
    if (testHostComponents.length === 0) {
        return;
    }

    console.info('converting ' + sourceFile.getBaseName());

    // remove imports and standalone from TestHostComponent Component decorator
    testHostComponents
        .flatMap((c) => c.getDecorators())
        .map((d) => d.getArguments()[0])
        .map((a) => (<any>a).getProperties())
        .forEach((a) => {
            a.filter((x: any) => x.getName() === 'standalone' || x.getName() === 'imports').forEach((x: any) => x.remove());
        });

    // remove TestHostComponent from imports array
    const calls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
    calls
        .filter((c) => c.compilerNode.expression.getText() === 'TestBed.configureTestingModule')
        .map((o) => o.getArguments()[0])
        .map((a) => a.getChildrenOfKind(SyntaxKind.PropertyAssignment).filter((x) => x.getName() === 'imports')[0])
        .filter(Boolean)
        .flatMap((a) =>
            a
                .getChildrenOfKind(SyntaxKind.ArrayLiteralExpression)[0]
                .getChildrenOfKind(SyntaxKind.SyntaxList)[0]
                .getChildrenOfKind(SyntaxKind.Identifier)
                .filter((i) => i.getText() === 'TestHostComponent')
        )
        .forEach((x) => {
            const prevComma = x.getPreviousSiblingIfKind(SyntaxKind.CommaToken);
            const nextComma = x.getNextSiblingIfKind(SyntaxKind.CommaToken);
            prevComma ? prevComma.replaceWithText('') : nextComma?.replaceWithText('');
            x.replaceWithText('');
        });

    // add declarations array with TestHostCompont in it
    const tb = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
    tb.filter((c) => c.compilerNode.expression.getText() === 'TestBed.configureTestingModule')
        .map((o) => o.getArguments()[0])
        .map((x) => x.getChildrenOfKind(SyntaxKind.SyntaxList)[0])
        .forEach((s) => {
            s.addChildText(', \n declarations: [TestHostComponent]');
        });

    const prettierOptions = prettier.resolveConfig.sync(path.resolve('.prettierrc'));

    console.info('formatting ' + sourceFile.getBaseName());
    const newS = prettier.format(sourceFile.getText(), { ...prettierOptions, parser: 'typescript' });
    sourceFile.replaceWithText(newS);
    console.info('✅ ' + sourceFile.getBaseName());
});
project.save();

你需要将 ts-morph 作为开发依赖项,然后使用 ts-node 运行文件 ts-node revert-test-hostcomponents.ts

相关问题