我正在尝试测试我的angular2应用程序。当我向TestComponentBuilder添加导入时,它会抛出一个错误:错误:没有DirectiveResolver的提供者!
这是一个使用Angular2 RC2的打字稿项目。
这是我的规范(测试)文件:
import { Component, provide, EventEmitter} from '@angular/core';
import { HTTP_PROVIDERS } from '@angular/http';
import { PreliminaryNotice } from '../models/preliminary-notice-model';
import { IndustryCategoryDynamicLookup } from '../models/industry-category-dynamic-lookup-model';
import { PreliminaryNoticeService } from '../preliminary-notice-service/preliminary-notice.service';
import { ExpandedPreliminaryNotice } from './expanded-preliminary-notice';
import { beforeEachProviders,
beforeEach,
inject,
injectAsync,
fakeAsync,
it,
describe,
expect
} from '@angular/core/testing';
import { TestComponentBuilder } from '@angular/compiler/testing';
class PreliminaryNoticeServiceMock {
public preliminaryNoticeSaved: EventEmitter<PreliminaryNotice> = new EventEmitter<PreliminaryNotice>(false);
public savePreliminaryNotice(preliminaryNotice: PreliminaryNotice) {
this.preliminaryNoticeSaved.next(preliminaryNotice);
}
}
describe('expandable preliminary notice tests',
() => {
let builder: TestComponentBuilder;
let preliminaryNoticeService: PreliminaryNoticeService;
beforeEachProviders(() => [
TestComponentBuilder,
provide(PreliminaryNoticeService, { useClass: PreliminaryNoticeServiceMock })
]);
beforeEach(inject([PreliminaryNoticeService, TestComponentBuilder],
(pns: PreliminaryNoticeService, tsb: TestComponentBuilder) => {
preliminaryNoticeService = pns;
builder = tsb;
}));
it('view invoices expanded',
(done: () => void) =>
{
return builder.createAsync(ExpandedPreliminaryNotice)
.then(fixture => {
let nativeElement = fixture.nativeElement;
fixture.detectChanges();
fixture.nativeElement.querySelector('showOpenInvoicesHeader').click();
fixture.detectChanges();
expect(nativeElement.querySelector('showOpenInvoicesContent') === null).toBe(false);
done();
});
});
it('view log expanded',
() => {
expect(false).toEqual(true);
});
});
以下是组件:
import { Component, Injectable, Inject, EventEmitter, OnInit, Output } from '@angular/core';
import { NgClass } from '@angular/common';
import { PreliminaryNotice } from '../models/preliminary-notice-model';
import { IndustryCategoryDynamicLookup } from '../models/industry-category-dynamic-lookup-model';
import { PreliminaryNoticeService } from '../preliminary-notice-service/preliminary-notice.service';
@Component({
selector: 'expanded-preliminary-notice',
inputs: ['preliminaryNotice: preliminaryNotice'],
templateUrl: 'app/grid/expanded-preliminary-notice.html',
styles: [
`
.expanded-preliminary-notice {
padding: 10px;
}
.bold-span {
font-weight: bold;
margin-right: 5px;
}
.pad-left {
margin-left: 90px;
}
.link-div {
color: #1F7A74;
cursor: pointer;
width: 300px;
margin-top: 10px;
margin-bottom: 10px;
}
.align-left {
text-align: left;
}
.align-right {
text-align: right;
}
.border-top {
border-top: 1px solid black;
}
.border-bottom {
border-bottom: 1px solid black;
}
.bold-gold {
color: #A95C15;
font-weight: bold;
}
table {
border-collapse: collapse;
}
.backgrounded {
font-family: 'Arial Regular', 'Arial';
font-weight: 400;
font-style: normal;
font-size: 13px;
color: #333333;
text-align: left;
background-color: #D9D1BD;
}
.number-input {
width: 75px;
}
.h2 {
font-family: Arial;
font-weight: Bold;
font-size: 22px;
color: #13294b;
}
.h3 {
font-family: Arial;
font-weight: Normal;
font-size: 20px;
color: #a6601c;
}
.h4 {
font-family: Arial;
font-weight: bold;
font-size: 18px;
color: #a6601c;
}
#showPreliminaryNoticeStatus {
margin-top: 5px;
margin-bottom: 25px;
}
.thick-hr {
border: none;
height: 5px;
color: #746F63;
background-color: #746F63;
}
`
]
})
export class ExpandedPreliminaryNotice {
@Output()
doViewThresholds: EventEmitter<boolean> = new EventEmitter<boolean>(false);
private preliminaryNotice: PreliminaryNotice;
private originalPreliminaryNotice: PreliminaryNotice;
private backgrounColor: string = "transparent";
private timerToken: any;
private intervalToken: any;
private opacity: number = 1;
protected primaryBusinesses: IndustryCategoryDynamicLookup[] = [
{
id: 1,
industryCategoryName: "Construction",
industryCategoryDescription: "Construction",
isLienableFlag: true,
displaySortOrder: 1
},
{
id: 3,
industryCategoryName: "Hospitality",
industryCategoryDescription: "Hospitality",
isLienableFlag: false,
displaySortOrder: 2
},
{
id: 2,
industryCategoryName: "Manufacturing and Logistics",
industryCategoryDescription: "Manufacturing and Logistics",
isLienableFlag: false,
displaySortOrder: 3
},
{
id: 5,
industryCategoryName: "Retail",
industryCategoryDescription: "Retail",
isLienableFlag: false,
displaySortOrder: 4
},
{
id: 4,
industryCategoryName: "Transportation",
industryCategoryDescription: "Transportation",
isLienableFlag: false,
displaySortOrder: 5
},
{
id: 6,
industryCategoryName: "Other",
industryCategoryDescription: "Other",
isLienableFlag: false,
displaySortOrder: 20
}
]; // todo: pull from service.
protected noticeStatuses: string[] = ['New', 'Suggested', 'Completed', 'Released']; // todo: pull from service.
protected doShowStatusLog: boolean = false;
protected doShowOpenInvoices: boolean = false;
protected doShowAllOrders: boolean = false;
protected setThresholdValue: number;
constructor(private preliminaryNoticeService: PreliminaryNoticeService) {
}
ngOnInit() {
this.preliminaryNoticeService.preliminaryNoticeSaved.subscribe((preliminaryNotice: PreliminaryNotice) => {
this.preliminaryNotice = preliminaryNotice;
this.originalPreliminaryNotice = JSON.parse(JSON.stringify(this.preliminaryNotice));
this.startSaveTimer(2500);
});
this.setThresholdValue = this.preliminaryNotice.lienThresholdAmount;
}
ngAfterViewInit() {
// cheap clone.
this.originalPreliminaryNotice = JSON.parse(JSON.stringify(this.preliminaryNotice));
}
protected getCssClass() {
return {
'padding': '10px'
,'background-color': this.backgrounColor
}
}
// 1 == stateThreshold, 2 == custom, 3 == don't send.
protected set noticeSetTo(value: number) {
this.preliminaryNotice.customerPreliminaryNoticeOption = value;
}
protected get noticeSetTo(): number {
return this.preliminaryNotice.customerPreliminaryNoticeOption;
}
protected getInvoiceStyle(isLienable: boolean, textAlign: string) {
if (isLienable) {
return {
"font-weight": "bold",
"text-align": textAlign,
"border-bottom": "1px solid lightgray"
}
}
return {
"font-weight": "normal",
"text-align": textAlign,
"border-bottom": "1px solid lightgray"
}
}
public toggleShowPreliminaryNoticeStatusLog() {
this.doShowStatusLog = !this.doShowStatusLog;
}
public toggleShowOpenInvoices() {
this.doShowOpenInvoices = !this.doShowOpenInvoices;
}
public toggleShowAllOrders() {
this.doShowAllOrders = !this.doShowAllOrders;
}
public viewThresholds() {
this.doViewThresholds.next(true);
}
public getStatusLogToggleText() {
if (this.doShowStatusLog) {
return "Hide preliminary notice status change log";
}
return "Show preliminary notice status change log";
}
public getOpenInvoicesToggleText() {
if (this.doShowOpenInvoices) {
return "Hide open invoices";
}
return "Show open invoices";
}
public getAllOrdersToggleText() {
if (this.doShowAllOrders) {
return "Hide orders";
}
return "Show orders";
}
public save() {
this.preliminaryNoticeService.savePreliminaryNotice(this.preliminaryNotice);
}
public undo() {
this.preliminaryNotice = JSON.parse(JSON.stringify(this.originalPreliminaryNotice));
this.setThresholdValue = this.preliminaryNotice.lienThresholdAmount;
}
private startSaveTimer(time: number) {
if (this.timerToken) {
clearTimeout(this.timerToken);
this.timerToken = 0;
}
if (this.intervalToken) {
clearInterval(this.intervalToken);
}
this.opacity = 1;
this.timerToken = setTimeout(() => this.backgrounColor = "transparent", time);
this.intervalToken = setInterval(() => {
if (this.opacity < 0) return;
this.opacity -= .02;
this.backgrounColor = "rgba(154, 212, 113, " + this.opacity + ")";
}, 10);
}
}
模板html:
<div [ngStyle]="getCssClass()" id="expandablePreliminaryNotice_{{preliminaryNotice.id}}">
<div class="h2">{{preliminaryNotice.customerName}}</div>
<div>
<span class="bold-span" id="expandPrelimNoticeSettingsSpan">Preliminary notice threshold settings for new orders</span>
<span class="bold-span" style="margin-left: 125px;">Primary business</span>
<select id="primaryBusinessSelect" [(ngModel)]="preliminaryNotice.customerJobType">
<option *ngFor="let primaryBusiness of primaryBusinesses" [value]="primaryBusiness.id">{{primaryBusiness.industryCategoryDescription}}</option>
</select>
</div>
<div id="thresholdsSettingsDiv">
<input type="radio" name="thresholdGroup" value="1" [ngModel]="{checked: noticeSetTo === 1}" (change)="noticeSetTo=$event.target.value"/>Use state threshold of jobsite
<div id="viewThresholdDiv" style="display: inline-block" class="link-div" (click)="viewThresholds()">View thresholds</div> <br />
<input type="radio" name="thresholdGroup" value="2" [ngModel]="{checked: noticeSetTo === 2}" (change)="noticeSetTo=$event.target.value" />Set threshold to <input type="number" [(ngModel)]="setThresholdValue" class="align-right number-input" [disabled]="noticeSetTo != 2"/><br />
<input type="radio" name="thresholdGroup" value="3" [ngModel]="{checked: noticeSetTo === 3}" (change)="noticeSetTo=$event.target.value" />Do not send notices
</div>
<hr />
<div class="h4">{{preliminaryNotice.jobSiteName}} at {{preliminaryNotice.formattedAddress}}</div>
<!--<div class="h4">{{preliminaryNotice.formattedAddress}}</div>-->
<div>
<span class="bold-span">Preliminary notice status</span>
<select id="noticeStatus">
<option *ngFor="let status of noticeStatuses" [value]="status">{{status}}</option>
</select>
<span class="bold-span align-left pad-left">Lien threshold</span><input type="number" [(ngModel)]="preliminaryNotice.lienThresholdAmount" class="align-right number-input" />
<span class="bold-span align-left pad-left">Estimated amount</span><input type="number" [(ngModel)]="preliminaryNotice.estimateAmount" class="align-right number-input" />
</div>
<div id="showPreliminaryNoticeStatus">
<div id="showPreliminaryNoticeStatusHeader" (click)="toggleShowPreliminaryNoticeStatusLog()" class="link-div">{{getStatusLogToggleText()}}</div>
<div id="showPreliminaryNoticeStatusContent" *ngIf="doShowStatusLog" style="width: 450px;">
<table>
<thead>
<tr>
<td class="bold-gold border-bottom" style="width: 120px;">Filing status</td>
<td class="bold-gold border-bottom" style="width: 130px;">Date and time</td>
<td class="bold-gold border-bottom" style="width: 200px;">Changed by</td>
</tr>
</thead>
<tbody *ngFor="let log of preliminaryNotice.statusChangeLog">
<tr>
<td style="border-bottom: 1px solid lightgray">{{log.status}}</td>
<td style="border-bottom: 1px solid lightgray">{{log.date | date:"MM/dd/yy"}} {{log.date | date:'shortTime'}}</td>
<td style="border-bottom: 1px solid lightgray">{{log.changedBy}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="invoicesDiv" class="level-one-no-left-margin">
<table style="width: 200px">
<thead>
<tr>
<td colspan="2" class="backgrounded">OPEN INVOICES</td>
</tr>
</thead>
<tbody>
<tr>
<td class="bold-span align-left">Construction</td>
<td class="bold-span align-right">{{preliminaryNotice.openLienableInvoiceAmount | number:'1.2-2'}}</td>
</tr>
<tr>
<td class="align-left">Non-Construction</td>
<td class="align-right">{{preliminaryNotice.openNonLienableInvoiceAmount | number:'1.2-2'}}</td>
</tr>
<tr>
<td class="align-left border-top">TOTAL</td>
<td class="align-right border-top">{{preliminaryNotice.openNonLienableInvoiceAmount + preliminaryNotice.openLienableInvoiceAmount | number:'1.2-2'}}</td>
</tr>
</tbody>
</table>
<div id="showOpenInvoices">
<div id="showOpenInvoicesHeader" (click)="toggleShowOpenInvoices()" class="link-div">{{getOpenInvoicesToggleText()}}</div>
<div id="showOpenInvoicesContent" *ngIf="doShowOpenInvoices" style="width: 275px;">
<table>
<thead>
<tr>
<td class="bold-gold border-bottom" style="width: 85px;">Number</td>
<td class="bold-gold border-bottom" style="width: 90px;">Date</td>
<td class="bold-gold border-bottom" style="width: 100px;">Balance</td>
</tr>
</thead>
<tbody *ngFor="let invoice of preliminaryNotice.invoices">
<tr>
<td [ngStyle]="getInvoiceStyle(invoice.invoiceJobType.isLienableFlag, 'left')">{{invoice.number}}</td>
<td [ngStyle]="getInvoiceStyle(invoice.invoiceJobType.isLienableFlag, 'left')">{{invoice.invoiceDate | date:"MM/dd/yyyy"}}</td>
<td [ngStyle]="getInvoiceStyle(invoice.invoiceJobType.isLienableFlag, 'right')">{{invoice.balanceAmount | number:'1.2-2'}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="ordersDiv" class="level-one-no-left-margin">
<span class="backgrounded">ALL ORDERS</span><br />
<div id="showAllOrders">
<div id="showAllOrdersHeader" (click)="toggleShowAllOrders()" class="link-div">{{getAllOrdersToggleText()}}</div>
<div id="showAllOrdersContent" *ngIf="doShowAllOrders" style="width: 500px; background-color: #00ced1; height: 100px;">
</div>
</div>
</div>
<hr class="thick-hr" />
<div class="buttons-div">
<button (click)="save()" class="primary-button">Save</button>
<button (click)="undo()" class="secondary-button">Undo</button>
</div>
</div>
我尝试为DirectiveResolver添加一个导入,这让我开始不得不手动添加许多其他项目,我猜这不是正确的方法。
已经有一段时间了,因为我在这里发布了一个问题所以如果我遗漏了什么请告诉我,我会添加它。
答案 0 :(得分:0)
您是否尝试过这些导入:
import {DirectiveResolver} from '@angular/compiler';
import {DirectiveMetadata, Type} from '@angular/core';
var directiveResolver = new DirectiveResolver();