-
Mark Florian authored
Some Geo tests needed to be fixed to support this version bump. Details below. For the GeoNodeFormCapacities and GeoNodeFormCore components: The tests set the input's value to the empty string, and try to assert that the form displays an error message saying that the input can't be empty. This doesn't work, because the underlying `BFormInput` does not emit a model event in the case that the model value hasn't changed (the value starts as an empty string, and ends up as an empty string!). Prior to this, the tests were passing because the `input` event is not the model event as far as the `BFormInput` is concerned, which it always fires. So, the fix in this case is to do one of the following: 1. Listen for the update event in the component, rather than the input event. 2. In the test, set the input element's value to something else before making it the empty string, so the model event fires. The first option seems best, since it's more "correct", but this change is entirely for the sake of the tests. Real-world behaviour won't have changed at all either way. This is because there's no way for a user to set an input to the empty string when it's already the empty string, so this edge case can't happen in reality. For the GeoSettingsForm component: This is actually a very subtle confluence of bugs, including implicit type casting, unrealistic synchronous calls and inconsistent amounts of behaviour mocking. Individually these are harmless, but all together produced a failing test. The crux of this is that `BFormInput` emits a model (`update`) Vue event when a [`blur` DOM event is triggered][237] on the input element, **if** the [_formatted_ input value doesn't strictly equal the `model` value][161]. The _formatted_ value is [_stringified_][138] copy of the model value (unless the `formatter` prop is given), which means that when the model is a `number`, the `model` ends up getting overwritten, because a string can't equal a number. Prior to GitLab UI v18.7, that model (`update`) event was completely ignored, and so triggering the `blur` *didn't* re-update the model value with the old value. But _with_ GitLab UI v18.7, that model (`update`) event _is_ listened to, (via the remapping to the `input` event) and the model gets is updated accordingly _because_ of the blur. The reason the _old_ value is used by `BFormInput`'s `onBlur` method is because it is called _synchronously_ after emitting the model event. This means that the parent scope's model has been updated, but Vue hasn't _yet_ passed it down to the component again. If you `await wrapper.vm.$nextTick()` _before_ triggering the blur, the _new but stringified_ value will be in the parent scope's model instead of the _old_ stringified model value. (This is was [suggested][zcuddy] by @zcuddyy, although _technically_ the model would a string instead of a number with this approach.) Finally, this can be reproduced with `BFormInput` itself. Below are some tests that simplify and capture the problem with the way the Geo tests were written: ```js import { BFormInput } from 'bootstrap-vue'; import { mount } from '@vue/test-utils'; describe('number/text confusion', () => { const factory = (template, value) => { wrapper = mount({ components: { BFormInput }, data: () => ({ modelValue: value }), template, }); }; describe('given a number value without number prop', () => { beforeEach(() => { factory(`<b-form-input v-model="modelValue" />`, 5); }); it('blur changes the model value to stringified original', () => { expect(wrapper.vm.modelValue).toBe(5); // BFormInput's model event is 'update' wrapper.find(BFormInput).vm.$emit('update', 20); expect(wrapper.vm.modelValue).toBe(20); wrapper.find('input').trigger('blur'); // Note value has changed! expect(wrapper.vm.modelValue).toBe('5'); }); }); describe('given a number value with number prop', () => { beforeEach(() => { factory(`<b-form-input v-model="modelValue" number />`, 5); }); it('blur does not mess things up', () => { expect(wrapper.vm.modelValue).toBe(5); // BFormInput's model event is 'update' wrapper.find(BFormInput).vm.$emit('update', 20); expect(wrapper.vm.modelValue).toBe(20); wrapper.find('input').trigger('blur'); // Note value has *not* changed expect(wrapper.vm.modelValue).toBe(20); }); }); }); ``` This explains why doing `findGeoSettingsTimeoutField().vm.$emit('blur');` fixes the tests (because this bypasses `BFormInput`'s `onBlur` handler), and why `findGeoSettingsTimeoutField().setValue(data);` also fixes the tests (because it correctly updates the _internal_ `vModelValue` of the `BFormInput`, such that the `onBlur` handler uses that new value rather than the old one). Phew! What all this means that this is not a bug in GitLab UI v18.7, and it's not a bug in `bootstrap-vue`. It's *actually* a subtle bug in `GeoSettingsForm`, due to weak typing of the model and our previously broken `GlFormInput` model binding that GitLab UI v18.7 fixes. Therefore, I think the _correct_ fix here is two-fold: 1. set the `number` prop to `true` on the `timeout` field 1. Use `setValue` in _both_ tests Either one of these fixes the tests, but I think *both* should be done. The first correctly casts the model to a number, and the second more closely mirrors a user's action. [138]: https://github.com/bootstrap-vue/bootstrap-vue/blob/028b880ea1ec168888368f3abc7e89f1f8f2f9cd/src/mixins/form-text.js#L138 [161]: https://github.com/bootstrap-vue/bootstrap-vue/blob/028b880ea1ec168888368f3abc7e89f1f8f2f9cd/src/mixins/form-text.js#L161 [237]: https://github.com/bootstrap-vue/bootstrap-vue/blob/028b880ea1ec168888368f3abc7e89f1f8f2f9cd/src/mixins/form-text.js#L237 [zcuddy]: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38824#note_394960100
e447cd3a
This project manages its dependencies using
Yarn.
Learn more