如何在 Aurelia 中对表单验证进行单元测试
How to unit test form validation in Aurelia
我正在尝试对表单实施一些单元测试,以查看验证规则是否按预期工作。
来自此页面:https://github.com/aurelia/testing/issues/63
我找到了这个实现:https://github.com/aurelia/validation/blob/master/test/validate-binding-behavior.ts
我尝试在我的项目中实现它
login.spec.js
import {bootstrap} from 'aurelia-bootstrapper';
import {StageComponent} from 'aurelia-testing';
import {PLATFORM} from 'aurelia-pal';
import { configure, blur, change } from './shared';
import { Login } from './login';
describe('ValidateBindingBehavior', () => {
it('sets validateTrigger', (done) => {
const component = StageComponent
.withResources(PLATFORM.moduleName('features/account/login/login'))
.inView('<login></login>')
.boundTo({});
component.bootstrap(configure);
let viewModel;
const renderer = { render: jasmine.createSpy() };
component.create(bootstrap)
// grab some references.
.then(() => {
viewModel = component.viewModel;
viewModel.controller.addRenderer(renderer);
})
.then(() => expect(viewModel.controller.errors.length).toBe(0))
.then(() => blur(viewModel.firstName))
.then(() => expect(viewModel.controller.errors.length).toBe(1))
.then(() => component.dispose())
.then(done);
});
});
login.js
import { inject, NewInstance } from 'aurelia-dependency-injection';
import { ValidationController } from 'aurelia-validation';
import { User } from './login.model';
@inject(NewInstance.of(ValidationController), User)
export class Login {
constructor(controller, user) {
this.controller = controller;
this.firstName = '';
this.lastName = '';
this.userName = '';
this.showForm = true;
this.user = user;
}
};
login.model.js
import {ValidationRules} from 'aurelia-validation';
export class User {
firstName = '';
lastName = '';
userName = '';
constructor() {
ValidationRules
.ensure('firstName')
.required()
.ensure('lastName')
.required()
.minLength(10)
.ensure('userName')
.required()
.on(this);
}
}
shared.js
import {DOM, PLATFORM} from 'aurelia-pal';
export function configure(aurelia) {
return aurelia.use
.standardConfiguration()
.plugin(PLATFORM.moduleName('aurelia-validation'))
}
export function blur(element) {
element.dispatchEvent(DOM.createCustomEvent('blur', {}));
return new Promise(resolve => setTimeout(resolve));
}
export function change(element, value) {
element.value = value;
element.dispatchEvent(DOM.createCustomEvent('change', { bubbles: true }));
return new Promise(resolve => setTimeout(resolve));
}
这是一段 html 标记:
<div>
<input ref="firstName" type="text" value.bind="user.firstName & validateOnBlur"
validation-errors.bind="firstNameErrors">
<label style="display: block;color:red" repeat.for="errorInfo of firstNameErrors">
${errorInfo.error.message}
</label>
</div>
<div>
在规范中,当我模糊元素时我希望得到一个错误,但 "controller.errors" 始终是一个空数组。我得到了失败的消息:
错误:0 应为 1。
更新 1:
我尝试手动验证,所以我在我的规范中添加了这个:
.then(()=>
viewModel.controller.validate({object: viewModel.user, propertyName: 'firstName' })
)
它工作正常,但模糊和更改功能不会触发验证。
更新 2:
我按照 "Sayan Pal" 的建议进行了更改。它现在可以工作,但有一个小问题。当我 "blur" 元素时,它会显示一个错误。但是当我 "blur" 几个元素(假设是三个)时,它没有显示最后一个错误。在这种情况下 controller.errors.length 将是 2.
我可以将最后一个元素模糊两次以获得正确的错误长度。但我认为应该有更好的解决方案。
.then(() => blur(viewModel.firstName))
.then(() => blur(viewModel.userName))
.then(() => blur(viewModel.lastName))
.then(() => blur(viewModel.lastName))
我认为您不需要使用 createCustomEvent
,而只需使用 element.dispatchEvent(new Event("blur"));
。 change
事件也是如此。
这对我一直有效,希望对你也有帮助:)
关于相关说明,我使用默认的 ValidationController
生成器工厂方法来确保默认触发器如下。
import { validateTrigger, ValidationControllerFactory } from "aurelia-validation";
...
const validationController = validationControllerFactory.createForCurrentScope();
validationController.changeTrigger(validateTrigger.changeOrBlur);
OP更新问题后更新
如果不调试,很难说出为什么会这样。由于我在您的测试代码中没有看到任何迫在眉睫的问题,我的假设是这是一个时间问题。主要思想是您需要等待更改发生。有几种方法可以做到这一点,所有这些都需要改变你的断言方式。
一种方法是使用一个具有超时的承诺,该承诺会定期轮询更改。然后等待承诺。
或者您可以使用 TaskQueue 对您的断言进行排队,并在断言之后调用 done
。这看起来像下面。
new TaskQueue().queueMicroTask(() => {
expect(foo).toBe(bar);
done();
});
另一种选择是使用 cypress 作为 e2e 测试框架。开箱即用,赛普拉斯等待更改发生,直到超时。
选择最适合您的需求。
我正在尝试对表单实施一些单元测试,以查看验证规则是否按预期工作。
来自此页面:https://github.com/aurelia/testing/issues/63
我找到了这个实现:https://github.com/aurelia/validation/blob/master/test/validate-binding-behavior.ts
我尝试在我的项目中实现它
login.spec.js
import {bootstrap} from 'aurelia-bootstrapper';
import {StageComponent} from 'aurelia-testing';
import {PLATFORM} from 'aurelia-pal';
import { configure, blur, change } from './shared';
import { Login } from './login';
describe('ValidateBindingBehavior', () => {
it('sets validateTrigger', (done) => {
const component = StageComponent
.withResources(PLATFORM.moduleName('features/account/login/login'))
.inView('<login></login>')
.boundTo({});
component.bootstrap(configure);
let viewModel;
const renderer = { render: jasmine.createSpy() };
component.create(bootstrap)
// grab some references.
.then(() => {
viewModel = component.viewModel;
viewModel.controller.addRenderer(renderer);
})
.then(() => expect(viewModel.controller.errors.length).toBe(0))
.then(() => blur(viewModel.firstName))
.then(() => expect(viewModel.controller.errors.length).toBe(1))
.then(() => component.dispose())
.then(done);
});
});
login.js
import { inject, NewInstance } from 'aurelia-dependency-injection';
import { ValidationController } from 'aurelia-validation';
import { User } from './login.model';
@inject(NewInstance.of(ValidationController), User)
export class Login {
constructor(controller, user) {
this.controller = controller;
this.firstName = '';
this.lastName = '';
this.userName = '';
this.showForm = true;
this.user = user;
}
};
login.model.js
import {ValidationRules} from 'aurelia-validation';
export class User {
firstName = '';
lastName = '';
userName = '';
constructor() {
ValidationRules
.ensure('firstName')
.required()
.ensure('lastName')
.required()
.minLength(10)
.ensure('userName')
.required()
.on(this);
}
}
shared.js
import {DOM, PLATFORM} from 'aurelia-pal';
export function configure(aurelia) {
return aurelia.use
.standardConfiguration()
.plugin(PLATFORM.moduleName('aurelia-validation'))
}
export function blur(element) {
element.dispatchEvent(DOM.createCustomEvent('blur', {}));
return new Promise(resolve => setTimeout(resolve));
}
export function change(element, value) {
element.value = value;
element.dispatchEvent(DOM.createCustomEvent('change', { bubbles: true }));
return new Promise(resolve => setTimeout(resolve));
}
这是一段 html 标记:
<div>
<input ref="firstName" type="text" value.bind="user.firstName & validateOnBlur"
validation-errors.bind="firstNameErrors">
<label style="display: block;color:red" repeat.for="errorInfo of firstNameErrors">
${errorInfo.error.message}
</label>
</div>
<div>
在规范中,当我模糊元素时我希望得到一个错误,但 "controller.errors" 始终是一个空数组。我得到了失败的消息:
错误:0 应为 1。
更新 1:
我尝试手动验证,所以我在我的规范中添加了这个:
.then(()=>
viewModel.controller.validate({object: viewModel.user, propertyName: 'firstName' })
)
它工作正常,但模糊和更改功能不会触发验证。
更新 2:
我按照 "Sayan Pal" 的建议进行了更改。它现在可以工作,但有一个小问题。当我 "blur" 元素时,它会显示一个错误。但是当我 "blur" 几个元素(假设是三个)时,它没有显示最后一个错误。在这种情况下 controller.errors.length 将是 2.
我可以将最后一个元素模糊两次以获得正确的错误长度。但我认为应该有更好的解决方案。
.then(() => blur(viewModel.firstName))
.then(() => blur(viewModel.userName))
.then(() => blur(viewModel.lastName))
.then(() => blur(viewModel.lastName))
我认为您不需要使用 createCustomEvent
,而只需使用 element.dispatchEvent(new Event("blur"));
。 change
事件也是如此。
这对我一直有效,希望对你也有帮助:)
关于相关说明,我使用默认的 ValidationController
生成器工厂方法来确保默认触发器如下。
import { validateTrigger, ValidationControllerFactory } from "aurelia-validation";
...
const validationController = validationControllerFactory.createForCurrentScope();
validationController.changeTrigger(validateTrigger.changeOrBlur);
OP更新问题后更新
如果不调试,很难说出为什么会这样。由于我在您的测试代码中没有看到任何迫在眉睫的问题,我的假设是这是一个时间问题。主要思想是您需要等待更改发生。有几种方法可以做到这一点,所有这些都需要改变你的断言方式。
一种方法是使用一个具有超时的承诺,该承诺会定期轮询更改。然后等待承诺。
或者您可以使用 TaskQueue 对您的断言进行排队,并在断言之后调用 done
。这看起来像下面。
new TaskQueue().queueMicroTask(() => {
expect(foo).toBe(bar);
done();
});
另一种选择是使用 cypress 作为 e2e 测试框架。开箱即用,赛普拉斯等待更改发生,直到超时。
选择最适合您的需求。