Skip to content

Commit b13c8db

Browse files
committed
Add tests
1 parent 77719d7 commit b13c8db

File tree

3 files changed

+180
-10
lines changed

3 files changed

+180
-10
lines changed
Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,43 @@
11
import BaseBsFormElement from 'ember-bootstrap/components/bs-form/element';
22
import { action } from '@ember/object';
33
import { tracked } from '@glimmer/tracking';
4+
import { assert } from '@ember/debug';
45

56
export default class BsFormElement extends BaseBsFormElement {
67
'__ember-bootstrap_subclass' = true;
78

9+
constructor() {
10+
super(...arguments);
11+
12+
assert(
13+
'showMultipleErrors is not supported for native constraint validations',
14+
this.args.showMultipleErrors !== true
15+
);
16+
}
17+
818
get controlElement() {
919
return document.getElementById(this.formElementId);
1020
}
1121

1222
@tracked
13-
errors = [];
23+
_invalidateErrors = 0;
24+
25+
get errors() {
26+
let { controlElement, _invalidateErrors } = this;
27+
return controlElement?.validity.valid
28+
? []
29+
: [controlElement.validationMessage];
30+
}
1431

1532
get hasValidator() {
1633
return true;
1734
}
1835

1936
@action
2037
showValidationOnHandler(...args) {
21-
this._updateErrors();
38+
// native validation state doesn't integrate with Ember's autotracking, so we need to invalidate our `errors` getter explicitly when an
39+
// event occurs that could change the validation state
40+
this._invalidateErrors++;
2241
super.showValidationOnHandler(...args);
2342
}
24-
25-
_updateErrors() {
26-
let { controlElement } = this;
27-
this.errors = controlElement.validity.valid
28-
? []
29-
: [controlElement.validationMessage];
30-
}
3143
}

packages/test-app/app/templates/application.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<BsForm @model={{this.model}} @onSubmit={{this.submit}} @onInvalid={{this.invalid}} as |form|>
44
<form.element @label="Name" @property="name" as |el|>
5-
<el.control required/>
5+
<el.control required minlength="3"/>
66
</form.element>
77
<form.element @controlType="email" @label="Email" @property="email" />
88
<form.submitButton>Submit</form.submitButton>
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { module, test } from 'qunit';
2+
import { setupRenderingTest } from 'ember-qunit';
3+
import { hbs } from 'ember-cli-htmlbars';
4+
import {
5+
render,
6+
triggerEvent,
7+
click,
8+
fillIn,
9+
focus,
10+
blur,
11+
findAll,
12+
pauseTest,
13+
} from '@ember/test-helpers';
14+
15+
module('Integration | Component | bs-form', function (hooks) {
16+
setupRenderingTest(hooks);
17+
18+
test('form is submitted if valid', async function (assert) {
19+
assert.expect(2);
20+
21+
let model = {
22+
name: '1234',
23+
};
24+
25+
this.set('model', model);
26+
this.submitAction = function () {
27+
assert.step('submit action has been called.');
28+
};
29+
this.invalidAction = function () {
30+
assert.ok(false, 'Invalid action must not been called.');
31+
};
32+
33+
await render(hbs`
34+
<BsForm @model={{this.model}} @onSubmit={{this.submitAction}} @onInvalid={{this.invalidAction}} as |form|>
35+
<form.element @label="Name" @property="name" as |el|>
36+
<el.control required/>
37+
</form.element>
38+
</BsForm>
39+
`);
40+
41+
await triggerEvent('form', 'submit');
42+
assert.verifySteps(['submit action has been called.']);
43+
});
44+
45+
test('validation errors are shown on submit', async function (assert) {
46+
assert.expect(3);
47+
48+
let model = {
49+
name: '',
50+
};
51+
52+
this.set('model', model);
53+
this.submitAction = function () {
54+
assert.ok(false, 'submit action must not been called.');
55+
};
56+
this.invalidAction = function () {
57+
assert.step('Invalid action has been called.');
58+
};
59+
60+
await render(hbs`
61+
<BsForm @model={{this.model}} @onSubmit={{this.submitAction}} @onInvalid={{this.invalidAction}} as |form|>
62+
<form.element @label="Name" @property="name" as |el|>
63+
<el.control required/>
64+
</form.element>
65+
</BsForm>
66+
`);
67+
68+
await triggerEvent('form', 'submit');
69+
assert.dom('input').hasClass('is-invalid', 'input has error class');
70+
assert.verifySteps(['Invalid action has been called.']);
71+
});
72+
73+
test('validation errors are shown after blur', async function (assert) {
74+
this.set('model', { name: '' });
75+
76+
await render(hbs`
77+
<BsForm @model={{this.model}} as |form|>
78+
<form.element @label="Name" @property="name" as |el|>
79+
<el.control required/>
80+
</form.element>
81+
</BsForm>
82+
`);
83+
assert.dom('input').doesNotHaveClass('is-invalid');
84+
85+
await focus('input');
86+
await blur('input');
87+
assert.dom('input').hasClass('is-invalid');
88+
});
89+
90+
test('validation success is shown after blur', async function (assert) {
91+
this.set('model', { name: 'Clara' });
92+
93+
await render(hbs`
94+
<BsForm @model={{this.model}} as |form|>
95+
<form.element @label="Name" @property="name" as |el|>
96+
<el.control required/>
97+
</form.element>
98+
</BsForm>
99+
`);
100+
assert.dom('input').doesNotHaveClass('is-valid');
101+
102+
await focus('input');
103+
await blur('input');
104+
assert.dom('input').hasClass('is-valid');
105+
});
106+
107+
test('validation errors are shown after user input', async function (assert) {
108+
this.set('model', { name: '' });
109+
110+
await render(hbs`
111+
<BsForm @model={{this.model}} as |form|>
112+
<form.element @label="Name" @property="name" as |el|>
113+
<el.control required pattern="[a-z]+"/>
114+
</form.element>
115+
</BsForm>
116+
`);
117+
assert.dom('input').doesNotHaveClass('is-invalid');
118+
119+
await fillIn('input', 'R');
120+
assert
121+
.dom('input')
122+
.doesNotHaveClass(
123+
'is-invalid',
124+
'validation is not shown while user is typing'
125+
);
126+
127+
await blur('input');
128+
assert
129+
.dom('input')
130+
.hasClass('is-invalid', 'validation error is shown after focus out');
131+
});
132+
133+
test('validation success is shown after user input', async function (assert) {
134+
this.set('model', { name: '' });
135+
136+
await render(hbs`
137+
<BsForm @model={{this.model}} as |form|>
138+
<form.element @label="Name" @property="name" as |el|>
139+
<el.control required pattern="[a-z]+"/>
140+
</form.element>
141+
</BsForm>
142+
`);
143+
assert.dom('input').doesNotHaveClass('is-valid');
144+
145+
await fillIn('input', 'abc');
146+
assert
147+
.dom('input')
148+
.doesNotHaveClass(
149+
'is-valid',
150+
'validation is not shown while user is typing'
151+
);
152+
153+
await blur('input');
154+
assert
155+
.dom('input')
156+
.hasClass('is-valid', 'validation error is shown after focus out');
157+
});
158+
});

0 commit comments

Comments
 (0)