我有一个简单的指令,它可以很好地工作(用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()回调被执行。
我尝试过作用域。$digest(),$rootScope.$digest(),$apply,Jasmine spyOn...我还检查了流行指令是如何做到这一点的。似乎没有人会这样做:/
有什么想法吗?
编辑:
感谢Hooray I和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.因此,如果代码中断,单元测试将失败,=>作业已完成:)
发布于 2015-04-08 04:01:00
我不使用Jasime,我总是使用mocha + chaijs + sinon,但是jasmine也实现了done函数,它应该是这样工作的:
it('async method', function(done){
//once we add the done param, karma will wait until the fn is executed
service.doSomethingAsync()
.then(function(){
//assertions
// expect(result).to.be.something();
done(); //Where we are telling karma that it can continue with the test
}
//Other assertions
}it('async method', function(done){
var result = service.doSomethingAsync();
expect(result).to.eventually.be(something=;
//Other assertions
}发布于 2015-04-08 08:07:19
您使用的是哪个版本的Jasmine?
如果是1.3,您可以使用waitsFor and runs等待一段时间,然后在规范上断言:
所以
let 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();变成了
let files = new Array<string>();
runs(function() {
scope.readInputFile = (data: any) => {
files.push(data);
};
el.triggerHandler({
type: 'change',
target: {
files: [
new Blob(['data0']),
new Blob(['data1'])
]
}
});
scope.$digest();
});和
expect(files.length).toEqual(2);
expect(files[0]).toEqual('data0');
expect(files[1]).toEqual('data1');变成了
waitsFor(function() {
expect(files.length).toEqual(2);
expect(files[0]).toEqual('data0');
expect(files[1]).toEqual('data1');
}, "failure message", time_in_milliseconds);如果你使用的是Jasmine 2.0,你可以使用done函数:
您可以将尝试测试的代码(以及它所依赖的任何设置代码)移动到beforeEach,在那里调用异步代码,然后在异步代码完成时通过调用done方法通知框架异步操作已经完成。在done调用后,您的规范将自动运行。我以前从来没有用过这个(我还在1.3版本上),我也没有使用Angular和Karma,所以我有点不熟悉。
根据this answer的说法,您的beforeEach函数应该如下所示:
beforeEach(function(done) {
inject((_$compile_: angular.ICompileService, _$rootScope_: angular.IRootScopeService) => {
$compile = _$compile_;
$rootScope = _$rootScope_;
scope = $rootScope.$new();
});
let el = compileTemplate('<input type="file" file-read="readInputFile(data)">');
let 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();
done();
});你的断言应该看起来像这样:
it('should call a callback each time a file has been read by FileReader', (done) => {
expect(files.length).toEqual(2);
expect(files[0]).toEqual('data0');
expect(files[1]).toEqual('data1');
done();
});同样,我没有使用angular、TypeScript或Karma,因此您可能需要对此进行调整。但这是总体思路;设置一切,运行异步代码,在设置中调用done,运行断言,在规范中调用done。
发布于 2015-04-08 02:54:24
你可以把你的expect操作放在回调函数中。
https://stackoverflow.com/questions/29498918
复制相似问题