冲突组件 Angular 测试
Conflicting Components Angular Testing
我正在关注这个项目 (https://stackblitz.com/edit/angular-material-expandable-table-rows) 以进行我的功能开发,因为我正在尝试实现类似的功能。
我能够让我的功能正常工作,但是当我 运行 测试时,我收到以下错误 -
Template parse errors:
More than one component matched on this element.
Make sure that only one component's selector can match a given element.
Conflicting components: MockedComponent,MatHeaderRow ("
</ng-container>
[ERROR ->]<mat-header-row *matHeaderRowDef="displayedColumnsProgramDetails"></mat-header-row>
"): ng:///DynamicTestModule/MaterialAudioProgramsComponent.html@107:18
More than one component matched on this element.
Make sure that only one component's selector can match a given element.
Conflicting components: MockedComponent,MatRow ("t-header-row *matHeaderRowDef="displayedColumnsProgramDetails"></mat-header-row>
[ERROR ->]<mat-row *matRowDef="let row; columns: displayedColumnsProgramDetails;"></mat-row>
我不知道我需要在这里做什么。
这是我的 component.html
<mat-spinner *ngIf="dataLoading" class="loading"></mat-spinner>
<ng-container class="ap-container" *ngIf="materialAudioPrograms$ | async as materialAudioPrograms">
<div class="container" *ngIf="materialAudioPrograms.length > 0; else dataLoading">
<mat-table [dataSource]="dataSource$" class="audio-programs vertical-data">
<ng-container matColumnDef="Start Channel">
<mat-header-cell *matHeaderCellDef>
Start Channel </mat-header-cell>
<mat-cell *matCellDef="let element">
<mat-icon *ngIf="element.show== false" aria-hidden="false" aria-label="ad-info-icon">keyboard_arrow_right
</mat-icon>
<mat-icon *ngIf="element.show== true" aria-hidden="false" aria-label="ad-info-icon">keyboard_arrow_down
</mat-icon>
{{element.startChannelNumber}}
</mat-cell>
</ng-container>
<ng-container matColumnDef="Layout">
<mat-header-cell *matHeaderCellDef> Layout </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.layout}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Language">
<mat-header-cell *matHeaderCellDef> Language </mat-header-cell>
<mat-cell mat-cell *matCellDef="let element"> {{(element.language != null) ? element.language : 'None'}}
</mat-cell>
</ng-container>
<ng-container matColumnDef="Active">
<mat-header-cell *matHeaderCellDef> Active </mat-header-cell>
<mat-cell *matCellDef="let element"> {{(element.active == true)? 'Yes': 'No'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="expandedDetail">
<mat-cell *matCellDef="let detail">
<mat-tab-group>
<!-- Program Tab and Table -->
<mat-tab label="Program">
<ng-container>
<mat-table [dataSource]="audioProgramDetailsDataSource$" class="vertical-data">
<ng-container matColumnDef="Censored">
<mat-header-cell *matHeaderCellDef> Censored </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.censored != null) ? (element.censored ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Dubbed">
<mat-header-cell *matHeaderCellDef> Dubbed </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.dubbed != null) ? (element.dubbed ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Music">
<mat-header-cell *matHeaderCellDef> Music </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.music != null) ? (element.music ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Effects">
<mat-header-cell *matHeaderCellDef> Effects </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.effects != null) ? (element.effects ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="DVS">
<mat-header-cell *matHeaderCellDef> DVS </mat-header-cell>
<mat-cell *matCellDef="let element"> {{(element.dvs != null) ? (element.dvs ? 'Yes' :'No'): 'None'}}
</mat-cell>
</ng-container>
<ng-container matColumnDef="MOS">
<mat-header-cell *matHeaderCellDef> MOS </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.muteOrSilence != null) ? (element.muteOrSilence ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Voiceover">
<mat-header-cell *matHeaderCellDef> Voiceover </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.voiceOver != null) ? (element.voiceOver ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Rescore">
<mat-header-cell *matHeaderCellDef> Rescore </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.rescore != null) ? (element.rescore ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Contains Dialog">
<mat-header-cell *matHeaderCellDef> Contains Dialog </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.dialog != null) ? (element.dialog ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="CALM Act">
<mat-header-cell *matHeaderCellDef> CALM Act Compliance </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.calmACT != null) ? (element.calmAct ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="EBUR128">
<mat-header-cell *matHeaderCellDef> EBUR128 Compliance </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.ebuR128 != null) ? (element.ebuR128 ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumnsProgramDetails"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumnsProgramDetails;"></mat-row>
</mat-table>
</ng-container>
</mat-tab>
<!--
Attributes Tab and Table -->
<mat-tab label="Attributes">
<ng-container>
<mat-table [dataSource]="audioProgramDetailsDataSource$" class="vertical-data">
<ng-container matColumnDef="Loudness Level">
<mat-header-cell *matHeaderCellDef> Loudness Level </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.loudnessLevel != null) ? (element.loudnessLevel ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Loudness Range">
<mat-header-cell *matHeaderCellDef> Loudness Range </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.loudness_Range != null) ? (element.loudness_Range ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="True Peak Level (Max)">
<mat-header-cell *matHeaderCellDef> True Peak Level (Max) </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.truePeakLevel != null) ? (element.truePeakLevel ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="True Peak Channel-L">
<mat-header-cell *matHeaderCellDef> True Peak Level Channel-L </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.truePeakLevelChannel_L != null) ? (element.truePeakLevelChannel_L ? 'Yes' :'No'): 'None'}}
</mat-cell>
</ng-container>
<ng-container matColumnDef="True Peak Channel-R">
<mat-header-cell *matHeaderCellDef> True Peak Level Channel-R </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.truePeakLevelChannel_R != null) ? (element.truePeakLevelChannel_R ? 'Yes' :'No'): 'None'}}
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumnsAttributeDetails"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumnsAttributeDetails;"></mat-row>
</mat-table>
</ng-container>
</mat-tab>
</mat-tab-group>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumnsAudioPrograms"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumnsAudioPrograms;" class="element-row" matRipple
(click)="toggleRow(row)"></mat-row>
<mat-row *matRowDef="let row; columns: ['expandedDetail']; when: isExpansionDetailRow"
[@detailExpand]="row.element.show ? 'expanded' : 'collapsed'" style="overflow: hidden"> </mat-row>
</mat-table>
<div class="container" *ngIf="materialAudioPrograms.length === 0 && (dataLoading === false | async)">
<p class="no-data-msg">No Audio Programs Available</p>
</div>
</div>
</ng-container>
这里是 component.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MaterialAudioProgramsComponent } from './material-audio-programs.component';
import {
MatTableModule,
MatTabsModule,
MatIconModule,
MatProgressSpinnerModule
} from '@angular/material';
@NgModule({
declarations: [MaterialAudioProgramsComponent],
imports: [CommonModule, MatProgressSpinnerModule, MatTableModule, MatTabsModule, MatIconModule],
entryComponents: [MaterialAudioProgramsComponent]
})
export class MaterialAudioProgramsModule {
// Define entry property to access entry component in lazy-loader service
static entry = MaterialAudioProgramsComponent;
}
和component.spec.ts
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MaterialMockModule } from '@content-platform/unit-test-helpers';
import { MaterialAudioProgramsComponent } from './material-audio-programs.component';
import { StoreModule } from '@ngrx/store';
import { RouterTestingModule } from '@angular/router/testing';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';
import { MatTableModule } from '@angular/material';
describe('MaterialAudioProgramsComponent', () => {
let component: MaterialAudioProgramsComponent;
let fixture: ComponentFixture<MaterialAudioProgramsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MaterialAudioProgramsComponent],
imports: [MaterialMockModule, StoreModule.forRoot({}), RouterTestingModule, MatTableModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MaterialAudioProgramsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
我无法找到解决方案,非常感谢您的帮助。
谢谢
尝试删除 MaterialMockModule 并保留 MatTableModule。如果我是你,我会利用 NO_ERRORS_SCHEMA 而不是导入任何模块,但在进行集成测试时你可能需要它们(子组件)。
我正在关注这个项目 (https://stackblitz.com/edit/angular-material-expandable-table-rows) 以进行我的功能开发,因为我正在尝试实现类似的功能。
我能够让我的功能正常工作,但是当我 运行 测试时,我收到以下错误 -
Template parse errors:
More than one component matched on this element.
Make sure that only one component's selector can match a given element.
Conflicting components: MockedComponent,MatHeaderRow ("
</ng-container>
[ERROR ->]<mat-header-row *matHeaderRowDef="displayedColumnsProgramDetails"></mat-header-row>
"): ng:///DynamicTestModule/MaterialAudioProgramsComponent.html@107:18
More than one component matched on this element.
Make sure that only one component's selector can match a given element.
Conflicting components: MockedComponent,MatRow ("t-header-row *matHeaderRowDef="displayedColumnsProgramDetails"></mat-header-row>
[ERROR ->]<mat-row *matRowDef="let row; columns: displayedColumnsProgramDetails;"></mat-row>
我不知道我需要在这里做什么。
这是我的 component.html
<mat-spinner *ngIf="dataLoading" class="loading"></mat-spinner>
<ng-container class="ap-container" *ngIf="materialAudioPrograms$ | async as materialAudioPrograms">
<div class="container" *ngIf="materialAudioPrograms.length > 0; else dataLoading">
<mat-table [dataSource]="dataSource$" class="audio-programs vertical-data">
<ng-container matColumnDef="Start Channel">
<mat-header-cell *matHeaderCellDef>
Start Channel </mat-header-cell>
<mat-cell *matCellDef="let element">
<mat-icon *ngIf="element.show== false" aria-hidden="false" aria-label="ad-info-icon">keyboard_arrow_right
</mat-icon>
<mat-icon *ngIf="element.show== true" aria-hidden="false" aria-label="ad-info-icon">keyboard_arrow_down
</mat-icon>
{{element.startChannelNumber}}
</mat-cell>
</ng-container>
<ng-container matColumnDef="Layout">
<mat-header-cell *matHeaderCellDef> Layout </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.layout}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Language">
<mat-header-cell *matHeaderCellDef> Language </mat-header-cell>
<mat-cell mat-cell *matCellDef="let element"> {{(element.language != null) ? element.language : 'None'}}
</mat-cell>
</ng-container>
<ng-container matColumnDef="Active">
<mat-header-cell *matHeaderCellDef> Active </mat-header-cell>
<mat-cell *matCellDef="let element"> {{(element.active == true)? 'Yes': 'No'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="expandedDetail">
<mat-cell *matCellDef="let detail">
<mat-tab-group>
<!-- Program Tab and Table -->
<mat-tab label="Program">
<ng-container>
<mat-table [dataSource]="audioProgramDetailsDataSource$" class="vertical-data">
<ng-container matColumnDef="Censored">
<mat-header-cell *matHeaderCellDef> Censored </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.censored != null) ? (element.censored ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Dubbed">
<mat-header-cell *matHeaderCellDef> Dubbed </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.dubbed != null) ? (element.dubbed ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Music">
<mat-header-cell *matHeaderCellDef> Music </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.music != null) ? (element.music ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Effects">
<mat-header-cell *matHeaderCellDef> Effects </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.effects != null) ? (element.effects ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="DVS">
<mat-header-cell *matHeaderCellDef> DVS </mat-header-cell>
<mat-cell *matCellDef="let element"> {{(element.dvs != null) ? (element.dvs ? 'Yes' :'No'): 'None'}}
</mat-cell>
</ng-container>
<ng-container matColumnDef="MOS">
<mat-header-cell *matHeaderCellDef> MOS </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.muteOrSilence != null) ? (element.muteOrSilence ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Voiceover">
<mat-header-cell *matHeaderCellDef> Voiceover </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.voiceOver != null) ? (element.voiceOver ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Rescore">
<mat-header-cell *matHeaderCellDef> Rescore </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.rescore != null) ? (element.rescore ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Contains Dialog">
<mat-header-cell *matHeaderCellDef> Contains Dialog </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.dialog != null) ? (element.dialog ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="CALM Act">
<mat-header-cell *matHeaderCellDef> CALM Act Compliance </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.calmACT != null) ? (element.calmAct ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="EBUR128">
<mat-header-cell *matHeaderCellDef> EBUR128 Compliance </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.ebuR128 != null) ? (element.ebuR128 ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumnsProgramDetails"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumnsProgramDetails;"></mat-row>
</mat-table>
</ng-container>
</mat-tab>
<!--
Attributes Tab and Table -->
<mat-tab label="Attributes">
<ng-container>
<mat-table [dataSource]="audioProgramDetailsDataSource$" class="vertical-data">
<ng-container matColumnDef="Loudness Level">
<mat-header-cell *matHeaderCellDef> Loudness Level </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.loudnessLevel != null) ? (element.loudnessLevel ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="Loudness Range">
<mat-header-cell *matHeaderCellDef> Loudness Range </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.loudness_Range != null) ? (element.loudness_Range ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="True Peak Level (Max)">
<mat-header-cell *matHeaderCellDef> True Peak Level (Max) </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.truePeakLevel != null) ? (element.truePeakLevel ? 'Yes' :'No'): 'None'}} </mat-cell>
</ng-container>
<ng-container matColumnDef="True Peak Channel-L">
<mat-header-cell *matHeaderCellDef> True Peak Level Channel-L </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.truePeakLevelChannel_L != null) ? (element.truePeakLevelChannel_L ? 'Yes' :'No'): 'None'}}
</mat-cell>
</ng-container>
<ng-container matColumnDef="True Peak Channel-R">
<mat-header-cell *matHeaderCellDef> True Peak Level Channel-R </mat-header-cell>
<mat-cell *matCellDef="let element">
{{(element.truePeakLevelChannel_R != null) ? (element.truePeakLevelChannel_R ? 'Yes' :'No'): 'None'}}
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumnsAttributeDetails"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumnsAttributeDetails;"></mat-row>
</mat-table>
</ng-container>
</mat-tab>
</mat-tab-group>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumnsAudioPrograms"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumnsAudioPrograms;" class="element-row" matRipple
(click)="toggleRow(row)"></mat-row>
<mat-row *matRowDef="let row; columns: ['expandedDetail']; when: isExpansionDetailRow"
[@detailExpand]="row.element.show ? 'expanded' : 'collapsed'" style="overflow: hidden"> </mat-row>
</mat-table>
<div class="container" *ngIf="materialAudioPrograms.length === 0 && (dataLoading === false | async)">
<p class="no-data-msg">No Audio Programs Available</p>
</div>
</div>
</ng-container>
这里是 component.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MaterialAudioProgramsComponent } from './material-audio-programs.component';
import {
MatTableModule,
MatTabsModule,
MatIconModule,
MatProgressSpinnerModule
} from '@angular/material';
@NgModule({
declarations: [MaterialAudioProgramsComponent],
imports: [CommonModule, MatProgressSpinnerModule, MatTableModule, MatTabsModule, MatIconModule],
entryComponents: [MaterialAudioProgramsComponent]
})
export class MaterialAudioProgramsModule {
// Define entry property to access entry component in lazy-loader service
static entry = MaterialAudioProgramsComponent;
}
和component.spec.ts
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MaterialMockModule } from '@content-platform/unit-test-helpers';
import { MaterialAudioProgramsComponent } from './material-audio-programs.component';
import { StoreModule } from '@ngrx/store';
import { RouterTestingModule } from '@angular/router/testing';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';
import { MatTableModule } from '@angular/material';
describe('MaterialAudioProgramsComponent', () => {
let component: MaterialAudioProgramsComponent;
let fixture: ComponentFixture<MaterialAudioProgramsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MaterialAudioProgramsComponent],
imports: [MaterialMockModule, StoreModule.forRoot({}), RouterTestingModule, MatTableModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MaterialAudioProgramsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
我无法找到解决方案,非常感谢您的帮助。
谢谢
尝试删除 MaterialMockModule 并保留 MatTableModule。如果我是你,我会利用 NO_ERRORS_SCHEMA 而不是导入任何模块,但在进行集成测试时你可能需要它们(子组件)。