【发布时间】:2015-06-12 11:34:50
【问题描述】:
我有一个可以正常工作的简单指令(使用 TypeScript 编写,但并不重要),它允许获取通过 <input type="file"> 和 HTML5 File API 上传的文件的内容。
使用简单:
<body ng-controller="MyCtrl as ctrl">
<input type="file" file-read="ctrl.readInputFile(data)">
</body>
正在使用文件内容调用回调 readInputFile()。
interface IFileReadScope extends angular.IScope {
fileRead(data: any): void;
}
class FileRead implements angular.IDirective {
restrict = 'A';
scope = {
fileRead: '&'
};
link = (scope: IFileReadScope, element: angular.IAugmentedJQuery) => {
element.on('change', (e: Event) => {
const files: FileList = (<HTMLInputElement> e.target).files;
for (let i = 0; i < files.length; i++) {
const file = files[i];
const reader = new FileReader();
reader.onload = (e: Event) => {
scope.$apply(() => {
scope.fileRead({data: (<FileReader> e.target).result});
});
};
reader.readAsText(file);
}
});
};
static factory(): angular.IDirectiveFactory {
const directive = () => new FileRead();
return directive;
}
}
app.directive('fileRead', FileRead.factory());
这里是对它的测试:
describe('fileRead', () => {
beforeEach(module('app'));
let $compile: angular.ICompileService;
let scope: any;
beforeEach(inject((_$compile_, _$rootScope_) => {
$compile = _$compile_;
scope = _$rootScope_.$new();
}));
function compileTemplate(template: string): angular.IAugmentedJQuery {
const el = $compile(angular.element(template))(scope);
scope.$digest();
return el;
}
it('should call a callback each time a file has been read', () => {
const el = compileTemplate(
'<input type="file" file-read="readInputFile(data)">'
);
const files = new Array<string>();
scope.readInputFile = (data: any) => {
files.push(data);
};
el.triggerHandler({
type: 'change',
target: {
files: [
new Blob(['data0']),
new Blob(['data1'])
]
}
});
scope.$digest();
// Fails because it does not wait for scope.readInputFile() to be called
expect(files.length).toEqual(2);
expect(files[0]).toEqual('data0');
expect(files[1]).toEqual('data1');
});
});
问题是我不知道如何等待scope.readInputFile()回调被执行。
我已经尝试过 scope.$digest()、$rootScope.$digest()、$apply、Jasmine spyOn... 我还检查了流行的指令是如何做到这一点的。似乎没有人这样做:/
有什么想法吗?
编辑:
感谢 Hooray Im Helping 和 Matho,这里有两种使用 Jasmine 2 的可能解决方案:
let files: string[];
beforeEach(done => {
const el = compileTemplate(
'<input type="file" file-read="readInputFile(data)">'
);
files = new Array<string>();
scope.readInputFile = (data: any) => {
files.push(data);
if (files.length === 2) done();
};
el.triggerHandler({
type: 'change',
target: {
files: [
new Blob(['data0']),
new Blob(['data1'])
]
}
});
scope.$digest();
});
it('should call a callback each time a file has been read', () => {
expect(files.length).toEqual(2);
expect(files[0]).toEqual('data0');
expect(files[1]).toEqual('data1');
});
在这种情况下更优雅:
it('should call a callback each time a file has been read', done => {
const el = compileTemplate(
'<input type="file" file-read="readInputFile(data)">'
);
const files = new Array<string>();
scope.readInputFile = (data: any) => {
files.push(data);
if (files.length === 2) {
expect(files[0]).toEqual('data0');
expect(files[1]).toEqual('data1');
done();
}
};
el.triggerHandler({
type: 'change',
target: {
files: [
new Blob(['data0']),
new Blob(['data1'])
]
}
});
scope.$digest();
});
如果scope.fileRead()没有在指令内部执行,那么测试将失败:
FAILED fileRead should call a callback each time a file has been read
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
因此,如果代码中断,单元测试将失败 => 工作完成 :)
【问题讨论】:
标签: angularjs typescript jasmine karma-jasmine