为什么我的单元测试通过 Chrome 而通过 PhantomJS 失败?
Why do my unit tests pass with Chrome and fail with PhantomJS?
我正在使用带有 angular-cli 的 Angular2 final (2.0.2)。
我正在尝试将其设置为使用 PhantomJS 进行 运行 单元测试。
运行 Chrome 和 karma-chrome-launcher 的规范有效——所有测试都通过了。
运行 Phantomjs-prebuilt 2.1.13 和 karma-phantomjs-launcher 1.0.2 测试失败。
我在 karma.conf 中的插件数组中添加了 phantomjs 启动器,在浏览器数组中添加了 PahntomJS。
我得到的错误是:
PhantomJS 2.1.1 (Mac OS X 0.0.0) DataTableFormat should transform date
in milliseconds FAILED
ReferenceError: Can't find variable: Intl in src/main/js/test.ts (line 53565)
intlDateFormat@webpack:///Users/sninio/dev/csp-ui/~/@angular/common/src/facade/intl.js:117:0
<- src/main/js/test.ts:53565:20
webpack:///Users/sninio/dev/csp-ui/~/@angular/common/src/facade/intl.js:148:36
<- src/main/js/test.ts:53596:59
dateFormatter@webpack:///Users/sninio/dev/csp-ui/~/@angular/common/src/facade/intl.js:157:0
<- src/main/js/test.ts:53605:39
format@webpack:///Users/sninio/dev/csp-ui/~/@angular/common/src/facade/intl.js:192:0
<- src/main/js/test.ts:53640:29
transform@webpack:///Users/sninio/dev/csp-ui/~/@angular/common/src/pipes/date_pipe.js:92:0
<- src/main/js/test.ts:70473:90
transform@webpack:///Users/sninio/dev/csp-ui/src/main/js/app/pages/+platform/events/data-table/data-table.pipe.ts:9:4418
<- src/main/js/test.ts:52698:5787
webpack:///Users/sninio/dev/csp-ui/src/main/js/app/pages/+platform/events/data-table/data-table.pipe.spec.ts:20:30
<- src/main/js/test.ts:60923:30
execute@webpack:///Users/sninio/dev/csp-ui/~/@angular/core/bundles/core-testing.umd.j
<- src/main/js/test.ts:2997:28
webpack:///Users/sninio/dev/csp-ui/~/@angular/core/bundles/core-testing.umd.js:951:32
<- src/main/js/test.ts:3084:56
invoke@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/zone.js:203:0
<- src/main/js/test.ts:98886:33
onInvoke@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/proxy.js:72:0
<- src/main/js/test.ts:68790:45
invoke@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/zone.js:202:0
<- src/main/js/test.ts:98885:42
run@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/zone.js:96:0 <-
src/main/js/test.ts:98779:49
webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/jasmine-patch.js:91:27
<- src/main/js/test.ts:68526:53
execute@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/jasmine-patch.js:119:0
<- src/main/js/test.ts:68554:46
execute@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/jasmine-patch.js:119:0
<- src/main/js/test.ts:68554:46
invokeTask@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/zone.js:236:0
<- src/main/js/test.ts:98919:42
runTask@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/zone.js:136:0
<- src/main/js/test.ts:98819:57
drainMicroTaskQueue@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/zone.js:368:0
<- src/main/js/test.ts:99051:42 PhantomJS 2.1.1 (Mac OS X 0.0.0)
DataTableFormat should transform date string FAILED
也许我在 angular-cli 创建的 test.ts 文件中遗漏了一些配置?
更新:
似乎只有导入 DatePipe
和 JsonPipe
.
失败的测试
我也尝试在 test.ts
中导入 @angular/common/testing
但这无济于事 - 它们不会在相关的 index.js.
中导出
还尝试导入整个 @angular/common/pipes
但这也不起作用。
这是管道:
import { Pipe, PipeTransform } from "@angular/core";
import { DatePipe, JsonPipe } from "@angular/common";
@Pipe({name: 'dataTableFormat'})
export class DataTablePipe implements PipeTransform {
// values with type 'json' are parsed to json. As a result, string values may be displayed with quotes ("<string>").
// To avoid that, we remove these quotes with this regex
private quotesExp: RegExp = /^\"|\"$/gi;
constructor(private datePipe: DatePipe, private jsonPipe: JsonPipe) {
}
transform(value: any, type: string): string {
switch (type) {
case "date":
return this.datePipe.transform(value, 'short');
case "json":
return this.jsonPipe.transform(value).replace(this.quotesExp, "");
default:
return value;
}
}
}
和规范:
import { inject, TestBed } from "@angular/core/testing";
import { DataTablePipe } from "./data-table.pipe";
import { DatePipe, JsonPipe } from "@angular/common";
describe('DataTableFormat', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
DatePipe, JsonPipe
]
});
});
it('should transform date in milliseconds', inject([DatePipe, JsonPipe], (datePipe, jsonPipe) => {
let pipe = new DataTablePipe(datePipe, jsonPipe);
let testDate: Date = new Date();
expect(pipe.transform(testDate.getTime(), 'date')).toBe(datePipe.transform(testDate, 'short'));
}));
it('should transform date string', inject([DatePipe, JsonPipe], (datePipe, jsonPipe) => {
let pipe = new DataTablePipe(datePipe, jsonPipe);
let testDate: Date = new Date();
expect(pipe.transform(testDate.toISOString(), 'date')).toBe(datePipe.transform(testDate, 'short'));
}));
it('should transform json', inject([DatePipe, JsonPipe], (datePipe, jsonPipe) => {
let pipe = new DataTablePipe(datePipe, jsonPipe);
let testJson = {
prop1: "val1",
prop2: "val2"
};
expect(pipe.transform(testJson, 'json')).toBe(jsonPipe.transform(testJson));
}));
});
这是我的 test.ts
文件 - 与 angular-cli
...
生成的文件相比没有太大变化
import "./polyfills.ts";
import "zone.js/dist/long-stack-trace-zone";
import "zone.js/dist/proxy.js";
import "zone.js/dist/sync-test";
import "zone.js/dist/jasmine-patch";
import "zone.js/dist/async-test";
import "zone.js/dist/fake-async-test";
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
declare var __karma__: any;
declare var require: any;
// Prevent Karma from running prematurely.
__karma__.loaded = function () {
};
//noinspection TypeScriptUnresolvedVariable
Promise.all([
System.import('@angular/core/testing'),
System.import('@angular/platform-browser-dynamic/testing'),
System.import('../../../node_modules/nvd3/build/nv.d3.min.js'),
])
// First, initialize the Angular testing environment.
.then(([testing, testingBrowser]) => {
testing.getTestBed().initTestEnvironment(
testingBrowser.BrowserDynamicTestingModule,
testingBrowser.platformBrowserDynamicTesting()
);
})
// Then we find all the tests.
.then(() => require.context('./', true, /\.spec\.ts/))
// And load the modules.
.then(context => context.keys().map(context))
// Finally, start Karma to run the tests.
.then(__karma__.start, __karma__.error);
知道为什么这适用于 Chrome 而不是 PhantomJS 吗?
由于 PhantomJS 没有实现 Angular2 所依赖的 Intl
实现,解决方案是安装 npm package intl polyfill 并编辑 polyfills.ts
以包含
import 'intl';
import 'intl/locale-data/jsonp/en.js';
看这里:https://github.com/angular/angular/issues/3333
这里:https://github.com/angular/angular/issues/10809
在这里:https://coryrylan.com/blog/adding-the-internationalization-polyfill-to-a-angular-cli-project
现在您的所有浏览器都支持 Intl。 polyfill.io 是一个漂亮的服务,它只会在请求的浏览器确实需要时才加载 polyfill。这是一个很好的解决方案,但是如果你希望你的 polyfill 与你的脚本捆绑在一起呢?您可能会获得更好的性能,并且您的应用程序不依赖第三方 CDN,如果它出现故障,您的应用程序也会随之崩溃。那么下一个选项是什么?
好吧,使用 Angular CLI 实际上非常简单。首先,我们将使用 npm 将 Intl polyfill 直接安装到我们的 Angular 应用程序中。为此 运行 npm install intl --save。安装到 Angular CLI 项目后,转到 /src/polyfills.ts 文件。在这里您可以添加以下行。
import 'intl';
import 'intl/locale-data/jsonp/en.js';
就是这样!现在,当您的项目构建时,它将添加 Intl polyfill 和英语语言服务。您可以通过导入更多语言文件来添加更多语言支持。现在这样做的缺点是,即使浏览器支持 Intl,它仍然必须下载代码。您可以使用特征检测和动态加载来缓解这种情况。关于 polyfilling 的重要部分是随着浏览器对 Intl 的支持越来越好,我们将能够一起删除依赖项!
Intl 实现在 Angular 中可用,在 PhantomJS 中不可用。要使其与 polyfills 一起使用,请按照以下步骤操作:
- 安装 Intl:npm install intl --save
将以下导入添加到 polyfills.ts 文件:
进口'intl';
导入 'intl/locale-data/jsonp/en.js';
我正在使用带有 angular-cli 的 Angular2 final (2.0.2)。 我正在尝试将其设置为使用 PhantomJS 进行 运行 单元测试。 运行 Chrome 和 karma-chrome-launcher 的规范有效——所有测试都通过了。 运行 Phantomjs-prebuilt 2.1.13 和 karma-phantomjs-launcher 1.0.2 测试失败。
我在 karma.conf 中的插件数组中添加了 phantomjs 启动器,在浏览器数组中添加了 PahntomJS。
我得到的错误是:
PhantomJS 2.1.1 (Mac OS X 0.0.0) DataTableFormat should transform date in milliseconds FAILED ReferenceError: Can't find variable: Intl in src/main/js/test.ts (line 53565) intlDateFormat@webpack:///Users/sninio/dev/csp-ui/~/@angular/common/src/facade/intl.js:117:0 <- src/main/js/test.ts:53565:20 webpack:///Users/sninio/dev/csp-ui/~/@angular/common/src/facade/intl.js:148:36 <- src/main/js/test.ts:53596:59 dateFormatter@webpack:///Users/sninio/dev/csp-ui/~/@angular/common/src/facade/intl.js:157:0 <- src/main/js/test.ts:53605:39 format@webpack:///Users/sninio/dev/csp-ui/~/@angular/common/src/facade/intl.js:192:0 <- src/main/js/test.ts:53640:29 transform@webpack:///Users/sninio/dev/csp-ui/~/@angular/common/src/pipes/date_pipe.js:92:0 <- src/main/js/test.ts:70473:90 transform@webpack:///Users/sninio/dev/csp-ui/src/main/js/app/pages/+platform/events/data-table/data-table.pipe.ts:9:4418 <- src/main/js/test.ts:52698:5787 webpack:///Users/sninio/dev/csp-ui/src/main/js/app/pages/+platform/events/data-table/data-table.pipe.spec.ts:20:30 <- src/main/js/test.ts:60923:30 execute@webpack:///Users/sninio/dev/csp-ui/~/@angular/core/bundles/core-testing.umd.j <- src/main/js/test.ts:2997:28 webpack:///Users/sninio/dev/csp-ui/~/@angular/core/bundles/core-testing.umd.js:951:32 <- src/main/js/test.ts:3084:56 invoke@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/zone.js:203:0 <- src/main/js/test.ts:98886:33 onInvoke@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/proxy.js:72:0 <- src/main/js/test.ts:68790:45 invoke@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/zone.js:202:0 <- src/main/js/test.ts:98885:42 run@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/zone.js:96:0 <- src/main/js/test.ts:98779:49 webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/jasmine-patch.js:91:27 <- src/main/js/test.ts:68526:53 execute@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/jasmine-patch.js:119:0 <- src/main/js/test.ts:68554:46 execute@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/jasmine-patch.js:119:0 <- src/main/js/test.ts:68554:46 invokeTask@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/zone.js:236:0 <- src/main/js/test.ts:98919:42 runTask@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/zone.js:136:0 <- src/main/js/test.ts:98819:57 drainMicroTaskQueue@webpack:///Users/sninio/dev/csp-ui/~/zone.js/dist/zone.js:368:0 <- src/main/js/test.ts:99051:42 PhantomJS 2.1.1 (Mac OS X 0.0.0) DataTableFormat should transform date string FAILED
也许我在 angular-cli 创建的 test.ts 文件中遗漏了一些配置?
更新:
似乎只有导入 DatePipe
和 JsonPipe
.
失败的测试
我也尝试在 test.ts
中导入 @angular/common/testing
但这无济于事 - 它们不会在相关的 index.js.
中导出
还尝试导入整个 @angular/common/pipes
但这也不起作用。
这是管道:
import { Pipe, PipeTransform } from "@angular/core";
import { DatePipe, JsonPipe } from "@angular/common";
@Pipe({name: 'dataTableFormat'})
export class DataTablePipe implements PipeTransform {
// values with type 'json' are parsed to json. As a result, string values may be displayed with quotes ("<string>").
// To avoid that, we remove these quotes with this regex
private quotesExp: RegExp = /^\"|\"$/gi;
constructor(private datePipe: DatePipe, private jsonPipe: JsonPipe) {
}
transform(value: any, type: string): string {
switch (type) {
case "date":
return this.datePipe.transform(value, 'short');
case "json":
return this.jsonPipe.transform(value).replace(this.quotesExp, "");
default:
return value;
}
}
}
和规范:
import { inject, TestBed } from "@angular/core/testing";
import { DataTablePipe } from "./data-table.pipe";
import { DatePipe, JsonPipe } from "@angular/common";
describe('DataTableFormat', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
DatePipe, JsonPipe
]
});
});
it('should transform date in milliseconds', inject([DatePipe, JsonPipe], (datePipe, jsonPipe) => {
let pipe = new DataTablePipe(datePipe, jsonPipe);
let testDate: Date = new Date();
expect(pipe.transform(testDate.getTime(), 'date')).toBe(datePipe.transform(testDate, 'short'));
}));
it('should transform date string', inject([DatePipe, JsonPipe], (datePipe, jsonPipe) => {
let pipe = new DataTablePipe(datePipe, jsonPipe);
let testDate: Date = new Date();
expect(pipe.transform(testDate.toISOString(), 'date')).toBe(datePipe.transform(testDate, 'short'));
}));
it('should transform json', inject([DatePipe, JsonPipe], (datePipe, jsonPipe) => {
let pipe = new DataTablePipe(datePipe, jsonPipe);
let testJson = {
prop1: "val1",
prop2: "val2"
};
expect(pipe.transform(testJson, 'json')).toBe(jsonPipe.transform(testJson));
}));
});
这是我的 test.ts
文件 - 与 angular-cli
...
import "./polyfills.ts";
import "zone.js/dist/long-stack-trace-zone";
import "zone.js/dist/proxy.js";
import "zone.js/dist/sync-test";
import "zone.js/dist/jasmine-patch";
import "zone.js/dist/async-test";
import "zone.js/dist/fake-async-test";
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
declare var __karma__: any;
declare var require: any;
// Prevent Karma from running prematurely.
__karma__.loaded = function () {
};
//noinspection TypeScriptUnresolvedVariable
Promise.all([
System.import('@angular/core/testing'),
System.import('@angular/platform-browser-dynamic/testing'),
System.import('../../../node_modules/nvd3/build/nv.d3.min.js'),
])
// First, initialize the Angular testing environment.
.then(([testing, testingBrowser]) => {
testing.getTestBed().initTestEnvironment(
testingBrowser.BrowserDynamicTestingModule,
testingBrowser.platformBrowserDynamicTesting()
);
})
// Then we find all the tests.
.then(() => require.context('./', true, /\.spec\.ts/))
// And load the modules.
.then(context => context.keys().map(context))
// Finally, start Karma to run the tests.
.then(__karma__.start, __karma__.error);
知道为什么这适用于 Chrome 而不是 PhantomJS 吗?
由于 PhantomJS 没有实现 Angular2 所依赖的 Intl
实现,解决方案是安装 npm package intl polyfill 并编辑 polyfills.ts
以包含
import 'intl';
import 'intl/locale-data/jsonp/en.js';
看这里:https://github.com/angular/angular/issues/3333
这里:https://github.com/angular/angular/issues/10809
在这里:https://coryrylan.com/blog/adding-the-internationalization-polyfill-to-a-angular-cli-project
现在您的所有浏览器都支持 Intl。 polyfill.io 是一个漂亮的服务,它只会在请求的浏览器确实需要时才加载 polyfill。这是一个很好的解决方案,但是如果你希望你的 polyfill 与你的脚本捆绑在一起呢?您可能会获得更好的性能,并且您的应用程序不依赖第三方 CDN,如果它出现故障,您的应用程序也会随之崩溃。那么下一个选项是什么?
好吧,使用 Angular CLI 实际上非常简单。首先,我们将使用 npm 将 Intl polyfill 直接安装到我们的 Angular 应用程序中。为此 运行 npm install intl --save。安装到 Angular CLI 项目后,转到 /src/polyfills.ts 文件。在这里您可以添加以下行。
import 'intl';
import 'intl/locale-data/jsonp/en.js';
就是这样!现在,当您的项目构建时,它将添加 Intl polyfill 和英语语言服务。您可以通过导入更多语言文件来添加更多语言支持。现在这样做的缺点是,即使浏览器支持 Intl,它仍然必须下载代码。您可以使用特征检测和动态加载来缓解这种情况。关于 polyfilling 的重要部分是随着浏览器对 Intl 的支持越来越好,我们将能够一起删除依赖项!
Intl 实现在 Angular 中可用,在 PhantomJS 中不可用。要使其与 polyfills 一起使用,请按照以下步骤操作:
- 安装 Intl:npm install intl --save
将以下导入添加到 polyfills.ts 文件:
进口'intl'; 导入 'intl/locale-data/jsonp/en.js';